Florian Purchess

webtypographythings that matter

21. January 2010

Speeding up Javascript

While working with Anthony Short’s amazing CSScaffold, the idea came up to have a similar compressing and caching mechanism for javascript. For this little experiment I took Douglas Crockford’s javascript compressor, ported to php by Ryan Grove (visit JSMin’s repository on github for more infos) and clashed it up with some lightweight additional code. It is pretty easy to integrate it into running applications or frameworks, for example simply put it into cakePHP’s javascript-directory. There is no need to change something in your application’s code. You can try the result yourself: Javascript Cache and Compressor.

Cache-Class

Just as easy as possible:

  • is_expired compares the file modification time to check out, if the cache is expired
  • write creates the folder structure if necessary and saves the minified javascript-code into a cache-file
  • read returns the content of a cache-file, if it’s not expired
  • lastmod is used to set header-informations for browser-cache during output
class Cache {
    public static function is_expired($file, $cache_file) {
        if ( !file_exists($cache_file) ) return true;

        return ( filemtime($cache_file) <= filemtime($file) );
    }

    public static function write($cache_file, $content) {
        if( !is_dir(dirname($cache_file)) ) {
            mkdir(dirname($cache_file), 0777, true);
            chmod(dirname($cache_file), 0777);
        }

        file_put_contents($cache_file, $content);

        chmod($cache_file, 0777);
        touch($cache_file, time());
    }

    public static function read($file, $cache_file) {
        if ( !self::is_expired($file, $cache_file) ) return file_get_contents($cache_file);
        return false;
    }

    public static function lastmod($file, $cache_file) {
        if ( file_exists($cache_file) ) return filemtime($cache_file);
        return filemtime($file);
    }
}

Config-File

Really self explaining. It provides some levers for developing / debugging and the only configuration that has to be made: the javascript base-dir.

/**
 * Set to FALSE to prevent error_reporting
 */
define('DEBUG', FALSE);

/**
 * Set to TRUE to enable javascript-compress via jsmin
 */
define('MINIFY', TRUE);

/**
 * Path to javascript-directory
 */
define('BASE_DIR', '/app/webroot/js/');

/**
 * Set to FALSE to disable caching
 */
define('CACHE', TRUE);

/**
 * Set to FALSE to force a recache
 */
define('FORCE_RECACHE', FALSE);

/**
 * Relative path to cache-directory
 */
define('CACHE_DIR', './cache/');

Index.php

It’s task is to tie JSMin and cache together with some logic using the config-values as easy as possible:

if ( DEBUG === false ) { error_reporting(0); } else { error_reporting(E_ALL | E_NOTICE); }

$result = false;
if ( CACHE && !FORCE_RECACHE ) $result = Cache::read(FILE, CACHE_FILE);
if ( $result === false ) {
    $result = file_get_contents(FILE, CACHE_FILE);
    if ( MINIFY ) $result = JSMin::minify($result);
	if ( CACHE && MINIFY ) Cache::write(CACHE_FILE, $result);
}

header('Content-Type: text/javascript');
header("Vary: User-Agent, Accept");
header('Last-Modified: '. gmdate('D, d M Y H:i:s', Cache::lastmod(FILE, CACHE_FILE)) .' GMT');

echo $result;

.htaccess

Links all javascript-files to index-file and by that way uses apache’s mod_deflate for a bit more performance.

<IfModule mod_deflate.c>
	SetOutputFilter DEFLATE
</IfModule>
<IfModule mod_rewrite.c>
	RewriteEngine on
	RewriteCond %{REQUEST_FILENAME}	-f
	RewriteCond %{REQUEST_URI} \.js$
	RewriteRule ^(.+)$ jsmin/index.php?request=%{REQUEST_URI}
</IfModule>

Thats all. I’m sure there is a lot more room for improvement and I’ll love to read your issues!

<code>class Cache {
public static function is_expired($file, $cache_file) {
if ( !file_exists($cache_file) ) return true;

return ( self::lastmod($cache_file) <= filemtime($file) );
}

public static function write($cache_file, $content) {
if( !is_dir(dirname($cache_file)) ) {
mkdir(dirname($cache_file), 0777, true);
chmod(dirname($cache_file), 0777);
}

file_put_contents($cache_file, $content);

chmod($cache_file, 0777);
touch($cache_file, time());
}

public static function read($file, $cache_file) {
if ( !self::is_expired($file, $cache_file) ) return file_get_contents($cache_file);
return false;
}

public static function lastmod($file, $cache_file) {
if ( file_exists($cache_file) ) return filemtime($cache_file);
return filemtime($file);
}
}

No Comments

Leave a Comment

  • Latest Article

    • Quality in Web?

      I’d been thinking about quality in web these days.  What supports a webdeveloper writing quality rich code? My result is a list with five central thoughts. This list could have been a blast of ideological presumptions. However, that ideological approach is as hard to defend as it’s general. So I tried to focus on the [...] read more »

  • Similar Tagged

    • Advanced CSS/JS Inclusion Technique

      Setting up an environment for small websites is pretty easy. Mostly everyone evolved his own way of structuring assets, stylesheet, javascript and serversided scripts. There’s no need of splitting CSS or JS into a quadrillion of files, due it causes unnecessary requests to your server. That works until complexity makes it impossible to keep track [...] read more »