[script] php class cache : accélérez vos pages !

Nouveau WRInaute
Alors voila, j'ai écris une classe qui gère le cache avec une utilisation simple :
Code:
$cache= new start_cache();
  • le cache démarre lors de l'appel a la classe
  • le cache s'arrête lorsque la classe meure : en bas de la page
  • Vous devez juste regler le repertoire ou attérit votre cache
    Code:
    var $cache_base='/mon/repertoire/physique/de/cache/'; // base path with / ending
  • le délai d'expiration par défaut est de 24h, mais on peut spécifier des délais d'expiration
    Code:
    $cache= new start_cache("monthly");
    autres délais disponibles :
    Code:
     array( "hourly" => 3600, "daily" => 3600*24,"weekly" => 3600*24*7, "monthly" => 3600*24*30, "yearly" => 3600*24*365 )
  • pour faire un test sans cache en appelant la page avec le paramètre "reload" :
    http://mon.site.fr/page.php?reload
  • on peut vider le cache avec une url:
    http://mon.site.fr/page.php?clear_cache=daily ,
    à utiliser avec un webcron ...
  • les logs de cache sont disponibles aussi avec ces paramètres :
    Code:
    var $log_file="log.txt"; // append will be done with $cache_base
    var $log_activated=false;


Code:
<?PHP
class start_cache {
var $cache_base='/mon/repertoire/physique/de/cache/'; // base path with / ending
var $log_file="log.txt"; // append will be done with $cache_base
var $log_activated=false;

	function __construct($planing="daily") {
		if (isset($_GET['clear_cache'])) $this->clear_cache($this->escape_string($_GET['clear_cache']));
		$schedules = array( "hourly" => 3600, "daily" => 3600*24,"weekly" => 3600*24*7, "monthly" => 3600*24*30, "yearly" => 3600*24*365 ); // list of expiration time
		$requested_url=$_SERVER['REQUEST_URI']; //read page addresse
		$requested_url=ereg_replace('/','-',$requested_url); // URI 2 filename
		if($requested_url=="-") $requested_url="-index.html"; // if url=/ rewrite in index.html
		$this->cache_dir=$this->cache_base.$planing;
		$this->cache_file=$this->cache_base.$planing."/cache".$requested_url; // build full cache file path
		$expire=$schedules[$planing]; // get expiration time
		$this->cache_activated=@filemtime($this->cache_file)< (time()- $expire) || isset($_GET['reload']) ; //if not exist or expired or forced
		if ($this->cache_activated) { 
		   ob_start(); // start buffering for the page nothing is sent to the browser
		   $this->logging(" cache started for file ".$this->cache_file, "start");
		} else { // file exist
			include ($this->cache_file); // on le copie ici
			$this->logging($this->cache_file , "read");
			die();
		}
	}
	
	// log event used like this -> argument : Message , [Group]
	// write string 18/01/2011 11:45:16 -> MYSQL: SELECT * FROM table
	function logging($msg, $group="DEFAULT")
	{
		if($this->log_activated) {
			// upper case
			$group=strtoupper ($group);
			// prepare message
			$msg= str_replace("\n"," ",$msg);
			$msg= str_replace("\r"," ",$msg);
			// open file
			$fd = fopen($this->cache_base.$this->log_file, "a");
			// write string 18/01/2011 11:45:16 -> MYSQL: SELECT * FROM table
			fwrite($fd, date("d/m/Y H:i:s")." -> ". $group. ": ".$msg . "\n");
			// close file
			fclose($fd);
		}
	}

	function clear_cache($planing="daily") {
		$this->logging("$planing called from ip ".$_SERVER['REMOTE_ADDR'], "clear");
		if (is_dir($this->cache_base.$planing)) $this->del_tree($this->cache_base.$planing);
		die ($planing." cache cleared !");
	}
	
   	function del_tree($dir)  
 	{ 
		$current_dir = opendir($dir); 
		while($entryname = readdir($current_dir)) { 
			if(is_dir("$dir/$entryname") and ($entryname != "." and $entryname!=".."))  
			{ 
				del_tree("${dir}/${entryname}"); 
			}  elseif($entryname != "." and $entryname!="..") { 
				unlink("${dir}/${entryname}"); 
			} 
		}  
		closedir($current_dir); 
		rmdir(${dir}); 
		$this->logging("remove dir ${dir}", "clear");

 	} 

	// samll function to protect from script attack or SQL injections
	function escape_string($uvar){
	$uvar=preg_replace("/((\%3C)|<)[^\n]+((\%3E)|>)/","",$uvar); // Prevent script CSS attack
	return mysql_escape_string($uvar); // prevent mysql attack
	}
	
	function __destruct() {
		if ($this->cache_activated) {
		 	$contenuCache = ob_get_contents(); //get buffer
		 	ob_end_flush();// end buffer
			if (!is_dir($this->cache_dir)) { 
				mkdir($this->cache_dir); // create dir if needed
				$this->logging($this->cache_dir, "create_dir");
			}
			$fd = fopen($this->cache_file, "w"); // open cache file and clear it
			if ($fd) {
				fwrite($fd,$contenuCache); // Write cache file 
				fclose($fd);
				$this->logging(" cache ended for file ".$this->cache_file."(".strlen($contenuCache).")", "stop");
			} 
		}
	}

}


$cache= new start_cache("yearly");
?>

Votre page hmtl ici
<?php 
echo" et vos codes php qui rament... ";
sleep(4);
?>

et tout ce que vous voulez mettre en cache

Ce code est inspiré de l'idée de fandecine ici :
https://www.webrankinfo.com/forum/t/script-mise-en-cache-des-pages-php.28614/
que je remercie au passage. Son code me plaisait beaucoup mais difficile à intégrer sur un gros projet.

Dites moi si ça marche pour vous ...
 
Nouveau WRInaute
j ai oublié de dire un truc :
ne pas utiliser de cache sur les pages de destination de vos formulaires, surtout les post ou encore si vous offrez du contenu diffèrent pour chaque visiteur !!!
 
WRInaute accro
Il suffit de vérifier si le script reçoit des données en _POST et/ou si le membre est connecté pour activer le cache ou pas.
 
WRInaute accro
C'est plus à faire dans la façon d'utiliser la classe que de l'intégrer dedans... j'ai donné l'exemple avec _POST et membre connecté, mais ça peut être différent dans d'autres applications. De plus il pourrait il y avoir d'autres conditions (isAjax, ...)
 
Nouveau WRInaute
vu que la classe démarre le cache et que si on a du cache on fait un kill, on va pas passer dans le code du coup, la seule façon de neutraliser le cache c'est de regarder si on a des POST avant de démarrer le cache
Code:
if (!(count($_POST)>0)) 
$cache= new start_cache();
ça vaut le coup de mettre le test dans la classe à ton avis ? ou en paramètre de classe ?
Code:
$cache= new start_cache("hourly",!(count($_POST)>0));
je vais rajouter une fonction clean_page tout à l'heure au cas ou on en est besoin.

Je modifierais mon premier message
 
Nouveau WRInaute
ps:Je n'arrive pas éditer mon premier post !

Alors voila, la class cache php supporte maintenant le nettoyage d'un fichier dans n'importe quelle planning de cache
à travers les urls comme les autres fonctions, exemple :
http://mon.site.fr/page.php?clear_cache_file=/mon/uri/page.php

Voici la nouvelle version de code de la class cache php :
Code:
<?php
class start_cache {
var $cache_base='/mon/repertoire/physique/de/cache/'; // base path with / ending
var $log_file="log.txt"; // append will be done with $cache_base
var $log_activated=false;

	function __construct($planing="daily") {
		if (isset($_GET['clear_cache'])) $this->clear_cache($this->escape_string($_GET['clear_cache']));
		if (isset($_GET['clear_cache_file'])) $this->del_file($this->make_name($this->escape_string($_GET['clear_cache_file'])));
		$schedules = array( "hourly" => 3600, "daily" => 3600*24,"weekly" => 3600*24*7, "monthly" => 3600*24*30, "yearly" => 3600*24*365 ); // list of expiration time
		$requested_url=$this->make_name($_SERVER['REQUEST_URI']); //make page addresse
		$this->cache_dir=$this->cache_base.$planing;
		$this->cache_file=$this->cache_base.$planing."/cache".$requested_url; // build full cache file path
		$expire=$schedules[$planing]; // get expiration time
		$this->cache_activated=@filemtime($this->cache_file)< (time()- $expire) || isset($_GET['reload']) ; //if not exist or expired or forced
		if ($this->cache_activated) { 
		   ob_start(); // start buffering for the page nothing is sent to the browser
		   $this->logging(" cache started for file ".$this->cache_file, "start");
		} else { // file exist
			include ($this->cache_file); // on le copie ici
			$this->logging($this->cache_file , "read");
			die();
		}
	}
// create a name like this : cache
	function make_name($address) {
		$address=ereg_replace('/','-',$address); // URI 2 filename
		if($address=="-") $address="-index.html"; // if url=/ rewrite in index.html
		return $address;
	}

	// log event used like this -> argument : Message , [Group]
	// write string 18/01/2011 11:45:16 -> MYSQL: SELECT * FROM table
	function logging($msg, $group="DEFAULT")
	{
		if($this->log_activated) {
			// upper case
			$group=strtoupper ($group);
			// prepare message
			$msg= str_replace("\n"," ",$msg);
			$msg= str_replace("\r"," ",$msg);
			// open file
			$fd = fopen($this->cache_base.$this->log_file, "a");
			// write string 18/01/2011 11:45:16 -> MYSQL: SELECT * FROM table
			fwrite($fd, date("d/m/Y H:i:s")." -> ". $group. ": ".$msg . "\n");
			// close file
			fclose($fd);
		}
	}

	function clear_cache($planing="daily") {
		$this->logging("$planing called from ip ".$_SERVER['REMOTE_ADDR'], "clear_dir");
		if (is_dir($this->cache_base.$planing)) $this->del_tree($this->cache_base.$planing);
		die ($planing." cache cleared !");
	}
	
   	function del_tree($dir)  
 	{ 
		$current_dir = opendir($dir); 
		while($entryname = readdir($current_dir)) { 
			if(is_dir("$dir/$entryname") and ($entryname != "." and $entryname!=".."))  
			{ 
				del_tree("${dir}/${entryname}"); 
			}  elseif($entryname != "." and $entryname!="..") { 
				unlink("${dir}/${entryname}"); 
			} 
		}  
		closedir($current_dir); 
		rmdir(${dir}); 
		$this->logging("remove dir ${dir}", "clear_dir");

 	} 

   	function del_file($file)  
 	{
		$file="cache".$file;
		$this->logging("$file called from ip ".$_SERVER['REMOTE_ADDR'], "clear_file");
		$current_dir = opendir($this->cache_base); 
		while($entryname = readdir($current_dir)) { 
			if(is_dir($this->cache_base."/$entryname") and ($entryname != "." and $entryname!=".."))  
			{ 
				$current_dir2 = opendir($this->cache_base."/$entryname"); 
				while($entryname2 = readdir($current_dir2)) { 
					if(is_file($this->cache_base."/$entryname/".$entryname2) and $entryname2 == $file ) {
						unlink($this->cache_base."/$entryname/".$entryname2); 
						$this->logging("remove file ".$this->cache_base."/$entryname/".$entryname2, "clear_file");
					}
				}
				closedir($current_dir2);
			}  
		}
		closedir($current_dir); 
		die ($file." cache cleared !");  
 	} 

	// samll function to protect from script attack or SQL injections
	function escape_string($uvar){
	$uvar=preg_replace("/((\%3C)|<)[^\n]+((\%3E)|>)/","",$uvar); // Prevent script CSS attack
	return mysql_escape_string($uvar); // prevent mysql attack
	}
	
	function __destruct() {
		if ($this->cache_activated) {
		 	$contenuCache = ob_get_contents(); //get buffer
		 	ob_end_flush();// end buffer
			if (!is_dir($this->cache_dir)) { 
				mkdir($this->cache_dir); // create dir if needed
				$this->logging($this->cache_dir, "create_dir");
			}
			$fd = fopen($this->cache_file, "w"); // open cache file and clear it
			if ($fd) {
				fwrite($fd,$contenuCache); // Write cache file 
				fclose($fd);
				$this->logging(" cache ended for file ".$this->cache_file."(".strlen($contenuCache).")", "stop");
			} 
		}
	}

}


$cache= new start_cache("daily");
?>

Votre page hmtl ici
<?php 
echo" et vos codes php qui rament... ";
sleep(4);?>

et tout ce que vous voulez y mettre en cache
 
WRInaute impliqué
L'instruction "var" n'est plus d'actualité.
De même, tu devrais définir la visibilité de tes méthodes (public, protected, private).

Comme PHP4 n'est plus supporté, il est inutile de maintenir un code pour ce dernier.
 
Nouveau WRInaute
super, merci pour les commentaires de codage !

mais des questions se posent à moi :
  • quelle visibilité pour __constructor et __destructor ?
  • quelle difference entre private et protected ? ça passe au classe extend en protected ?

et sinon l'interet de la classe ? tu trouves ça utile ?
 
WRInaute impliqué
Le constructeur a en général une visibilité public. Sinon, tu ne peux l'instancié de cette manière "new ClassName();" hors de la class. Elle est mise en protected ou private pour créer un singleton en général.

La différence entre protected et private est que :
* protected : empèche l'accès au propriété et méthode hors de la classe
* private : pareil que protected sauffe qu'en plus, la propriété et la méthode ne peut être accédé dans les classes filles.

Je n'ai pas du tout examiné la classe, je ne jugerai donc pas son utilité.
 
Nouveau WRInaute
Finalement, j'ai du mettre cette classe sur un hébergement PHP4. Alors j'ai du faire pas mal de modif si ça peut servir à quelqu'un voici une class php4 de cache obsolète :(
Code:
<?php
    class start_cache {
    var $cache_base="/mon/repertoire/cache/";  //'/mon/repertoire/physique/de/cache/'; // base path with / ending
    var $log_file="log.txt"; // append will be done with $cache_base
    var $log_activated=true;
	function start_cache() {
	 //destructor not working with ob_get_contents
	 //register_shutdown_function(array(&$this, '__destruct'));
	 // must call $this->save(); manually at the end
	 
	 //constructor 
	 $argcv = func_get_args();
	 call_user_func_array(array(&$this, '__construct'), $argcv);
	}

		function save() {
		$this->__destruct();
		}
		
       function __construct($planning="daily") {
          if (isset($_GET['clear_cache'])) $this->clear_cache($this->escape_string($_GET['clear_cache']));
          if (isset($_GET['clear_cache_file'])) $this->del_file($this->make_name($this->escape_string($_GET['clear_cache_file'])));
          $schedules = array( "minute"=>60 ,"hourly" => 3600, "daily" => 3600*24,"weekly" => 3600*24*7, "monthly" => 3600*24*30, "yearly" => 3600*24*365 ); // list of expiration time
          $requested_url=$this->make_name($_SERVER['REQUEST_URI']); //make page addresse
          $this->cache_dir=$this->cache_base.$planning;
          $this->cache_file=$this->cache_base.$planning."/cache".$requested_url; // build full cache file path
          $expire=$schedules[$planning]; // get expiration time
          $this->cache_activated=@filemtime($this->cache_file)< (time()- $expire) || isset($_GET['reload']) ; //if not exist or expired or forced
          if ($this->cache_activated) {
             // start buffering for the page nothing is sent to the browser
			ob_start();
			$this->logging("cache started for file ".$this->cache_file."($planning)", "start");
          } else { // file exist
             include ($this->cache_file); // on le copie ici
             $this->logging($this->cache_file , "read");
             die();
          }
       }
	//singleton detector   
	   function &getInstance() {
            static $instance = null;
            if (null === $instance) {
                $instance = new start_cache();
            }
            return $instance;
        }

    // create a name like this : cache
       function make_name($address) {
          $address=ereg_replace('/','-',$address); // URI 2 filename
          if($address=="-") $address="-index.html"; // if url=/ rewrite in index.html
          return $address;
       }

       // log event used like this -> argument : Message , [Group]
       // write string 18/01/2011 11:45:16 -> MYSQL: SELECT * FROM table
       function logging($msg, $group="DEFAULT")
       {
          if($this->log_activated) {
             // upper case
             $group=strtoupper ($group);
             // prepare message
             $msg= str_replace("\n"," ",$msg);
             $msg= str_replace("\r"," ",$msg);
             // open file
             $fd = fopen($this->cache_base.$this->log_file, "a");
             // write string 18/01/2011 11:45:16 -> MYSQL: SELECT * FROM table
             fwrite($fd, date("d/m/Y H:i:s")." -> ". $group. ": ".$msg . "\n");
             // close file
             fclose($fd);
          }
       }

       function clear_cache($planning="daily") {
          $this->logging("$planning called from ip ".$_SERVER['REMOTE_ADDR'], "clear_dir");
          if (is_dir($this->cache_base.$planning)) $this->del_tree($this->cache_base.$planning);
          die ($planning." cache cleared !");
       }
       
          function del_tree($dir) 
       {
          $current_dir = opendir($dir);
          while($entryname = readdir($current_dir)) {
             if(is_dir("$dir/$entryname") and ($entryname != "." and $entryname!="..")) 
             {
                del_tree("${dir}/${entryname}");
             }  elseif($entryname != "." and $entryname!="..") {
                unlink("${dir}/${entryname}");
             }
          } 
          closedir($current_dir);
          rmdir(${dir});
          $this->logging("remove dir ${dir}", "clear_dir");

       }

          function del_file($file) 
       {
          $file="cache".$file;
          $this->logging("$file called from ip ".$_SERVER['REMOTE_ADDR'], "clear_file");
          $current_dir = opendir($this->cache_base);
          while($entryname = readdir($current_dir)) {
             if(is_dir($this->cache_base."/$entryname") and ($entryname != "." and $entryname!="..")) 
             {
                $current_dir2 = opendir($this->cache_base."/$entryname");
                while($entryname2 = readdir($current_dir2)) {
                   if(is_file($this->cache_base."/$entryname/".$entryname2) and $entryname2 == $file ) {
                      unlink($this->cache_base."/$entryname/".$entryname2);
                      $this->logging("remove file ".$this->cache_base."/$entryname/".$entryname2, "clear_file");
                   }
                }
                closedir($current_dir2);
             } 
          }
          closedir($current_dir);
          die ($file." cache cleared !"); 
       }

       // samll function to protect from script attack or SQL injections
       function escape_string($uvar){
       $uvar=preg_replace("/((\%3C)|<)[^\n]+((\%3E)|>)/","",$uvar); // Prevent script CSS attack
       return mysql_escape_string($uvar); // prevent mysql attack
       }
       
       function __destruct() {
          if ($this->cache_activated) {
              $contenuCache = ob_get_contents(); //get buffer
              ob_end_flush();// end buffer
             if (!is_dir($this->cache_dir)) {
                mkdir($this->cache_dir); // create dir if needed
                $this->logging($this->cache_dir, "create_dir");
             }
			 if(!$contenuCache===false) {
				 $fd = fopen($this->cache_file, "w"); // open cache file and clear it
				 if ($fd) {
					fwrite($fd,$contenuCache); // Write cache file
					fclose($fd);
					$this->logging("cache ended for file ".$this->cache_file."(".strlen($contenuCache).")", "stop");
				 }
			} else{
					$this->logging("cache ended without content", "warning");
			}
          }
       }

    }
?>

qui s'utilise ainsi en header :
PHP:
<span class="syntaxdefault"><?php include</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'includes/class_cache.inc.php'</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> if </span><span class="syntaxkeyword">(!(</span><span class="syntaxdefault">count</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">$_POST</span><span class="syntaxkeyword">)></span><span class="syntaxdefault">0</span><span class="syntaxkeyword">))</span><span class="syntaxdefault"> $cache</span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> new start_cache</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"yearly"</span><span class="syntaxkeyword">);</span><span class="syntaxdefault"> </span><span class="syntaxcomment">//can  be hourly, daily, weekly, monthly, yearly </span><span class="syntaxdefault">?></span>
et ainsi en footer :
PHP:
<span class="syntaxdefault"><?php $cache</span><span class="syntaxkeyword">-></span><span class="syntaxdefault">save</span><span class="syntaxkeyword">();</span><span class="syntaxdefault"> ?></span>
Merci PHP4 qui ne supporte pas les fonction ob_get_contents sur les function activé en __destruct :(
 
Discussions similaires
Haut