Supercharging Javascript, Part 6: The Internet Explorer Problem

Previous: Caching on the Client

Of course, no discourse on Web development would be complete without the inevitable "What about IE6?" question and this one is no exception.

Unpatched versions of Internet Explorer 6 do not correctly decompress data using the GZip compression method. Thankfully this is an increasingly uncommon problem because IE6 is dying and unpatched versions are increasingly rarer.

But it is still best practice to detect buggy versions and disable GZip compression (ignoring the GZip accept encoding header) on those versions. Unfortunately ob_gzhandler() does not do this automatically.

<?php
define('SCRIPT_DIR', $_SERVER['DOCUMENT_ROOT'] . '/script/');
define('CACHE_DIR', $_SERVER['DOCUMENT_ROOT'] . '/cache/');

$bundles = array(
  'site' => array(
    'jQuery-1.3.2.js',
    'jquery.bgiframe.js',
    'jquery.dimensions.js',
    'supersubs.js',
    'superfish.js',
    'site.js',
  ),
);

$site = $_GET['site'];
if (!isset($bundles[$site])) {
  error_log("javascript.php: Unknown bundle '$site' requested");
  exit;
}

$mtime = $_GET['mtime'];
$cache_file = CACHE_DIR . $site . '.js';
$cache_mtime = @filemtime($cache_file);

// we need to rebuild is the passed in mtime is newer than the cache file mtime
if ($mtime > $cache_mtime) {
  require 'jsmin-1.1.1.php';
  $scripts = '';
  foreach ($bundles[$site] as $file) {
    $contents = @file_get_contents(SCRIPT_DIR . $file);
    if ($str === false) {
      error_log("javascript.php: Error reading file '$file'");
    } else {
      $scripts .= $contents;
    }
  }
  $min_content = JSMin::minify($scripts);
  file_put_contents($cache_file, $min_content);
} else {
  $min_content = file_get_contents($cache_file);
}

header('Content-Type: text/javascript');
header('Expires: ' . gmdate('D, d M Y H:i:s', time()+365*24*3600) . ' GMT');
header('ETag: "' . md5($min_content) . '"');

if (is_buggy_IE()) {
  ob_start();
} else {
  ob_start('ob_gzhandler');
}

echo $min_content;

function is_buggy_IE() {
  $ret = false;
  $agent = $_SERVER['HTTP_USER_AGENT'];
  if (strpos($agent, 'Mozilla/4.0 (compatible; MSIE ') === 0 && strpos($ua, 'Opera') === false) {
    $version = floatval(substr($agent, 30));
    if ($version < 6) {
      $ret = true;
    } else if ($version == 6 && strpos($agent, 'SV1') === false) {
      $ret = true;
    }
  }
  return $ret;
}
?>

So that's it. The above is a fully-working, fully-tested solution for efficient delivery of Javascript that can make a profound difference to page loading times that doesn't sacrifice one of the great things about PHP: the ability to save a file, reload a Web page and see if what you've done has worked or not. There is no separate build step yet none of the caching functionality is compromised.

Feel free to use the code in any way you wish. Drop me a line or leave a comment if this was helpful to you or you find an issue or simply have a suggestion.

6 comments:

Stephane Mourey said...

Very interesting!
Are you aware of the PhpSpeedy script (http://aciddrop.com/php-speedy/)? It essentially does the same thing, but works also for css files.
I would be please to read a review of it from you.

William Shields said...

I'm not aware of it no. Having had a quick look at it, it looks similar. Like I said though, my goal here wasn't just to deliver some drop-in code. I wanted to work through what the script is trying to accomplish and why as well as the issues involved.

And yes, minus the minification, this script is equally applicable to CSS. In my code I use it for that too.

Anonymous said...

Shouldn't the is_buggy_IE() have its condition like:
if ($version <= 6) ?

Anonymous said...

Ah, sorry, missed the else part... :)

Excelent series of posts!

Coughlin said...

So this line:

http://pastie.org/647040

Replaces all of my current < script > tags, becaue they were replaced in the array couple posts back correct?

I get an undefined error? Do I need to include anything?

Excellent series, thanks so much for this.

Ryan

JasonDavis said...

I wanted to build a script that does EXACTLY this, so thank you so much, I really didn't know how to do it all and you explained it all really well

Post a Comment