Path and URL of files in subfolders

The best way to bust the browser cache for scripts and stylesheets is to append the last time the files contents was changed to the filename. This way the browser believes that it has a new file and simply reloads it.

In WordPress it’s pretty easy to do so, as you can append the version number. It only gets tricky if you try to load that from within a plugins subfolder. Here’s one way to get the files URL and path from any location in your plugin:

[plugins]
├── [plugin-folder]
│   ├── [js]
│   │   └── scripts.css
│   ├── [css]
│   │   └── style.css
│   ├── [inc]
│   |   └── assets.class.php
│   └── bootstrap.class.php
├── [foo]
└── bar.php

Example: We want to load the script.js file from inside assets.class.php.

wp_enqueue_script(
    'plugin_script_handle',
    plugins_url( 'js/script.js', dirname( __FILE__ ) ),
    array( 'jquery' ),
    filemtime( plugin_dir_path( dirname( __FILE__ ) ).'js/script.js' ),
    true
);

That’s it!

7 thoughts on “Path and URL of files in subfolders

  1. Another option that doesn’t require making a system call for each referenced file, is to simply declare a constant for the version number and use that constant wherever you enqueue a script / stylesheet in your plugin / theme.

    To force cache busting during development, make use of the SCRIPT_DEBUG constant in WordPress, like this:

    if (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG)
        define('MY_PLUGIN_VERSION', time());
    else
        define('MY_PLUGIN_VERSION', '1.6.6');
    
    • Thanks for adding your considerations. Here’re my thoughts on it:

      Albeit you can use a constant, one shouldn’t. They add to the global namespace, which should be avoided and I recommend against it.

      About the SCRIPT_DEBUG constant: Yes, this is one way, but you can’t rely on it as it’s for debugging and the user possibly won’t have turned it on. You could as well use time() or, if it is set, use $_SERVER['REQUEST_TIME'] as file version number, but that would prevent browser caching completely.

      So what I’ve shown in this post is what I consider to be “best practice” as it covers the most cases with as less lines as possible, is usable pretty much everywhere (in a plugin) and has the least impact on the global namespace and ecosystem. And appending the filemtime() works as well with CDNs.

      • I think I’d prefer the minimal (in-process) overhead of a constant, defined once per plugin and accessed very quickly, over a file access (via system call) per file. System calls are more expensive than in-process array lookups.

        Regarding SCRIPT_DEBUG, I did say “during development”; it isn’t something you’d expect to find set on a production system. I also use it for switching between minified and unminified scripts and CSS. During development, uncached unminified code is a benefit, not a hindrance :)

        If you have an aversion to using even a small handful (single digits) of PHP constants in your plugin, make them class static constants. But it isn’t worth sweating over little things like a few global constants, especially when your plugin is likely to be running alongside a greedy power-hungry plugin like Gravity Forms or WooCommerce :)

        cheers,
        Ross

    • Sadly you can’t, as it would as well add the folder name of the folder you’re currently in. So if you would be inside

      /plugin-folder/inc/assets.php

      and try to load a script from inside

      /plugin-folder/assets/js/script.js

      the script loading would fail as WP would not get rid of the current subfolder and the script would get loaded from

      /plugin-folder/inc/assets/js/script.js
      • Sorry, I should clarify: if you’re in the base plugin folder, you can just use __FILE__. Otherwise, you will need to use dirname() to exit out of the current directory for as many subfolders you’re working in.

        I just usually access the $version variable from inside a class initialized in the main file, or do everything in the one file.

        Also, from your folder tree it looks as if bootstrap.class.php is in the wp-content/plugins folder instead of the plugin folder. Is this intended?

Comments are closed.