DEV Community

Cover image for Auto-Versioning JavaScript and CSS Files in PHP
Antonello Zanini for Writech

Posted on • Originally published at writech.run

Auto-Versioning JavaScript and CSS Files in PHP

How often have your clients complained to you about not being able to see the new changes you have made to their website? This happens to me a lot, and it seems to be quite a common occurrence for other web developers too. For example, perhaps I've just updated the JavaScript and/or CSS files, but my client's browser is still using their cached version (preventing them from seeing any of the new changes) and they're confused as to why they're looking at an older site.

The most common way to tackle this issue is to version JavaScript and CSS files. Unfortunately for me, however, manually versioning all the files every time that I have to perform a deploy is boring and error-prone. Happily, there is a way around this that this article will dive into, so without further ado let's explore two ways to automatically version JavaScript and CSS Files in PHP.

Please note: While we are going to look at how to auto-version JavaScript and CSS files, the two following methods could easily be adapted to images (or any other files) as well.

Method 1

This method aims at harnessing the modification time of a JavaScript or CSS file to implement auto-versioning. The idea is to automatically append a query string to each of our files. This query string represents their own version, whose number is determined from their modification time. In so doing, we are tricking the browser into thinking that new files are being specified when, in fact, browsers usually simply look at the full name of each file (meaning that they will be cached accordingly).

This can be easily achieved with some lines of code, as follows:

<?php

/**
 *  Given a valid file location (it must be an path starting with "/"), i.e. "/css/style.css",
 *  it returns a string containing the file's mtime as query string, i.e. "/css/style.css?v=0123456789".
 *  Otherwise, it returns the file location.
 *
 *  @param $file  the file to be loaded.
 */
function auto_version($file) {
    // if it is not a valid path (example: a CDN url)
    if (strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file)) return $file;

    // retrieving the file modification time
    // https://www.php.net/manual/en/function.filemtime.php
    $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);

    return sprintf("%s?v=%d", $file, $mtime);
}
Enter fullscreen mode Exit fullscreen mode
<?php

const CSS = array(
    '/css/myCSSFile1.css',
    '/css/myCSSFile2.css',
    '/css/myCSSFile3.css',
);

foreach (CSS as $css)   
  echo '<link rel="stylesheet" href="' . auto_version($css) . '" type="text/css">';
Enter fullscreen mode Exit fullscreen mode
<?php

const JS = array(
    '/js/myJsFile1.js',
    '/js/myJsFile2.js',
    '/js/myJsFile3.js',
);

foreach (JS as $js) 
    echo '<script src="' . auto_version($js) . '" type="text/javascript"></script>';
Enter fullscreen mode Exit fullscreen mode

By inspecting the HTML code of the web page, we will see:

<link rel="stylesheet" href="/css/myCSSFile1.css?v=1593800287" type="text/css">
<link rel="stylesheet" href="/css/myCSSFile2.css?v=1693189689" type="text/css">
<link rel="stylesheet" href="/css/myCSSFile3.css?v=1933020647" type="text/css">
Enter fullscreen mode Exit fullscreen mode
<script src="/js/myJsFile1.js?v=1752818290" type="text/javascript"></script>
<script src="/js/myJsFile2.js?v=1912415271" type="text/javascript"></script>
<script src="/js/myJsFile3.js?v=1452837239" type="text/javascript"></script>
Enter fullscreen mode Exit fullscreen mode

The downside of this method is that some browsers may ignore the appended query strings and use the previously cached copy, instead of the new version with the same files. This is why we are going to run through a second method.

Method 2

This method is more complex and aims to automatically append the version of each JavaScript and CSS file directly to their name. Again, the version number is generated using the file modification time. Firstly, we need to add some additional lines to our .htaccess file, since some mod_rewrite rules to allow version numbers in our file names are required:

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]
Enter fullscreen mode Exit fullscreen mode

Apache will now automatically redirect any files with 10 digits (since 10 digits cover all timestamps from 9/9/2001 to 11/20/2286) before a .css or .js extension back to just the filename and extension. With these rules in place, the URL .../style.css can now be rewritten as .../style.0123456789.css and Apache will see those as the exact same files. Additionally, as the second file has a different name from the first one, the browser will ignore the previously cached copy of the file.

Now, we need to change our CSS and JavaScript import logic. This can be easily achieved with a custom PHP function:

<?php

/**
 *  Given a valid file location (it must be an path starting with "/"), i.e. "/css/style.css",
 *  it returns a string containing the file's mtime, i.e. "/css/style.0123456789.css".
 *  Otherwise, it returns the file location.
 *
 *  @param $file  the file to be loaded.
 */
function auto_version($file) {
    // if it is not a valid path (example: a CDN url)
    if (strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file)) return $file;

    // retrieving the file modification time
    // https://www.php.net/manual/en/function.filemtime.php
    $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);

    return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}
Enter fullscreen mode Exit fullscreen mode
<?php

const CSS = array(
    '/css/myCSSFile1.css',
    '/css/myCSSFile2.css',
    '/css/myCSSFile3.css',
);

foreach (CSS as $css)   
  echo '<link rel="stylesheet" href="' . auto_version($css) . '" type="text/css">';
Enter fullscreen mode Exit fullscreen mode
<?php

const JS = array(
    '/js/myJsFile1.js',
    '/js/myJsFile2.js',
    '/js/myJsFile3.js',
);

foreach (JS as $js) 
    echo '<script src="' . auto_version($js) . '" type="text/javascript"></script>';
Enter fullscreen mode Exit fullscreen mode

By inspecting the HTML code of the web page, we will see:

<link rel="stylesheet" href="/css/myCSSFile1.1593800287.css" type="text/css">
<link rel="stylesheet" href="/css/myCSSFile2.1693189689.css" type="text/css">
<link rel="stylesheet" href="/css/myCSSFile3.1933020647.css" type="text/css">
Enter fullscreen mode Exit fullscreen mode
<script src="/js/myJsFile1.1752818290.js" type="text/javascript"></script>
<script src="/js/myJsFile2.1912415271.js" type="text/javascript"></script>
<script src="/js/myJsFile3.1452837239.js" type="text/javascript"></script>
Enter fullscreen mode Exit fullscreen mode

Conclusion

Not being able to view new updates to websites can be a confusing experience for clients, not to mention frustrating for web developers! There is a way to overcome this issue, however, as my article has explored. Using the above methods, the browser will be able to cache our JavaScript and CSS files, but when any changes are made it will not use their cached copies (meaning that we will not run into issues with the clients being unable to view new changes to the site).

I hope that you found this article helpful, thanks for reading!


The post "Auto-Versioning JavaScript and CSS Files in PHP" appeared first on Writech.

Top comments (0)