2010-06-04 10 views
4

J'ai un fichier PHP qui retournera la même chose avec les mêmes paramètres $ _GET à chaque fois - c'est déterministe.Rendre la page PHP return "304 Not Modified" si elle n'a pas été modifiée

Malheureusement pour l'efficacité (ce fichier est demandé très souvent), Apache affiche par défaut une réponse "200 OK" chaque fois qu'une page PHP est demandée, ce qui oblige l'utilisateur à télécharger à nouveau le fichier.

Est-il possible d'envoyer un 304 Not Modified en-tête si et seulement si les paramètres sont les mêmes?

Bonus: Puis-je définir une date d'expiration sur elle, de sorte que si la page en cache est plus que, disons, trois jours, il envoie une réponse « 200 OK »?

Répondre

10

Sans la mise en cache la page vous (ou au moins son GATE) vous ne pouvez pas vraiment faire usage de la 304. Un algorithme de mise en cache à part entière est un peu hors de portée, mais l'idée générale:

<?php 
function getUrlEtag($url){ 
    //some logic to get an etag, possibly stored in memcached/database/file etc. 
} 
function setUrlEtag($url,$etag){ 
    //some logic to get an etag, possibly stored in memcached/database/file etc. 
} 
function getPageCache($url,$etag=''){ 
    //[optional]some logic to get the page from cache instead, possibly not even using etag 
} 
function setPageCache($url,$content,$etag=''){ 
    //[optional]some logic to save the page to cache, possibly not even using etag 
} 
ob_start(); 
$etag = getUrlEtag($_SERVER['REQUEST_URI']); 
if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { 
    header("HTTP/1.1 304 Not Modified"); 
    exit; 
} 
if(($content=getPageCache($_SERVER['REQUEST_URI'],$etag))!==false){ 
    echo $content; 
    exit; 
} 
?> 
//the actual page 
<?php 
$content = ob_get_clean(); 
setUrlEtag($_SERVER['REQUEST_URI'],$etag=md5($url.$content)); 
function setPageCache($_SERVER['REQUEST_URI'],$content,$etag); 
header("Etag: $etag"); 
echo $content; 
?> 

Toutes les communes les pièges s'appliquent: vous ne pouvez pas afficher les pages de cache pour les utilisateurs connectés, une mise en cache du contenu partiel est plus souhaitable, vous êtes responsable de la prévention du contenu obsolète dans le cache (éventuellement en utilisant des triggers dans le backend ou la base de données autour avec la logique getUrlEtag), etc. etc.

Vous co uld joue également avec HTTP_IF_MODIFIED_SINCE si c'est plus facile à contrôler.

+0

Cela me semble être l'idée générale. –

+0

Bien que pour l'OP, il n'est pas nécessaire de mettre en cache quoi que ce soit. Selon ses conditions, il peut simplement calculer un Etag basé sur id. –

+0

Ouais, la mise en œuvre réelle peut être aussi simple ou aussi élaborée que nécessaire dans la situation. J'ai essayé d'illustrer cela, et pourtant je suis tombé dans l'écueil de fournir une implémentation, md5() sur le contenu, ne devrait pas être là: P – Wrikken

2

Avez-vous essayé header("HTTP/1.0 304 Not Modified"); dans votre code PHP qui s'appelle? Si vous ne le connaissez pas, vous voudrez le mettre dans votre code AVANT de commencer à sortir quoi que ce soit dans le tampon.

http://php.net/manual/en/function.header.php

+0

je devrais être appelé à chaque fois que le script est accédé? –

7

En général, vous revenez codes d'état HTTP en utilisant la fonction d'en-tête:

Header("HTTP/1.1 304 Not Modified"); 
exit(); 

Cependant, cela seul ne suffit pas. Le problème est que vous ne savez pas comment demandé le fichier, donc vous aurez besoin d'un peu de coopération de navigateur.

Vous pouvez rechercher les en-têtes If-modified-since dans la demande entrante et renvoyer le code d'état approprié s'il est présent et dans la plage de dates. Si vous envoyez un en-tête Expires correct lors de la génération initiale du PHP, le navigateur ou le cache de proxy peut décider de ne pas récupérer la requête du tout (bien que plus probable, ils vont définir l'en-tête If-modified-since). Sans un en-tête Expires, le navigateur réessaiera probablement toujours la requête complète.

Pour plus d'informations, consultez http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html et recherchez « 14,25 »

Le navigateur fera la mise en correspondance des paramètres GET à la copie en cache, btw. Vous n'avez pas besoin de faire du travail là-bas.

0

Ce script restaure tout le script PHP, mais après cela, il vérifie si ETag est équivalent au MD5 de la chaîne de sortie et si oui, il envoie un 304 et aucune bande passante n'est utilisée. Vous pouvez aussi créer une telle chose avec le MD5 de tous les QueryString etc. et le stocker quelque part, vous auriez pas besoin de recréer le contenu de sortie (encore plus rapide)

function sanitize_output($buffer) { 
    $headers = apache_request_headers(); 
    $tt5=md5($buffer); 
    header('ETag: '.$tt5); 
    if (isset($headers['If-None-Match']) && $headers['If-None-Match']===$tt5) { 
     header('HTTP/1.1 304 Not Modified'); 
     header('Connection: close'); 
     exit(); 
    } 
    return $buffer; 
} 

ob_start("sanitize_output"); 
Questions connexes