Integrez un systeme de cache performant dans vos contenus dynamiques

Description

Je vais vous présenter ici comment intégrer mon systéme de cache dans vos codes ceci afin de diminuer considérablement le temps d'execution de vos scripts PHP.

Avant de commencer je voudrais mettre au point quelques notions :

1 - Le cache server != cache utilisateur :
Même si le cache à pour but d'augmenter la rapidité d'affichage, il ne faut pas confondre ces deux types de caches car chacun agit différement.
En effet, le cache utilisateur (comme le cache d'internet explorer par exemple) augmente la vitesse de communication en diminuant le transit d'informations. C'est pour cela qu'il pré-enregistre les images et toutes les ressources définies dans les pages, afin qu'il ne les actualise pas lors d'un second accés.
Le cache serveur lui va augmenter cette vitesse en envoyant plus rapidement les pages demandées.
On peut donc constater que les deux caches agissent sur des parties différentes :
Serveur : Composition rapide des ressources HTML
Client : Stockage des ressources externes au fichier HTML

2 - Les cache VS dynamisme :
Le cache server va à l'encontre des sites dynamiques. En effet, une page mise en cache et ressortie périodiquement ne semble pas être dynamique. Il faut donc évaluer le taux de dynamicité des pages mises en cache.

Vous avez par exemple un site qui gére des offres d'emploi :

--> La page d'accueil contient des news : mises à jour 1 fois par semaine
--> La page listant les offres : mises à jour plusieurs fois par jour

Vous avez donc deux méthodes pour mettre à jour le cache :

1 - Périodiquement pour les systémes à haute dynamicité
2 - Aprés une mise à jour pour les systémes à faible dynamicité

Je ne vais pas aller jusque là dans mon tutorial, je vous expliquerais seulement la methode de stockage et de ré-utilisation afin de faire un systéme de cache.

Je vais donc tout d'abord commencer ce tutorial par l'explication des différentes fonctions que j'utiliserais par la suite en exemple de code.

1) Les fonctions utilisées :
----------------------------

Pour avoir un systéme de cache, il faut avoir la source de la page. A premiére vue cela simple difficilement envisageable car du moment où on fait un echo "toto", apache l'envoie directement au client.
Il existe cependant des fonctions permettant de gérer le flux d'informations qui sont envoyées au clients :
  • ob_start(callback[string]) :

Cette fonction va vous permettre d'intercepter le flux d'informations qui vont être envoyées à votre client. Elle va seulement laisser passer les entêtes et le contenu de la page sera envoyé que seulement quand vous le déciderez. Pour cela il faut indiquer dans le paramétre callback le nom de la fonction qui va intercepter le flux d'informations.

Exemple :

test.php
<?
function CallBackExemple($buffer) {
$buffer = '-->Debut'.$buffer.'<--Fin';
return $buffer;
}

ob_start('CallBackExemple');

for ($i=0; $i<100; $i++) echo 'ligne '.$i.'<br>';
header('Author: C. CHIRIAC');
?>

Cet exemple vous montre 3 choses :
1 - Pour lancer la 'bufferisation de sortie' vous utilisez la fonction ob_start en lui passant le nom de la fonction en argument
2 - La fonction de bufferisation recoit en argument une chaine contenant le code source de la page a envoyer. Vous pouvez modifier la source de la page, il transmettra au client le résultat de cette fonction seulement.
3 - Une fois la bufferisation activée vous pouvez envoyer des headers/cookies/lancer des sessions même si vous avez fait auparavant des print/echo

Pour plus de détails vous pouvez consulter la documentation technique de PHP à cette adresse :
http://fr3.php.net/manual/fr/function.ob-start.php

Je vous présenterais maintenant une seconde fonction interessante, celle vous permettant de gérer le moment ou vous souhaitez lancer la sortie de la page.
  • ob_end_flush() : Envoie les données du buffer de sortie et éteint la bufferisation de sortie.


Exemple : (pris dans la doc)

function change_title($new_title) {
$output = ob_get_contents();
ob_end_clean();

$output = preg_replace("/<title>(.*?)<\/title>/", "<title>$new_title</title>", $output);
echo $output;
}

Example:
<?
ob_start();
?>
<html>
<head>
<title>Mauvais titre ...</title>
</head>
<body>
Hello World
</body>
</html>
<?
// ... some output
change_title('NEW TITLE!');
?>
Pour plus de détails vous pouvez consulter la documentation technique de PHP à cette adresse :
http://fr3.php.net/manual/fr/function.ob-end-flush.php

/!\ BON A SAVOIR /!\
Le contenu de ce buffer peut être copié dans une chaîne avec la fonction ob_get_contents().
http://fr3.php.net/manual/fr/function.ob-get-contents.php

On va maintenant pouvoir rentrer dans le vif du sujet car si vous avez lu attentivement ce que j'ai expliqué au-dessus on connais tout ce qui est nécéssaire pour rédiger en PHP notre systéme de cache.

2) Le principe :
----------------

Le principe est simple :
On va actualiser périodiquement le cache, donc à chaque chargement de la page PHP on vérifie si le cache est obsolette, si c'est le cas on le met à jour, sinon en lui envoye le cache sans executer les fonctions de la page.

Organisation sous forme algorithmique :

1 - On vérifie le cache
-> Si le cache est à jour on l'envoye et on arréte l'execution
-> Sinon on continue

2 - Execution de vos scripts personnels, et rédaction de votre page html ...

3 - A la fin, on passe par la fonction de bufferisation qui met à jour le cache avec le contenu actuel de la page

En gros, c'est trés simple ...

Je vais donc vous proposer mon code ci-dessous :

Source / Exemple :


fichier cache.req.php
<?
// FONCTIONS DE GESTION DU CACHE
// --------------------------------------
// * Date de création : 18-Aou-2005 12:00 PM - C. CHIRIAC
// Historique des modifications :
// --------------------------------------

function StartCache() {
	$GLOBALS['timeinit'] = microtime();
	
	$cached_file = 'cache/'.md5($_SERVER['REQUEST_URI']);
	$interval = time() - @filemtime($cached_file);
	if (file_exists($cached_file) && ($interval < __CACHETIME__)) {
		// ON ENVOI LE FICHIER DU CACHE
		echo '<!-- STORED FROM CACHE -->';
		readfile($cached_file);
		
		$end = microtime();
		$total = (integer)(($end - $GLOBALS['timeinit']) * 1000);
		if ($total<0) $total = 0;

		echo '<!-- EXECUTION TIME : '.$total.' -->';
		die();
	} else {
		// ON LANCE LE SYSTEME DE CACHE
		ob_start("CacheStream");
	}
}
function EndCache() {
	ob_end_flush();
	clearstatcache();	
}
function CacheStream($buffer) {
	if (isset($GLOBALS['set_to_cache'])) {
		// ENREGISTRE LE FICHIER DANS LE CACHE
		$cached_file = 'cache/'.md5($_SERVER['REQUEST_URI']);
		$f = fopen($cached_file, 'w+');
		fputs($f, $buffer);
		fclose($f);
	}
	// return fatal_error_handler($buffer); BUG TRACKER ;p
	return $buffer;
}
?>

fichier index.php (a appeller)
<?
	define('__CACHETIME__', 600); // en secondes 600 = 10 min
	require_once('cache.req.php');
	StartCache();
        if (!isset($_GET['page'])) $_GET['page'] = 'accueil';
	include($_GET['page'].'.php');
	EndCache();
?>

fichiers d'exemple à mettre en cache :
accueil.php
<?
	$GLOBALS['set_to_cache'] = true;
?>
Bienvenue sur la page d'accueil
<br>
<a href="index.php?page=news">News</a>

news.php
<?
	$GLOBALS['set_to_cache'] = true;
?>
Bienvenue sur la page de news
<br>
<a href="index.php">Accueil</a>

/*
Explications finales :

Sur les deux pages de test vous pourrez constater que je demande au systéme de les mettre en cache
avec la commande qui suit :
$GLOBALS['set_to_cache'] = true;

Le systéme enregistre les fichiers dans le cache selon les noms des fichiers, et selon les variables GET envoyées.
Si ces pages sont différenciées par des requettes sous la forme de formulaires POST (formulaire de recherche dans la base
par exemple) il ne faut pas les mettre en cache.

Tous les critéres différenciateur qui ne sont pas à prendre en compte pour le contenu de la page sont :
- Les variables POST
- Les cookies
- les sessions
(car elles sont uniques à chaque client qui navigue le site donc le dynamisme doit être total)

Vous pouvez retrouver ces codes dans le zip joint à ce tutorial

  • /

Conclusion :


Le systéme permettant la gestion du cache est vraiment basique, et permet seulement une mise à jour temporelle. Si vous souhaitez rafraichir par exemple la page de news à partir d'une partie administration, juste aprés la création d'une news dans la base vous pouvez écrire ceci aprés votre requettage de création de l'article dans la base :

unlink(md5('cache/index.php?page=news'));

Cette methode de mise à jour du cache aprés la mise à jour dans la base est la plus optimisée, il faudra alors enlever la gestion du temps.

Bonne prog à tous, akh

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.