Search

A new extension, "Static Asset Versioning" is now available for download. Comments and feedback can be left here but if you discover any issues, please post it on the issue tracker.

This extension provides you with a sha1 hash of your static assets, so you can have agressive caching, but still making sure all your visitors will use the latest scripts and styles.

To use, simply install the extension. I have not had the time to make the configuration easier, but this will come in the next release. For now, just edit these lines in extension.driver.php to match your setup:

    $watch_dirs = array(
        'js' => WORKSPACE . '/js',
        'css' => WORKSPACE . '/css',
    );

    $extensions = array(
        'css',
        'js'
    );

Example output:

<assets>
  <js>
    <app-ck.js>77d68d889</app-ck.js>
    <app.js>1a3ac2a00</app.js>
    <bootstrap-alert.js>9cfbeb5d6</bootstrap-alert.js>
    <bootstrap-button.js>a8f204c81</bootstrap-button.js>
    <bootstrap-carousel.js>265e52c5f</bootstrap-carousel.js>
    <bootstrap-collapse.js>320469b1c</bootstrap-collapse.js>
    <bootstrap-dropdown.js>2664c0490</bootstrap-dropdown.js>
    <bootstrap-modal.js>e12c769aa</bootstrap-modal.js>
    <bootstrap-popover.js>99aee04c5</bootstrap-popover.js>
    <bootstrap-scrollspy.js>bd739439a</bootstrap-scrollspy.js>
    <bootstrap-tab.js>8b3264cc1</bootstrap-tab.js>
    <bootstrap-tooltip.js>b9809fcb0</bootstrap-tooltip.js>
    <bootstrap-transition.js>aa978472d</bootstrap-transition.js>
    <bootstrap-typeahead.js>01f6c6fab</bootstrap-typeahead.js>
    <carousel.js>c01d4b6d4</carousel.js>
    <flickr-widget.js>acdac461d</flickr-widget.js>
    <paginate-ck.js>6788645c4</paginate-ck.js>
    <paginate.js>ec9b205bd</paginate.js>
    <twitter-ck.js>25429fa03</twitter-ck.js>
    <twitter.js>20ecf0567</twitter.js>
  </js>
  <css>
    <style.css>53b2ef39e</style.css>
  </css>
</assets>   

edit: no, I am not using all these JS files separately in my own projects, this is just for the demonstration:)

Example usage:

< script src="{$static-workspace}/js/app-ck-{/data/assets/js/app-ck.js}.js" type="text/javascript">< /script>

gives:

< script src="http://localhost/workspace/js/app-ck-77d68d889.js" type="text/javascript">< /script>

nice! been manually doing the versioning so far so this is indeed pretty welcome :)

Very cool, same as @gunglien, I've been manually upping the versioning, this will save some time!

@brendo, @gunglien that's exactly what I am using it for, too. Hope you enjoy it!

Time saver! Cheers :)

Very nice Huib, thanks!

If I understand correctly this extension is called on every FrontendOutputPreGenerate correct? I can not find any checks on identical hashes so I presume it is called on every page load?

Could this have a negative impact on performance?

@David, yes, that's exactly right.

I knew this question would come, so I've added the profiler to the code:) On my local machine without XCache or APC, the results are as follows:

Engine Initialisation   0.0000 s
Page creation process started   0.0258 s
XML Built   0.2787 s
XML Generation  0.0267 s
Page Built  0.3271 s
Asset Hashes Generated  0.0029 s    <---    Here it is!
XSLT Transformation 0.0000 s
Page creation complete  0.4707 s

The assets calculated here are the assets I've listed in the example, so that's about 20 of them, and as you can see the performance impact is very, very low.

edit: regarding the sha1 hashing, I found that the algorithm was perfectly able to keep up with my SSD, while only using about 6-7% of cpu on a late 2011 macbook pro. In fact, at first I wanted to use the modification date as the source for the hash, but I found that there was absolutely no difference in performance - not for the handful of files you'll be running this on, that is.

Awesomes. Thanks Huib.

@brendo, I do have a feature request, which I found while working on this extension. Right now the FrontendOutputPreGenerate delegate is passed the XML, but it's the generate()'ed XML, so doing manipulations on it requires you to either parse it again or do string manipulations (as I've done for this extension).

I'd much rather have the object to work with than the string of text. Is this something we can change, or would that mess up the API too much?

Sounds like a bug to be honest, PreGenerate implies before it gets generated, so I too would expect the XMLElement version, not the generated string.

Sounds like a bug to be honest, PreGenerate implies before it gets generated, so I too would expect the XMLElement version, not the generated string.

That's what I would expect too. Am I correct in assuming that regex on a string is slower than manipulating an object?

Am I correct in assuming that regex on a string is slower than manipulating an object?

Yes, a bit. Fortunately I've been able to use a simple str_replace call, which is a bit faster than a regex. However, speed isn't the main reason I'd like to change this, as even a preg_replace will generally fast enough.

The biggest advantage is that it's possible to do xpath expressions here, things that aren't simply possible with regex.

@brendo, do you think it's possible to change the API for 2.4?

@creativedutchmen you have to keep in mind that some times the XML in some parts might actually be in plain text rather then an object.

I think cache-able extension puts the complete string rather then re-building the objects. It's part of the definition of XMLElement that it can either contain an object or a string. So probably that would have to be taken into consideration.

@gunglien, true that. Ah well, for this extension it won't really matter, since I'll be dealing with the root (data) node only, but I can imagine that being a problem for other extensions doing more advanced stuff.

Another reason to make the change from XMLElement to DOMDocument, I'd say!

@brendo, do you think it's possible to change the API for 2.4?

Yeah, can you file a bug so we don't forget?

That would be a breaking change, right? I've got some things going on this delegate.

@creativedutchmen , or guys - sorry for my ignorance - and what actually serves those versioned files is a .htaccess rule? 'cause I tried it out of the box and it didn't worked for me, so I understood I have to handle those unique filename calls e.g. with a rewrite rule. Or maybe I write too soon as its still a v0.1 and some devel knowledge is expected by default, sorry.

As of .htaccess I tried out something like this, but maybe it has some bad impact on the Symphony .htaccess rule set as whole, that is I am not that comfortable with it and only trying for this time being.

### VERSIONING
RewriteCond %{REQUEST_FILENAME} !-s
RewriteCond %{REQUEST_URI} -[a-z0-9]+.(css|js)$ [NC]
RewriteRule ^(.*)-[a-zA-Z0-9]+.(css|js)$ $1.$2 [L]

Anyway, its a great extension, thanks!

@juro, yeah, the documentation (and user-friendly-ness) still need to come :)

Your rule looks spot on to me, and I think there will be no side-effects from that. There are just two things you must keep in mind when using this extension (which you have!):

  • Rewrite, rather than redirect. Since the versioning is meant to "bust the cache", the browser (or proxy) must see the resource as a unique entity. The target of a redirect can (and will) be cached, despite different URL's pointing there.
  • Avoid query strings. Some caching proxies (most notably Squid) will not cache the file when there is a query string in the URL. So, while style.css?v=127 is very tempting, it's best to put the version in the URL, like: style-127.css - unless you are absolutely sure your CDN or cache will cache the file (CloudFront has an option for that).

In all honesty, I am breaking rule #2 myself most of the time, since I am either running varnish myself or using CloudFront - both of which cache regardless of the querystring, or can be configured to do so.

When the project I am using this for is finished (within the next few weeks) I'll be sure to expand the documentation and provide some example rewrite rules. Thanks for the feedback!

Thanks for an early wrap up and for those important points! Yes, about the #2, I also use the ?v= more, and your example actually led me to this page (just for to reference), as I thought it must to have some good point in doing so.

Great :), Thanks again and good luck with your project!

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details