Go to main content

Textpattern CMS support forum

You are not logged in. Register | Login | Help

#1 2014-06-04 11:13:54

philwareham
Core designer
From: Haslemere, Surrey, UK
Registered: 2009-06-11
Posts: 3,564
Website GitHub Mastodon

Cache busting CSS and JavaScript

Just wondering what people use for cache busting CSS and JavaScript on their Textpattern-powered sites, if any of you do?

Offline

#2 2014-06-04 12:00:15

Bloke
Developer
From: Leeds, UK
Registered: 2006-01-29
Posts: 11,271
Website GitHub

Re: Cache busting CSS and JavaScript

For files-based resources (when using rvm_css, for example), I tend to append the last-modified timestamp as a query param.

I have a (non-Txp-specific) PHP function I wrote which appends a timestamp to either a single URL, or a bunch of them. If you throw a tag as the 2nd argument, it’ll create full tags from all the URLs given, complete with stamped resources. If I was to Txp-ise it or make it a plugin, the function would become a lot simpler as I could use tag() and join_atts().

Doesn’t work for database CSS though as it’s not (currently) timestamped.

For the vaguely interested, here’s the code. Not fully tested yet, just an idea I had and jotted down in a ‘Maybe to do one day’ scratchpad:

/**
 * Add a timestamp to the given resource for cache-busting purposes.
 *
 * If passed a single $url, just that entry is stamped.
 * If passed an array and an HTML tag (script or link) it generates the
 * relevant tag, with the given attributes.
 *
 * @param  string|array $url  URL(s) to append timestamps to
 * @param  string       $tag  HTML tag to create from each $url
 * @param  array        $atts HTML attributes to apply to each $tag
 * @return string
 */
function stampFile($url, $tag = null, $atts = array())
{
	if (!is_array($url)) {
		$url = array($url);
	}

	$out = array();

	foreach ($url as $thing) {
		// Permitted tags:
		// -> key: 'url' attribute name for this tag.
		// -> sc: whether the tag is self-closing or not.
		$ctags = array(
			'script' => array('key' => 'src',  'sc' => false),
			'link'   => array('key' => 'href', 'sc' => true),
		);

		$parts = parse_url($thing);
		$path = ((strpos($parts['path'], '/') === 0) ? $_SERVER['DOCUMENT_ROOT'] : '') . $parts['path'];
		$ver = filemtime($path);

		$thing .= ((isset($parts['query'])) ? '&' : '?') . 'mstamp='.$ver;

		if ($tag === null) {
			$out[] = $thing;
		} else if (array_key_exists($tag, $ctags)) {
			$attlist = array();
			$attmerge = array($ctags[$tag]['key'] => $thing) + $atts;
			foreach ($attmerge as $key => $val) {
				$attlist[] = htmlspecialchars($key) . '=' . '"' . htmlspecialchars($val) . '"';
			}

			$tagout = '<' . $tag . ($attlist ? ' ' . implode(' ', $attlist) : '') . '>';
			if ($ctags[$tag]['sc'] === false) {
				$tagout .= '</' . $tag . '>';
			}

			$out[] = $tagout;
		}
	}

	return implode("\n", $out) . (($tag === null) ? '' : "\n");
}

It currently only permits ‘script’ and ‘link’ tags to be created, but it could be exetended to cater for images or whatever.


The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.

Txp Builders – finely-crafted code, design and Txp

Offline

#3 2014-06-04 12:40:51

gaekwad
Server grease monkey
From: People's Republic of Cornwall
Registered: 2005-11-19
Posts: 4,137
GitHub

Re: Cache busting CSS and JavaScript

Bloke wrote #281230:

For files-based resources (when using rvm_css, for example), I tend to append the last-modified timestamp as a query param.

+1. I don’t use it regularly, but I tack on GMT epoch time as a query:

<link href="//example.com/default.css?1401885603" media="screen" rel="stylesheet" type="text/css">

Offline

#4 2014-06-04 12:45:27

philwareham
Core designer
From: Haslemere, Surrey, UK
Registered: 2009-06-11
Posts: 3,564
Website GitHub Mastodon

Re: Cache busting CSS and JavaScript

Thanks, and yes I am referring to flat files of CSS and JavaScript.

@pete

I want to avoid using query strings in the URL, as this article explains.

Offline

#5 2014-06-04 13:07:53

philwareham
Core designer
From: Haslemere, Surrey, UK
Registered: 2009-06-11
Posts: 3,564
Website GitHub Mastodon

Re: Cache busting CSS and JavaScript

I think I can work something out using a combination of Grunt and rah_flat to write UNIX epoch time onto the CSS/JS URLs in Textpattern page/form templates when copied from my src directory to the public directory.

Jukka did a similar thing when he set up the build process for this Textpattern forum, so I’ll reverse engineer that.

Thanks anyway!

Offline

#6 2014-06-04 13:16:43

Bloke
Developer
From: Leeds, UK
Registered: 2006-01-29
Posts: 11,271
Website GitHub

Re: Cache busting CSS and JavaScript

philwareham wrote #281232:

I want to avoid using query strings in the URL

Sure, if you want to use revisioned or datestamped filenames instead of query params you can use the htaccess route and serve resources that way. Equally valid. A good example that I’ve used, if you don’t mind having to remember to reapply the .htaccess patch each upgrade.


The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.

Txp Builders – finely-crafted code, design and Txp

Offline

#7 2014-06-04 13:32:58

gaekwad
Server grease monkey
From: People's Republic of Cornwall
Registered: 2005-11-19
Posts: 4,137
GitHub

Re: Cache busting CSS and JavaScript

philwareham wrote #281232:

I want to avoid using query strings in the URL, as this article explains.

Makes sense. Consider me schooled -thanks for the link.

Last edited by gaekwad (2014-06-04 13:33:12)

Offline

#8 2014-06-04 15:20:40

philwareham
Core designer
From: Haslemere, Surrey, UK
Registered: 2009-06-11
Posts: 3,564
Website GitHub Mastodon

Re: Cache busting CSS and JavaScript

Jukka’s solution (for forum templates, but it equates to the same principle as Textpattern templates) was to put a placeholder hook into the filenames of CSS/JS links source template files in src build files and use Grunt to automatically write a UNIX Epoch timestamp into that hook as the build copied those files to public.

<link rel="stylesheet" href="style/Textpattern/css/main.@@timestamp.css">

I think I’ll use that as a basis, seems like the least manual effort.

Offline

#9 2014-06-05 21:31:45

Gocom
Developer Emeritus
From: Helsinki, Finland
Registered: 2006-07-14
Posts: 4,533
Website

Re: Cache busting CSS and JavaScript

In Txpmag we simply read the modification timestamp:

<txp:variable name="assets_url" /><txp:php>
    $file = trim(parse('<txp:yield />'));
    $path = pathinfo($file);
    $time = filemtime(txpath . '/../assets/' . $file);
    echo $path['dirname'] . '/' . $path['filename'] . '.' . $time . '.' . $path['extension'];
</txp:php>

Save as a asset from and use like so:

<txp:output_form form="asset">css/main.css</txp:output_form>

And it then returns http://assets.example.tld/css/main.123456789.css

This can be then combined with a caching solution, like Memcached too:

<txp:rah_memcached>
    <txp:output_form form="asset">css/main.css</txp:output_form>
</txp:rah_memcached>

Offline

#10 2014-06-06 08:19:55

philwareham
Core designer
From: Haslemere, Surrey, UK
Registered: 2009-06-11
Posts: 3,564
Website GitHub Mastodon

Re: Cache busting CSS and JavaScript

@Gocom

Nice solution, thanks!

Offline

Board footer

Powered by FluxBB