PHP und das Caching via HTTP-Header-ETag

Normalerweise wird das Caching in PHP serverseitig vollautomatisch bestimmt. Das kann gut funktionieren, wir können dies aber noch verbessern indem wir eine extra Funktion in unsere Webprogramme einbauen um noch viel aktiver das Caching via HTTP-Header klientseitig im Browser zu beeinflussen.

Ich könnte euch jetzt direkt viel zu diesen Caching-Controls und dem so genannten eTag (dazu später mehr) erzählen. Aber zunächst möchte ich euch diese kurze PHP-Funktion an den Kopf werfen:

function set_eTagHeaders($file, $timestamp) {
    $gmt_mTime = gmdate('r', $timestamp); 

    header('Cache-Control: public');
    header('ETag: "' . md5($timestamp . $file) . '"');
    header('Last-Modified: ' . $gmt_mTime);

    if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
        if ($_SERVER['HTTP_IF_MODIFIED_SINCE'] == $gmt_mtime || str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])) == md5($timestamp . $file)) {
            header('HTTP/1.1 304 Not Modified');
            exit();
        }
    }
}

Die Funktion wird mit zwei Parametern aufgerufen: der erste ist ein String des Pfades zur aktuellen Datei (sollte auf dem Server auch wirklich vorhanden sein), der zweite ist ein Timestamp der letzten Änderung der Datei.

Im einfachsten Fall wird die Funktion von der aktuellen Datei aufgerufen:

set_eTagHeaders(__FILE__, filemtime(__FILE__));

Nun lasst uns also einen genaueren Blick auf die Funktion oben werfen. Das Wichtigste an ihr ist die Benutzung des so genannten eTag’s. Diesen eTag benutzen wir um einen MD5-Hash, bestehend aus dem Dateipfad und dem Timestamp der letzten Änderungen der Datei. Ändern wir also irgendetwas an der Datei, so ändern wir auch gleichzeitig den Timestamp der letzten Dateiänderung – und somit ergibt sich ein anderer Hashwert.

Vorher senden wir allerdings noch einen Header namens Cache-Control. Durch ihn wird der Browser (und alle Proxys die sonst noch für den Transfer verantwortlich sind) dazu genötigt die Datei gerne zu cachen aber den eTag und den Wert von Last-Modified zu beachten, auf den ich als nächstes komme:

Der dritte HTTP-Header-Tag Last-Modified ist eigentlich überflüssig aber wie sagt man so schön in der Informatik – er ist mit dem Problem „gewachsen“ ;-). Über ihn hat man früher das Caching gesteuert.

Wer mehr über die Caching-Controls im HTTP-Header erfahren will, dem sei dieser Artikel ans Herz gelegt.

Wenn wir dann unsere Headers gesetzt haben checken wir den ankommenden Request (den als die Datei aufgerufen wurde). Dazu benutzen wir die globale Variable $_SERVER, die alle Daten beeinhaltet die wir brauchen.
Der erste Parameter ist HTTP_IF_MODIFIED_SINCE – dieser sollte dem Timestamp entsprechen den wir von der Datei haben (letzte Änderung). Der zweite HTTP_IF_NONE_MATCH sollte unserem generierten Hash entsprechen.
Wenn das beides so ist wird ein HTTP-Header 304 (not modified) ausgegeben und das Script wird beendet. Somit wird der Browser dazu gebeten die Seite aus dem Cache zu laden.

Wenn die beiden Bedingungen nicht passen, so wird das Script weiter ausgeführt und der original PHP-Content wird zum Browser gesendet (erneut mit eTag damit der Browser, Proxy, .. diese Version cachen kann).

Realisierung im ZendFramework

Im ZendFramework gibt es die Möglichkeit gebrauch von der Funktion preDispatch() zu machen. Dort kann eine eigene Cache-Logik implementiert werden:

class myController
{
    public function preDispatch()
    {
        // Your logic here
        $response = $this->getResponse();
        $response->setHeader('Cache-Control', 'public', $replace = true); 
        $response->setHeader('ETag', md5(filemtime(__FILE__).__FILE__), $replace = true);
        $response->setHeader('Last-Modified', gmdate('r', filemtime(__FILE__)), $replace = true);

        if ($fileNotExpired === true) {
            // Set 304 header
            $response->sendResponse();
        } else {
            // Serve the file
        }
    }
}

Dies zeigt wie einfach es selbst im sonst immer ziemlich diffizilen ZendFramework ist, einen solchen Caching-Controller einzubauen.

Flattr this!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.