Components used in Themes and Plugins: How to get file URL?

In my projects I have some components which I use in a lot of custom Plugins as well as in some custom coded Themes like e.g. admin image or video selectors.

Those I am holding in custom git repositories. However as those components are used in both - themes and plugins - I always have to manually change the URL getters after pulling from GIT. If the components exist multiple times its not a problem I have wrapped those with function_exists.

This is what I use in the Theme:

wp_enqueue_script(get_template_directory_uri().'/libs/my_lib/admin_image_upload', ...);

This is what I use in a Plugin:

wp_enqueue_script(plugins_url(__FILE__).'admin_image_upload.js', ...);

Is there a way to determine if my current PHP file is within the plugins' folder or within the themes' folder in order that I can let decide for the correct path automatically?

Topic plugins-url paths wp-enqueue-script directory urls Wordpress

Category Web


Here's a solution that will return the URL to a file relative to its location, as well as string indicating where the file lives, i.e. mu-plugins/plugins/theme.

This code has been adapted from get_url_from_dir() used in the meta box library CMB2.

/**
 * Converts a system file path to a URL.
 * Returns URL and the detected location of the file.
 *
 * Based on get_url_from_dir() via CMB2
 * @link https://github.com/CMB2/CMB2
 * 
 * @param  string $file file path to convert.
 * @return string Converted URL.
 * @return array|bool (on error)
 *  array
 *    $url string Converted URL.
 *    $location string location of dir (mu-plugins, plugins, theme)
 *
 */
function wpse_get_url_info_from_file( $file ) {
    $file = wp_normalize_path( $file );
    $test_dir = pathinfo( $file );

    if ( ! $test_dir ) {
        return false;
    }

    $test_dir = trailingslashit( $test_dir['dirname'] );

    // Test if we are in the mu-plugins dir.
    if ( 0 === strpos( $test_dir, wp_normalize_path( WPMU_PLUGIN_DIR ) ) ) {
        return [
            'url' => trailingslashit( plugins_url( '', $file ) ),
            'location' => 'mu-plugins'
        ];
    }

    // Test if we are in the plugins dir.
    if ( 0 === strpos( $test_dir, wp_normalize_path( WP_PLUGIN_DIR ) ) ) {
        return [
            'url' => trailingslashit( plugins_url( '', $file ) ),
            'location' => 'plugins'
        ];
    }

    // Now let's test if we are in the theme dir.
    $theme_root = wp_normalize_path( get_theme_root() );
    if ( 0 === strpos( $file, $theme_root ) ) {
        // Ok, then use get_theme_root_uri.
        $url = set_url_scheme(
            trailingslashit(
                str_replace(
                    untrailingslashit( $theme_root ),
                    untrailingslashit( get_theme_root_uri() ),
                    $test_dir
                )
            )
        );

        return [
            'url' => $url,
            'location' => 'theme'
        ];
    }
}

For example, if we have a plugin named wpse living in plugins-directory/wpse and a theme named my-theme, we can use the following code in either the plugin or the theme to enqueue the admin_image_upload.js file, which lives in different directories in the plugin and theme.

add_action( 'wp_enqueue_scripts', 'wpse_enqueue_js_based_on_location' );
function wpse_enqueue_js_based_on_location() {
    $url_info = wpse_get_url_info_from_file( __FILE__ );
    //exit ( print_r( $url_info ) );

    // Bail if something is wrong.
    if ( ! $url_info ) {
        return;
    }

    // Bail if something is wrong.
    if ( ! isset( $url_info['url'] ) || ! isset( $url_info['location'] ) ) {
        return;
    }

    // Enqueue the JS based on detected location of the file.
    if ( 'plugins' === $url_info['location'] ) {
        wp_enqueue_script( 'wpse-js',  plugins_url( '/admin_image_upload.js', __FILE__ ) );
    } elseif ( 'theme' === $url_info['location'] ) {
        wp_enqueue_script( 'wpse-js', get_template_directory_uri() . '/libs/my_lib/admin_image_upload.js' );
    }
}

When using the code above from a file in the root directory of the wpse plugin, $url_info will look like this:

Array(
    [url] => http://example.com/wp-content/plugins/wpse/
    [location] => plugins
)

If we run the same code from within a theme's functions.php, $url_info will look like this:

Array(
    [url] => http://example.com/wp-content/themes/my-theme/
    [location] => theme
)

Is there a way to determine if my current PHP file is within the plugins' folder or within the themes' folder

Broadly you always have path to current file (__FILE__) so you can always examine it for where you are and compare with context. This might be good enough in custom site, but likely too brittle for public code. There are plenty of edge cases with paths since they can be highly customized.

What you have is not just asset problem, but dependency problem — how do you reuse multiple components, in different ways, and without conflicts.

Personally I’d strongly consider looking into Composer, since it’s de–facto way to manage dependencies in modern PHP. It’s... not perfect for quirks of publicly distributed WP extensions, but your case sounds more like custom–work focused anyway.

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.