CastlesBlog.com

a blog about a blog

Subscribe

Using PHP to minify Javascript and CSS

An Update to the Dynamic Javascript include

Back in February I wrote the code to combine all the javascript into one file with the intention to limit the requests to the server as recommended by PageSpeed and YSlow. I have revisited this code to make it do a few extra things:

So lets look at the new script...

<?php

$files = array(
    
    '../javascript/jquery.easing.js'
    
    ,'../templates/castlesblog/template.js'

);
    
$modified = 0;

foreach($files as $file) {        
    $age = filemtime($file);
    if($age > $modified) {
        $modified = $age;
    }
}

$offset = 60 * 60 * 24 * 7; // Cache for 1 weeks
header ('Expires: ' . gmdate ("D, d M Y H:i:s", time() + $offset) . ' GMT');

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $modified) {
    header("HTTP/1.0 304 Not Modified");
    header ('Cache-Control:');
} else {
    header ('Cache-Control: max-age=' . $offset);
    header ('Content-type: text/javascript; charset=UTF-8');
    header ('Pragma:');
    header ("Last-Modified: ".gmdate("D, d M Y H:i:s", $modified )." GMT");
    
    function compress($buffer) {
        /* remove comments */
        $buffer = preg_replace("/((?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:\/\/.*))/", "", $buffer);
        /* remove tabs, spaces, newlines, etc. */
        $buffer = str_replace(array("\r\n","\r","\t","\n",'  ','    ','     '), '', $buffer);
        /* remove other spaces before/after ) */
        $buffer = preg_replace(array('(( )+\))','(\)( )+)'), ')', $buffer);
        return $buffer;
    }
    
    ob_start('ob_gzhandler');

        foreach($files as $file) {
            if(strpos(basename($file),'.min.')===false) { //compress files that aren't minified
                ob_start("compress");
                    include($file);
                ob_end_flush();
            } else {
                include($file);
            }
        }
    
    ob_end_flush();
}

Explanation

  1. Firstly, you will see an array of javascript files to include. It loops through each one to work out which is the newest.
  2. The next two lines set a header that should cache the file for one week.
  3. After that we work out if the modified date is newer than what the browser has in cache and if so return the Not Modified header.
  4. Alternatively if the file is modified or new to the browser it sets/resets a few more headers and then loops through our array again and includes the code.

I've wrapped the include in two ob_start methods. The first one gzips the output and the second runs the defined compress function.

It will only run the compress on files that don't have .min. in the filename. This was added because the compress function messes up scripts that are already minified such as jQuery. The compress function uses a few regular expressions for the minification. Suggestions on how to improve this is welcome.

A few things to note

CSS Minification

The CSS is using a similar minification process.

<?php

$files = array(
    
    '../templates/castlesblog/template.css'

);
    
$modified = 0;

foreach($files as $file) {        
    $age = filemtime($file);
    if($age > $modified) {
        $modified = $age;
    }
}

$offset = 60 * 60 * 24 * 7; // Cache for 1 weeks
header ('Expires: ' . gmdate ("D, d M Y H:i:s", time() + $offset) . ' GMT');

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $modified) {
    header("HTTP/1.0 304 Not Modified");
    header ('Cache-Control:');
} else {
    header ('Cache-Control: max-age=' . $offset);
    header ('Content-type: text/css; charset=UTF-8');
    header ('Pragma:');
    header ("Last-Modified: ".gmdate("D, d M Y H:i:s", $modified )." GMT");
    
    function compress($buffer) {
        /* remove comments */
        $buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
        /* remove tabs, spaces, newlines, etc. */
        $buffer = str_replace(array("\r\n","\r","\n","\t",'  ','    ','     '), '', $buffer);
        /* remove other spaces before/after ; */
        $buffer = preg_replace(array('(( )+{)','({( )+)'), '{', $buffer);
        $buffer = preg_replace(array('(( )+})','(}( )+)','(;( )*})'), '}', $buffer);
        $buffer = preg_replace(array('(;( )+)','(( )+;)'), ';', $buffer);
        return $buffer;
    }
    
    ob_start('ob_gzhandler');

        foreach($files as $file) {
            if(strpos(basename($file),'.min.')===false) { //compress files that aren't minified
                ob_start("compress");
                    include($file);
                ob_end_flush();
            } else {
                include($file);
            }
        }
    
    ob_end_flush();
}

Changes to .htaccess

To make nice urls to these files the following is added to the .htaccess

RewriteRule ^scripts.js$ includes/scripts.php [L]

RewriteRule ^styles.css$ includes/styles.php [L]

Use Google's CDN for jQuery

One final change was to remove jQuery from the included scripts and use Google CDN version.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script>!window.jQuery && document.write('<script src="js/jquery-1.4.2.min.js"><\/script>')</script>

Here is a few reasons why this is a good idea.

Post Comment