Sunday, March 25, 2007

History reloaded

My last post was about Really Simple History and how to get it to work in IE7. I forgot to mention the problems you easily get then using dhtmlHistory.js and IE if you serve your pages from a script language. This is because for dynamic history to work, IE must preserve the content of a form between page reloads. This can only happend if the web server returns a status code 304 (Not modified) on the page access.

In my case I used PHP as the server side script language to build my pages and had problems getting F5 to reload the page and stay on the current page. After a page reload, dhtmlHistory.js considered it a first load and the home page was shown again.

The solution I found after a while was to check if any of the pages had been modified and return the 304 status code from PHP. I also use an ETag header to make the checking for modifications simple.

The index.php file creates the HTML by listing all page_*.php files in a directory. For this I use the glob PHP function. After that I check for the file that was modified last, this is because this time will be used as modification date for the generated page. Then serving the generated page an ETag header will also be generated. This ETag will be constructed by using the md5 PHP function on the modification time.

On hitting F5 the web browser will send this ETag back in a header (if-none-match) to check for modified content. In PHP this header will be called $_SERVER["HTTP_IF_NONE_MATCH"] and the code checks if the current modification time is the same, if so a 304 status is returned.

Here is the code from index.php:


$pages = glob("page_*.php");

$mtime = filemtime("index.php");
foreach($pages as $page_file) {
$m = filemtime($page_file);
if ($m > $mtime) {
$mtime = $m;
}
}


$etag=(isset($_SERVER['HTTP_IF_NONE_MATCH']))? $_SERVER['HTTP_IF_NONE_MATCH']:"";

$etime = $mtime + 3600; // How long can a cache server save this content without asking again? (1 hour)

if ($etag == md5($mtime)) {
header('HTTP/1.0 304 Not Modified');
die;
}

header("Last-Modified: " . gmdate('D, d M Y H:i:s', $mtime) . ' GMT');
header("Expires: ". gmdate('D, d M Y H:i:s', $etime) . ' GMT');
header("ETag: " . md5($mtime));

No comments: