I found a way of speeding up the static publisher a bit by adding a compressed version of each cached file to the cache. The reason why I did this, and didn’t simply use the mod_deflate module or the ob_gzhandler function, was due to the following:
- mod_deflate and ob_gzhandler dynamically compress data. This is great for dynamic content, but we’re working with static content. It doesn’t make sense to compress a file over and over when we could just compress it once.
- Some Web hosts may not have the mod_deflate module available.
Creating compressed versions of static content is a bit tricky, since no function really exists in PHP to generate the exact deflated content that Web browsers expect. Here’s a snippet of the code I used:
cms/code/staticpublisher/FilesystemPublisher.php
class FilesystemPublisher extends StaticPublisher {
…
/**
* Create a second set of cached files that are compressed using the PHP function gzcompress().
* By default, this is false, but so many browsers support gzip encoding that you should seriously consider setting this to true.
*/
public static $create_compressed_files = true;
…
$files[] = array(
'Content' => $content,
'Folder' => dirname($path).'/',
'Filename' => basename($path),
);
// Should we create a compressd version of the file?
if (self::$create_compressed_files && $this->fileExtension != 'php') {
$gzsize = strlen($content);
$gzcrc = crc32($content);
$gzdata = gzcompress($content);
$gzdata = substr($gzdata, 0, strlen($gzdata) - 4);
$files[] = array(
'Content' => "\x1f\x8b\x08\x00\x00\x00\x00\x00" . $gzdata . pack('V', $gzcrc) . pack('V', $gzsize),
'Folder' => dirname($path).'/',
'Filename' => basename($path).'.gz',
);
}
…
Like I said… a bit tricky. Thankfully this code will only be run when the static cache is generated.
Here’s the changes made to sapphire/static-main.php:
…
// Look for the file in the cachedir
$file = preg_replace('/[^a-zA-Z0-9]/si', '_', trim($_SERVER['REQUEST_URI'], '/'));
$file = $file ? $file : 'index';
$acceptCompressed = (preg_match('/gzip/', $_SERVER["HTTP_ACCEPT_ENCODING"]));
$fileExtension = '.html';
$compressedExists = ($acceptCompressed && file_exists('../cache/'.$cacheDir.$file.$fileExtension.'.gz'));
// If a compressed version of this file exists, output a header that says we're about to send compressed data.
if ($compressedExists)
{
header('Content-Encoding: gzip');
$fileExtension .= '.gz';
}
$gmNow = @gmdate('D, d M Y H:i:s \G\M\T');
if ($compressedExists || file_exists('../cache/'.$cacheDir.$file.$fileExtension)) {
$fileToSend = '../cache/'.$cacheDir.$file.$fileExtension;
header('X-cache: hit at '.$gmNow);
echo file_get_contents($fileToSend);
} elseif (file_exists('../cache/'.$cacheDir.$file.'.php')) {
header('X-cache: hit at '.$gmNow);
include_once '../cache/'.$cacheDir.$file.'.php';
if ($cacheDebug) echo "<h1>File was cached</h1>";
} else {
header('X-cache: miss at '.$gmNow . ' on ' . $cacheDir . $file);
// No cache hit... fallback!!!
include 'main.php';
if ($cacheDebug) echo "<h1>File was !NOT! cached</h1>";
}
…
It’s somewhat of a cheap hack but it works. On average, it reduces HTML bandwidth by about 80%, and you don’t even need to mess with the Apache or PHP configuration.