How can I manage browser cache in PHP?

835 views Asked by At

My idea is simple, take all css files and generate one minified in a time of change in some css file. Then tell the browser to clear the cache. If there is an unchanged file in browser cache then use it - so user don't need to redownload it every time.

I'm using the following snip of code to do that. But the part with using cache is a bit buggy, most of time it works but sometimes it tell the browser to use the cached version (as there is no change) and browser is using the old one and user must do client side cache refresh.

Could you give me some advice how to do that, so it would refresh client side browser cache everytime when the change occurs and if there is no change just use the cache?

$cssFiles = getCssFiles();

$fm = new FileMinifier(FileMinifier::TYPE_CSS);
$lastModified = $fm->lastModification($cssFiles);
$savedLastModified = DateUtils::convertToTimestamp($this->system->systemSettings['cssLastChange']);

$etagFile = md5('css-file');
header("Content-type: text/css");
header("Pragma: public");
header('Cache-Control: public');
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $lastModified) . " GMT");
header("Etag: $etagFile");

// if there is a change - generate new minified css
if ($lastModified > $savedLastModified)
{
    // take files minify them, save it and redirect to output, update last change time
    ...
}
// or use already generated
else
{
    $ifModifiedSince = (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : 0);
    $etagHeader = (isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : false);
    // if it is in chache use it! - no need for redownloading
    if (strtotime($ifModifiedSince) == $lastModified || $etagHeader == $etagFile)
    {
        header("HTTP/1.1 304 Not Modified");
        exit;
    }
    $this->data['text'] = file_get_contents(SystemInfo::getServerRoot() . '/public/css/minified.css');
}
1

There are 1 answers

2
Elliot B. On

What you're trying to do is admirable, but it's a bit of re-inventing the wheel. As far as CSS files, JavaScript files, etc are concerned, modern browsers already do a fine job of pulling unchanged files from the cache.

Manipulating the HTTP headers to notify the browser of a file change is do-able, but there are browser differences (especially older browsers) in how the headers are interpreted which makes that approach fraught with nuance.

It is far easier to accomplish your goal by versioning your CSS includes. A change in file version will prompt the browser to re-download the file.

Example:

Before file change:

<link href="http://yourwebsite.com/file.css?_=1.0.0.1" rel="stylesheet" type="text/css">

After file change:

<link href="http://yourwebsite.com/file.css?_=1.0.0.2" rel="stylesheet" type="text/css">

All browsers will interpret the change in URI parameter as a new file and will re-download it.

It's also possible to automate the versioning so that you don't need to manually edit the include line after every change. Here's one way to do it...

Example:

<?php
    $ver = filemtime($filename);
    echo '<link href="http://yourwebsite.com/file.css?_='.$ver.'" rel="stylesheet" type="text/css">';
?>

That code will place append modified date of the file (Unix timestamp format) to the URI of the file include.