This PHP code generates an eTag for an xml file. The problem is eTag updates only when the file it self is updated/modified. I need the etag to update when the dynamic results are updated as well. any idea how this can be done?
//get the last-modified-date of this very file
$lastModified=filemtime(__FILE__);
//get a unique hash of this file (etag)
$etagFile = md5_file(__FILE__);
//get the HTTP_IF_MODIFIED_SINCE header if set
$ifModifiedSince=(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false);
//get the HTTP_IF_NONE_MATCH header if set (etag: unique file hash)
$etagHeader=(isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : false);
//set last-modified header
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $lastModified)." GMT");
//set etag-header
header("Etag: $etagFile");
//make sure caching is turned on
//check if page has changed. If not, send 304 and exit
if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])==$lastModified || $etagHeader == $etagFile)
{
header("HTTP/1.1 304 Not Modified");
exit;
}
You can do something like this.
Get a unique hash of dynamic content instead of file and set it as eTag
.
<?php
$last_modified = filemtime( __FILE__ );
$modified_since = ( isset( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) ? strtotime( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) : false );
$etagHeader = ( isset( $_SERVER["HTTP_IF_NONE_MATCH"] ) ? trim( $_SERVER["HTTP_IF_NONE_MATCH"] ) : false );
// This is the actual output from this file (in your case the xml data)
$content = 'your xml data from db or file';
// generate the etag from your output
$etag = sprintf( '"%s-%s"', $last_modified, md5( $content ) );
//set last-modified header
header( "Last-Modified: ".gmdate( "D, d M Y H:i:s", $last_modified )." GMT" );
//set etag-header
header( "Etag: ".$etag );
// if last modified date is same as "HTTP_IF_MODIFIED_SINCE", send 304 then exit
if ( (int)$modified_since === (int)$last_modified && $etag === $etagHeader ) {
header( "HTTP/1.1 304 Not Modified" );
exit;
}
// new content or file modified, so output ypur content
echo $content;
exit;
?>
You can use get_included_files() to get all PHP files that have been used to generate the file.
Once you generate the file for a first time, store that list in some sort of storage (associated with the URL, of course), and give to the client the largest filemtime() from that list. Next time, retrieve the file list, and check if any of the files' filemtime() is larger than the one given in the HTTP request. Regenerate as soon you find one, or deliver the cached response if you don't find one.
Obviously, this approach assumes that generating the response and/or sending it is very performance costly. Otherwise, doing all those filemtime() checks can end up being more taxing than simply regenerating the file each time.
This is my actual production code, using CRC32 which is faster than a crypto hash.
<?php
// pull xml feed from wordpress blogs! then build an etag from the crc32 of the content! BOOOOMMMMMMMM
$xml=("http://www.funk.co.nz/auckland-music-update/feed/");
$xmlDoc = new DOMDocument();
$xmlDoc->load($xml);
$last_modified = filemtime( __FILE__ );
$modified_since = ( isset( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) ? strtotime( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) : false );
$etagHeader = ( isset( $_SERVER["HTTP_IF_NONE_MATCH"] ) ? trim( $_SERVER["HTTP_IF_NONE_MATCH"] ) : false );
// This is the actual output from stream
$content = $xmlDoc->saveXML() . "\n";
// pull a set of blogs
$items = $xmlDoc->getElementsByTagName('item');
// pull out the last blog title (for use in page)
$latestBlog=$items->item(0)->getElementsByTagName('title')->item(0)->nodeValue;
// generate the etag from xml content
$etag = sprintf( '"%s-%s"', $last_modified, crc32( $content ) );
//set last-modified header
header( "Last-Modified: ".gmdate( "D, d M Y H:i:s", $last_modified )." GMT" );
//set etag-header
header( "Etag: ".$etag );
// if last modified date is same as "HTTP_IF_MODIFIED_SINCE", send 304 then exit
if ( (int)$modified_since === (int)$last_modified && $etag === $etagHeader ) {
header( "HTTP/1.1 304 Not Modified" );
exit;
}
?>