Cache-cache en php
Aujourd'hui, je vous propose de voir comment créer un système simple (simpliste ?) de cache pour votre site web.
On trouve plusieurs codes de ce genre sur le net, mais ils sont souvent trop élaborés à mon goût.
Je détaille ici tout le cheminement.
Ce cache devra :
- être simple à utiliser,
- permettre de stocker une page complète, une ou plusieurs parties d'une page ou même une variable,
- n'avoir besoin d'aucune configuration, création de dossier etc...
- gérer seul l'obsolescence des fichiers temporaires (le fichier est renouvelé au bout d'un certain temps),
- permettre le vidage complet du cache ou l'effacement individuel des fichiers,
-
être efficace
Pour les impatients, le fichier c'est par là ;)
La fonction fondamentale d'un cache étant la sauvegarde et l'extraction de données du dossier temporaire, on commencera donc par créer deux fonctions:cache_write() pour écrire des données et cache_read() pour les lire.
Ces deux fonctions recevront en argument le nom du fichier temp à créer/lire.
La fonction cache_write() aura en outre besoin qu'on lui fournisse les données à stocker ainsi que la durée de vie du fichier à créer.
Afin d'éviter de se compliquer la vie avec des créations de dossier temp, des chemins variables etc, la fonction créera un dossier temp local s'il n'y en a pas.
Quant à la fonction cache_read(), elle devra évidemment vérifier l'existence du fichier avant de le charger et retourner false si le fichier n'existe pas: ainsi nous saurons s'il est nécessaire ou pas de créer le contenu.
function cache_read($fichier){ if (file_exists('temp/'.$fichier)){ // on ne charge que si le fichier existe () $donnees=file_get_contents('temp/'.$fichier); // si le fichier existe, on le charge return $donnees; }else{return false;} }
function cache_write($fichier,$donnees,$duree){ if (!is_dir('temp/')){mkdir ('temp');}//crée le /temp/ si nécessaire file_put_contents('temp/'.$fichier,$donnees); }
Toutefois, cache_write() ne permet pour l'instant que de stocker une chaine...
Si on veut pouvoir conserver le résultat d'une requête sql un peu longue par exemple, il faudra pouvoir stocker un array().
Pour cela, on doit le sérializer avant de l'enregistrer.
function cache_write($fichier,$donnees,$duree){ if (!is_dir('temp/')){mkdir ('temp');}//crée le /temp/ si nécessaire if (is_array($donnees)){$donnees=serialize($donnees);} file_put_contents('temp/'.$fichier,$donnees); }
Mais dans ce cas, il faudra que cache_read puisse unserializer les données au cas où.
Plutôt que de s'embêter à faire des test... on va simplement vérifier si unserialize renvoie autre chose que false en inhibant les erreurs (oui, je sais, c'est maaaaal!):
function cache_read($fichier){ if (file_exists('temp/'.$fichier)){ // on ne charge que si le fichier existe () $donnees=file_get_contents('temp/'.$fichier); // si le fichier existe, on le charge if ($donnees2=@unserialize($donnees)){$donnees=$donnees2;} // si unserialize renvoie un contenu, on remplace $donnees return $donnees; }else{return false; }// si pas de fichier dans le cache, on renvoie faux }
Oui, mais et la variable $duree alors?!
Ben on va s'en occuper de suite: pour que le cache sache (ouch!) si le fichier est périmé ou pas, je propose de postdater le fichier.
La fonction touch() permet de fixer la date de modification d'un fichier.
Il suffira donc d'ajouter la $duree à l'heure de création et de touch() le fichier.
function cache_write($fichier,$donnees,$duree){ if (!is_dir('temp/')){mkdir ('temp');}//crée le /temp/ si nécessaire if (is_array($donnees)){$donnees=serialize($donnees);} file_put_contents('temp/'.$fichier,$donnees); if ($duree!=0){$duree=@date('U')+(60*$duree);}// on multiplie par 60 pour pouvoir fournir une durée en minutes touch('temp/'.$fichier,$duree); }
Date('U') renvoie le nb de secondes Unix. On y ajoute la durée en minutes x 60.
Pourquoi tester si la $duree est à 0 ?
On se servira d'une durée à 0 pour permettre la création de fichiers sans date de péremption.
La fonction cache_read() doit à présent savoir détecter si le fichier demandé est obsolète ou pas.
Créons une fonction qui vérifie si un fichier est obsolète et qui le détruit si c'est le cas:
function cache_is_obsolete($fichier){ $dat=@filemtime('temp/'.$fichier); // on récup la date du fichier if (!file_exists('temp/'.$fichier)){return true;} // si le fichier n'existe pas, il est obsolèteif ($dat==0){return false;}// si la date est à 0: il n'est pas obsolète if ($dat<@date('U')){ // si la date du fichier est dépassée, il est obsolète cache_delete($fichier); // on le vire return true; // il était obsolète ! } return false; // si on en est là, c'est que tout va bien, le fichier est toujours valide... }
Bien sûr, il nous faut créer une fonction pour effacer le fichier:
function cache_delete($fichier){if (file_exists('temp/'.$fichier)){unlink ('temp/'.$fichier);}}
Il suffit d'ajouter le recours à cache_is_obsolete() dans cache_read() dans les conditions de validation:
function cache_read($fichier){ if (file_exists('temp/'.$fichier)&&!cache_is_obsolete($fichier)){ // on ne charge que si le fichier existe () ou qu'il n'est pas obsolete $donnees=file_get_contents('temp/'.$fichier); // si le fichier existe, on le charge if ($donnees2=@unserialize($donnees)){$donnees=$donnees2;} // si unserialize renvoie un contenu, on remplace $donnees return $donnees; }else{return false;}// si pas de fichier dans le cache, on renvoie faux }
A ce stade, on peut écrire et lire des données dans le cache avec une date de péremption gérée automatiquement.
Mais comment collecter les données ?
Grâce aux fonctions php ob-start() et ob_get_clear() qui permettent de détourner les sorties vers une variable.
Je propose de créer une fonction cache_start() qui se contentera de faire un ob_start() (pour gagner en lisibilité lors de l'utilisation) et une fonction cache_end() pour récupérer les données, les traiter et les sauver.
function cache_start(){ob_start();}function cache_end($fichier,$duree){ // termine la mise en cache $donnees=ob_get_clean(); // on récupère le contenu cache_write($fichier,$donnees,$duree); // on le sauvegarde via cache_write() return $donnees; // on renvoie les données }
Là, on a presque terminé.
Pour se servir de nos fonctions on devra tester si cache_read() renvoie quelque-chose:
- si oui, on echo ou on utilise les données du cache,
- sinon on démarre le cache d'abord, puis on crée le contenu et on fait appel à cache_end() pour stocker les données.
if (!$contenu=cache_read('nom du fichier')){ // pas dans le cache on crée (mais sinon, $contenu reçoit les données) cache_start(); // tout le code php etc ici // on sauve le fichier pour une durée de 30 minutes $contenu=cache_end('nom du fichier',30); / } echo $contenu;
Si on a un tableau de données à stocker, c'est sensiblement pareil:
if (!$tableau=cache_read('nom du fichier')){ // pas dans le cache on crée (mais sinon, $contenu reçoit les données) cache_start(); // ici on crée le tableau // on sauve le tableau dans le fichier pour une durée de 30 minutes $tableau=cache_end('nom du fichier',30); } // on utilise les données du tableau
Selon la portion de code qui génère le contenu, le gain peut être considérable.
Ainsi, j'ai utilisé cette méthode sur ce blog et voilà un avant après

Une fois qu'il suffit d’accéder au cache, lors du second accès

Si vous voulez récupérer le fichier c'est par là ;)
Si vous voulez une autre méthode, un peu plus simple pour cacher des pages entières mais ne permettant pas de cacher plusieurs parties de pages séparément, jetez un oeil sur catswhocode ...
Ma version donne la possibilité de stocker par exemple la liste des articles de la catégorie, les derniers tweets, la liste des derniers articles etc séparément et de les réutiliser séparément.
C'est ce que j'ai fait sur ce site: un cache pour le bottom (les 4 colonnes de bas), un pour le menu (sans la partie "connerie") et un pour la liste d'articles de la catégorie.
❝ 2 commentaires ❞
Fil RSS des commentaires de cet article
✍ Écrire un commentaire
les commentaires relevant du SPAM seront filtrés et dégagés direct...