Software Engineer

yii VersionAssetManager

· by jsnby · Read in about 3 min · (477 Words)
Computers Yii PHP

Update 2014-08-13:

A new version of the VersionAssetManager has been produced that is compatible with Yii2. Read more about it here or find it as an installable package on Packagist, or the source code on Github.

I’ve been playing with the Yii PHP framework for a while now. I evaluated several PHP frameworks and found that I liked Yii the best.

I started building apps and ran into a problem with cache busting for images, css, javascript, etc.. I found this article that talks about one strategy for using the CAssetManager to publish your assets for you to counter caching issues. I deployed that as my solution, but a couple of other problems surfaced:

  1. I publish my assets into a sub-folder of the assets folder, so often times the modification timestamp on the assets folder wouldn’t get updated and I would still get old assets
  2. Since the url used to access the assets contains a hash based on the modification time of a folder on the filesystem, you could run into issues if you have multiple load-balanced webservers with different modification times on that particular directory.

I decided I still wanted everything I gained by using the AssetManagement paradigm, I just didn’t like how it hashed things and tracking down caching issues isn’t how I like to spend my time. I introduce to you the VersionAssetManager (available in this gist on Github for now).

I didn’t change much from CAssetManager except what it’s hashing. Traditionally, CAssetManager hashed the path + the Yii version + the modification timestamp. My VersionAssetManager hashes path + Yii version + application version.

Did he just say application version? I sure did. Every time I deploy my site, the verision number gets incremented. I use jenkins and package my sites into RPMS. As part of the build, Jenkins adds a file to the rpm called version.php that contains

<?php return '1.0.<jenkins build number>';

This gets loaded in my config/web.php:

    return array(
            'version' => is_file($protectedPath . '/config/version.php')
                ? require_once($protectedPath . '/config/version.php')
                : 'Development',

Now I can access Yii::app()->params['version'] anywhere in my application. You may notice a problem though. In my development environment, version.php doesn’t exist, so the version will always be set to ‘Development’ and thus my hash will always collide and won’t bust cache. To bust cache in development I decided to hash the asset’s path + the current time. This means that every pageview will generate a new hash (unless you have multiple pageviews during the same second) and thus you’ll be making copies of lots of assets into your assets directory which you’ll pay for in both disk space and page load time, but you’ll never have to worry about busting cache during development.

Now I don’t have to worry about busting cache while developing and when I load balance my site to multiple web servers, they will each use the same hash based on the application’s version number to access the assets.