Mathieu Hays

In this article, I will introduce you to the usage of the built-in WordPress function to add scripts and styles in theme or plugin while also fixing the browser caching issue related to the built-in handling of the file’s versioning.

You want to use this method for you website to be optimised ( resources are loaded once ) and flexible ( avoid conflict between plugins ). In other words, no need to load jQuery 5 times per page. The registering part is not required but fairly recommended to enable more modularity in your projects.

<?php
add_action('wp_enqueue_scripts', 'myproject_add_scripts');
function myproject_add_scripts()
{
  wp_register_script(
    'script-id', // Script unique ID
    get_stylesheet_directory_uri() . '/app.js', // Location of the file
    array('jquery', 'my-jquery-plugin'), // Dependencies
    '1.0', // Version
    true // Put in the footer (or not)
  );
  // don't forget to enqueue the script after registering it
  wp_enqueue_script('script-id');
}
?>

The built-in behaviour of WordPress will print these resources as /path/to/the/location/of/your/script.js?ver=1.0. This is bad though. Because of query argument in the url, the browser can’t cache it. So I came up with a little function to change the urls registered in WordPress to be printed differently enabling the browser to cache the file and enabling you to reset that cache without having to change the filename manually !

<?php
function cache_enabled_resources( &$obj, $handle 
{
  if(!isset($obj->registered[$handle])) {
    return false;
  }

  $resource = $obj->registered[$handle];
  $final_src = $resource->src;

  if (!preg_match('/^(https?:)?\/\//', $final_src) && !($obj->content_url && 0 === strpos($final_src, $obj->content_url))) {
    $final_src = $obj->base_url . $final_src;

    if (!empty($resource->ver)) {
      // 1.11.1 => 101101 - avoid conflict on 11.1.1 & 1.11.1 >> 1111
      $ver = str_replace(".", "0", $resource->ver);
      $ext = get_class($obj) == "WP_Scripts" ? 'js' : 'css';

      if ($this->cache_busting) {
        $final_src = preg_replace("/(.$ext)$/", ".$ver.$ext", $final_src);
      }
    }
  }
  
  return $final_src;
}
function cache_enabled_resources_script_filter($src, $handle)
{
  global $wp_scripts;

  $out = cache_enabled_resources($wp_scripts, $handle);

  return $out === false ? $src : $out;
}
add_filter('script_loader_src', 'cache_enabled_resources_script_filter', 10, 2);
function cache_enabled_resources_style_loader($src, $handle)
{
  global $wp_styles;

  $out = cache_enabled_resources($wp_styles, $handle);

  return $out === false ? $src : $out;
}
add_filter('style_loader_src', 'cache_enabled_resources_style_loader', 10, 2);

The script above will format the url so the version number is included to the url. Instead of /path/to/script.js?v=1 the url printed will be /path/to/script.1.js. As it is, it’ll output a 404 error. So you’ll have to add a few lines in your htaccess (or apache config) to enable the redirection to the original file. This is important that you add those lines before the WordPress rules otherwise the condition will not be matched.

I took this apache configuration from HTML5boilerplate ( if you don’t know this already you should definitely check it out as it’s a source of great advices and good practices ).

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^(.+)\.(\d+)\.(css|js)$ $1.$3 [L]
</IfModule>

Feel free to let me know your thoughts in the comments or if something doesn’t make sense.

EDIT: 14/05/2015 – Fix cache_enabled_resources for WP 4.2.0+