diff options
| author | 2013-11-03 20:28:52 +0100 | |
|---|---|---|
| committer | 2013-11-04 23:32:22 +0100 | |
| commit | adc9a958afa5fb9f6f2dab4ae8abac1f932a7db4 (patch) | |
| tree | 4a739d492582f3ba95d66d6f8072f6ecbcc1e654 /lib/http-conditional.php | |
| parent | 231516f5238b6023001bed548569077c61411a4e (diff) | |
Préchargement et requêtes conditionnelles HTTP/1.1
Grosse amélioration des performances en utilisant le cache HTTP :
- Implémentation de HTTP/1.1, c.a.d. If-Modified-Since, If-None-Match,
If-Unmodified-Since, If-Match... avec la librairie
http://alexandre.alapetite.fr/doc-alex/php-http-304/
- Support de HEAD (HTTP /1.0).
- Préchargement de la page suivante (avec link next prefetch) dans le
cas de pagination.
- Et nouvelle possibilité de navigation pour les navigateurs qui
supportent "next".
- La date de dernier changement est pour l'instant primitive et
correspond au dernier changement de la session PHP ou
Configuration.array.php ou application.log ou touch.txt.
- touch.txt est modifié a chaque requête UPDATE ou INSERT ou DELETE.
Diffstat (limited to 'lib/http-conditional.php')
| -rw-r--r-- | lib/http-conditional.php | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/lib/http-conditional.php b/lib/http-conditional.php new file mode 100644 index 000000000..bd038aec3 --- /dev/null +++ b/lib/http-conditional.php @@ -0,0 +1,207 @@ +<?php +/* + Enable support for HTTP/1.x conditional requests in PHP. + Goal: Optimisation + - If the client sends a HEAD request, avoid transferring data and return the correct headers. + - If the client already has the same version in its cache, avoid transferring data again (304 Not Modified). + - Possibility to control cache for client and proxies (public or private policy, life time). + - When $feedMode is set to true, in the case of a RSS/ATOM feed, + it puts a timestamp in the global variable $clientCacheDate to allow the sending of only the articles newer than the client's cache. + - When $compression is set to true, compress the data before sending it to the client and persitent connections are allowed. + - When $session is set to true, automatically checks if $_SESSION has been modified during the last generation the document. + + Interface: + - function httpConditional($UnixTimeStamp,$cacheSeconds=0,$cachePrivacy=0,$feedMode=false,$compression=false) + [Required] $UnixTimeStamp: Date of the last modification of the data to send to the client (Unix Timestamp format). + [Implied] $cacheSeconds=0: Lifetime in seconds of the document. If $cacheSeconds<0, cache is disabled. If $cacheSeconds==0, the document will be revalidated each time it is accessed. If $cacheSeconds>0, the document will be cashed and not revalidated against the server for this delay. + [Implied] $cachePrivacy=0: 0=private, 1=normal (public), 2=forced public. When public, it allows a cashed document ($cacheSeconds>0) to be shared by several users. + [Implied] $feedMode=false: Special RSS/ATOM feeds. When true, it sets $cachePrivacy to 0 (private), does not use the modification time of the script itself, and puts the date of the client's cache (or a old date from 1980) in the global variable $clientCacheDate. + [implied] $compression=false: Enable the compression and allows persistant connections (automatic detection of the capacities of the client). + [implied] $session=false: To be turned on when sessions are used. Checks if the data contained in $_SESSION has been modified during the last generation the document. + Returns: True if the connection can be closed (e.g.: the client has already the lastest version), false if the new content has to be send to the client. + + Typical use: + <?php + require_once('http-conditional.php'); + //Date of the last modification of the content (Unix Timestamp format). + //Examples: query the database, or last modification of a static file. + $dateLastModification=...; + if (httpConditional($dateLastModification)) + { + ... //Close database connections, and other cleaning. + exit(); //No need to send anything + } + //Do not send any text to the client before this line. + ... //Rest of the script, just as you would do normally. + ?> + + Version 1.6.2a, 2008-03-06, http://alexandre.alapetite.fr/doc-alex/php-http-304/ + + ------------------------------------------------------------------ + Written by Alexandre Alapetite, http://alexandre.alapetite.fr/cv/ + + Copyright 2004-2008, Licence: Creative Commons "Attribution-ShareAlike 2.0 France" BY-SA (FR), + http://creativecommons.org/licenses/by-sa/2.0/fr/ + http://alexandre.alapetite.fr/divers/apropos/#by-sa + - Attribution. You must give the original author credit + - Share Alike. If you alter, transform, or build upon this work, + you may distribute the resulting work only under a license identical to this one + (Can be included in GPL/LGPL projects) + - The French law is authoritative + - Any of these conditions can be waived if you get permission from Alexandre Alapetite + - Please send to Alexandre Alapetite the modifications you make, + in order to improve this file for the benefit of everybody + + If you want to distribute this code, please do it as a link to: + http://alexandre.alapetite.fr/doc-alex/php-http-304/ +*/ + +//In RSS/ATOM feedMode, contains the date of the clients last update. +$clientCacheDate=0; //Global public variable because PHP4 does not allow conditional arguments by reference +$_sessionMode=false; //Global private variable + +function httpConditional($UnixTimeStamp,$cacheSeconds=0,$cachePrivacy=0,$feedMode=false,$compression=false,$session=false) +{//Credits: http://alexandre.alapetite.fr/doc-alex/php-http-304/ + //RFC2616 HTTP/1.1: http://www.w3.org/Protocols/rfc2616/rfc2616.html + //RFC1945 HTTP/1.0: http://www.w3.org/Protocols/rfc1945/rfc1945.txt + + if (headers_sent()) return false; + + if (isset($_SERVER['SCRIPT_FILENAME'])) $scriptName=$_SERVER['SCRIPT_FILENAME']; + elseif (isset($_SERVER['PATH_TRANSLATED'])) $scriptName=$_SERVER['PATH_TRANSLATED']; + else return false; + + if ((!$feedMode)&&(($modifScript=filemtime($scriptName))>$UnixTimeStamp)) + $UnixTimeStamp=$modifScript; + $UnixTimeStamp=min($UnixTimeStamp,time()); + $is304=true; + $is412=false; + $nbCond=0; + + //rfc2616-sec3.html#sec3.3.1 + $dateLastModif=gmdate('D, d M Y H:i:s \G\M\T',$UnixTimeStamp); + $dateCacheClient='Thu, 10 Jan 1980 20:30:40 GMT'; + + //rfc2616-sec14.html#sec14.19 //='"0123456789abcdef0123456789abcdef"' + if (isset($_SERVER['QUERY_STRING'])) $myQuery='?'.$_SERVER['QUERY_STRING']; + else $myQuery=''; + if ($session&&isset($_SESSION)) + { + global $_sessionMode; + $_sessionMode=$session; + $myQuery.=print_r($_SESSION,true).session_name().'='.session_id(); + } + $etagServer='"'.md5($scriptName.$myQuery.'#'.$dateLastModif).'"'; + + if ((!$is412)&&isset($_SERVER['HTTP_IF_MATCH'])) + {//rfc2616-sec14.html#sec14.24 + $etagsClient=stripslashes($_SERVER['HTTP_IF_MATCH']); + $is412=(($etagClient!='*')&&(strpos($etagsClient,$etagServer)===false)); + } + if ($is304&&isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) + {//rfc2616-sec14.html#sec14.25 //rfc1945.txt + $nbCond++; + $dateCacheClient=$_SERVER['HTTP_IF_MODIFIED_SINCE']; + $p=strpos($dateCacheClient,';'); + if ($p!==false) + $dateCacheClient=substr($dateCacheClient,0,$p); + $is304=($dateCacheClient==$dateLastModif); + } + if ($is304&&isset($_SERVER['HTTP_IF_NONE_MATCH'])) + {//rfc2616-sec14.html#sec14.26 + $nbCond++; + $etagClient=stripslashes($_SERVER['HTTP_IF_NONE_MATCH']); + $is304=(($etagClient==$etagServer)||($etagClient=='*')); + } + if ((!$is412)&&isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) + {//rfc2616-sec14.html#sec14.28 + $dateCacheClient=$_SERVER['HTTP_IF_UNMODIFIED_SINCE']; + $p=strpos($dateCacheClient,';'); + if ($p!==false) + $dateCacheClient=substr($dateCacheClient,0,$p); + $is412=($dateCacheClient!=$dateLastModif); + } + if ($feedMode) + {//Special RSS/ATOM + global $clientCacheDate; + $clientCacheDate=strtotime($dateCacheClient); + $cachePrivacy=0; + } + + if ($is412) + {//rfc2616-sec10.html#sec10.4.13 + header('HTTP/1.1 412 Precondition Failed'); + header('Cache-Control: private, max-age=0, must-revalidate'); + header('Content-Type: text/plain'); + echo "HTTP/1.1 Error 412 Precondition Failed: Precondition request failed positive evaluation\n"; + return true; + } + elseif ($is304&&($nbCond>0)) + {//rfc2616-sec10.html#sec10.3.5 + header('HTTP/1.0 304 Not Modified'); + header('Etag: '.$etagServer); + if ($feedMode) header('Connection: close'); //Comment this line under IIS + return true; + } + else + {//rfc2616-sec10.html#sec10.2.1 + //rfc2616-sec14.html#sec14.3 + if ($compression) ob_start('_httpConditionalCallBack'); //Will check HTTP_ACCEPT_ENCODING + //header('HTTP/1.0 200 OK'); + if ($cacheSeconds<0) + { + $cache='private, no-cache, no-store, must-revalidate'; + header('Pragma: no-cache'); + } + else + { + if ($cacheSeconds==0) $cache='private, must-revalidate, '; + elseif ($cachePrivacy==0) $cache='private, '; + elseif ($cachePrivacy==2) $cache='public, '; + else $cache=''; + $cache.='max-age='.floor($cacheSeconds); + } + //header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T',time()+$cacheSeconds)); //HTTP/1.0 //rfc2616-sec14.html#sec14.21 + header('Cache-Control: '.$cache); //rfc2616-sec14.html#sec14.9 + header('Last-Modified: '.$dateLastModif); + header('Etag: '.$etagServer); + if ($feedMode) header('Connection: close'); //rfc2616-sec14.html#sec14.10 //Comment this line under IIS + return $_SERVER['REQUEST_METHOD']=='HEAD'; //rfc2616-sec9.html#sec9.4 + } +} + +function _httpConditionalCallBack($buffer,$mode=5) +{//Private function automatically called at the end of the script when compression is enabled + //rfc2616-sec14.html#sec14.11 + //You can adjust the level of compression with zlib.output_compression_level in php.ini + if (extension_loaded('zlib')&&(!ini_get('zlib.output_compression'))) + { + $buffer2=ob_gzhandler($buffer,$mode); //Will check HTTP_ACCEPT_ENCODING and put correct headers such as Vary //rfc2616-sec14.html#sec14.44 + if (strlen($buffer2)>1) //When ob_gzhandler succeeded + $buffer=$buffer2; + } + header('Content-Length: '.strlen($buffer)); //Allows persistant connections //rfc2616-sec14.html#sec14.13 + return $buffer; +} + +function httpConditionalRefresh($UnixTimeStamp) +{//Update HTTP headers if the content has just been modified by the client's request + //See an example on http://alexandre.alapetite.fr/doc-alex/compteur/ + if (headers_sent()) return false; + + if (isset($_SERVER['SCRIPT_FILENAME'])) $scriptName=$_SERVER['SCRIPT_FILENAME']; + elseif (isset($_SERVER['PATH_TRANSLATED'])) $scriptName=$_SERVER['PATH_TRANSLATED']; + else return false; + + $dateLastModif=gmdate('D, d M Y H:i:s \G\M\T',$UnixTimeStamp); + + if (isset($_SERVER['QUERY_STRING'])) $myQuery='?'.$_SERVER['QUERY_STRING']; + else $myQuery=''; + global $_sessionMode; + if ($_sessionMode&&isset($_SESSION)) + $myQuery.=print_r($_SESSION,true).session_name().'='.session_id(); + $etagServer='"'.md5($scriptName.$myQuery.'#'.$dateLastModif).'"'; + + header('Last-Modified: '.$dateLastModif); + header('Etag: '.$etagServer); +} |
