I'm using a PHP script to minimize javascript. The problem is that the script frequently takes a second or so before the server responds, which almost completely defeats the purpose. Even if just sending a 304, it still sometimes takes a second or so to send the response, during which time the browser is hanging.
On load, the script functions like this:
- determine what file (or files) to load based on query string var (or use default if query string var is null)
- check if a cached copy of the file is available and up-to-date (this is a .txt file stored on the server)
- if so, do nothing. If not, the cache file is created or recreated (I'm using JShrink 0.5.1 for minification)
- finally, check if the etags and last-modified of the cache file match what the user has
- -- if so, just send a 304
- -- if not, do a readfile on the cache file
It does NOT do this consistently - on one refresh it may take a second, the next time around it'll take 100ms, with both returning a 304. Since it's not consistent my best guess is that the delay is in accessing the cache file on the hard drive (even when sending a 304 it still has to do filemtime() on the cache file and any file(s) in $fileList, and later an md5_file() on the cache file to generate the current etag).
Any thoughts on how to get around this? Here are some relevant blocks of code:
<script type="text/javascript" src="/loadJS.php"></script>
// steps 2 and 3 (check cache, update if necessary, fileList is an array of paths to .js files):
if ( file_exists( $cacheFile ) ) {
$cacheLastModified = filemtime( $cacheFile );
for( $i = 0; $i < sizeof( $fileList ); $i++ ) {
/* check if any files in the list are newer than the cache's last modification date
* if so, cache must be updated
*/
if ( $cacheLastModified < filemtime( $fileList[$i] ) ) {
$outdated = true;
}
}
}
if ( isset( $outdated ) || !file_exists($cacheFile) ) {
// update the cache file now
ob_start();
foreach( $fileList as $file ) {
readfile( $file );
}
$output = ob_get_clean();
include( "includes/JShrink-0.5.1.class.php" );
$output = Minifier::minify($output, array('flaggedComments' => false));
if ( $cacheFileFP = fopen( $cacheFile, "w" ) ) {
if ( fwrite( $cacheFileFP, $output ) ) {
fclose( $cacheFileFP );
} else {
// handle fwrite errors
}
} else {
// handle fopen errors
}
}
// step 4 (function checks etags and modification date):
function fileNotModified ( $file ) {
/* returns false if file NOT modified
* -- or an array of headers if file has been modified
* use two criteria to determine if file has been modified
* a) if-modified-since header vs the file's last-modified time
* b) etag match
* if if-modified-since is the same as file mod time, or etags match
* --- return array of headers to send
* else return false
*
* see http://stackoverflow.com/questions/2000715/answering-http-if-modified-since-and-http-if-none-match-in-php/2015665#2015665
*/
$gmtModifiedTime = gmdate('r', filemtime($file) );
$eTag = md5_file( $file );
$result = false;
if( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) || isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ) {
if ( $_SERVER['HTTP_IF_MODIFIED_SINCE'] == $gmtModifiedTime || str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])) == $eTag ) {
$result["ETag"] = $eTag;
$result["Last-Modified"] = $gmtModifiedTime;
}
}
return $result;
}
// steps 5 and 6 (304 header or send readfile output):
if ( $headers = fileNotModified( $cacheFile ) ) {
// send the headers and 304 not modified
foreach( $headers as $name=>$value ) {
header( $name . ": " . $value );
}
header('HTTP/1.1 304 Not Modified');
/*exit();*/
} else {
// read and output the cache
header("Last-Modified: " . gmdate("r", filemtime( $cacheFile )) );
header("Etag: " . md5_file( $cacheFile ) );
readfile( $cacheFile );
}