janvier
2011
Apparu avec le Zend Framework 1.10, le backend de cache Static (Zend_Cache_Backend_Static) est un cache particulièrement agressif, qui permet d’accéder à des pages de votre application comme si elles étaient de simples fichiers HTML statiques : aucun fichier PHP n’est appelé, et le gain de rapidité peut être vraiment énorme sur des pages lourdes à générer.
Bien entendu, cela n’est pas sans inconvénients :
- L’application Zend Framework n’étant plus du tout sollicitée, la validité du cache n’est pas testée avant affichage, ce qui signifie que le cache doit être vidé via une autre partie de l’application (cron job, vidage du cache lors de la modification du contenu…).
- Les pages très dynamiques peuvent être difficiles à mettre en cache, surtout si des fonctionnalités Ajax sont présentes (activer ou non le cache en fonction du contexte, ne pas servir le cache pour les requêtes POST…).
- Les fichiers de cache doivent être accessibles dans le docroot, ce qui rends délicat la mise en cache de contenu à accès restreint.
Pour ne rien arranger, la documentation à ce sujet est un peu légère : je n’ai par exemple pas trouvé de cas concret de mise en place, et c’est ce que je vais tenter de faire maintenant
Avant d’aller plus loin je tiens a donner un petit avertissement : ce billet tient plus d’une compilation d’informations au sujet du cache Static que d’un véritable tutoriel. Les informations ont été testées tant que possible, mais n’ont pas bénéficiées de multiples relectures ou de tests approfondis. L’intégralité du code a été testé avec le Zend Framework 1.11.1, et PHP 5.3.3.
Le backend de cache Static est prévu pour fonctionner conjointement avec le frontend Capture. Il utilise également un autre cache pour stocker des métadonnées sur les pages mises en cache, typiquement un cache Core avec un backend File.
Première chose, on va créer un projet vierge :
zf create project static-cache
Ensuite on va créer une action qui sera mise en cache :
zf create action Cached Index
Ensuite on va éditer le fichier de configuration pour désactiver le buffer de sortie du FrontController : c’est un pré-requis indispensable pour que le cache Static fonctionne.
resources.frontController.params.disableOutputBuffering = 1
Accessoirement vous pouvez préférer le faire directement dans le code :
$front->setParam('disableOutputBuffering', true);
Il nous faut également quelques directives Apache, afin d’indiquer au serveur de servir les pages mises en cache en priorité si elles existent. On part du principe que les pages mises en cache sont seront situées dans le dossier cached du docroot. Voici le contenu de mon .htaccess, vous pouvez aussi vous inspirer de celui de la documentation du backend static pour définir le votre :
RewriteCond %{DOCUMENT_ROOT}/cached/index.html -f
RewriteRule ^/?$ cached/index.html [L]
RewriteCond %{DOCUMENT_ROOT}/cached/%{REQUEST_URI}.html -f
RewriteRule .* %{DOCUMENT_ROOT}/cached/%{REQUEST_URI}.html [L]
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
Vous noterez que ce .htaccess ne gère que les pages d’extension html et, si il suffit pour notre exemple, il se montrera très insuffisant pour une véritable application. On remarquera aussi que le cache de la racine du site est géré alors que l’action index ne sera pas mise en cache dans notre exemple : en prévoyant le coup dés le début, on s’assure de la possibilité de le faire plus tard au besoin.
Maintenant que les bases sont posées, on va mettre un cache static sur l’action cached du contrôleur index. On va le faire de deux façons différentes : « from scratch » d’abord, pour mieux cerner le fonctionnement du frontend Capture, et ensuite en utilisant le Zend_Cache_Manager et l’action helper Cache.
Pour activer le cache nous allons remplir la méthode init de notre contrôleur comme suit :
{
$tagCache = Zend_Cache::factory('Core', 'File', array('automatic_serialization' => true), array('cache_dir' => APPLICATION_PATH . '/../data/cache'));
$cache = Zend_Cache::factory('Capture', 'Static', array(), array('public_dir' => APPLICATION_PATH . '/../public/cached',
'tag_cache' => $tagCache));
$actions = array('cached');
if (in_array($this->_request->getActionName(), $actions)) {
$id = bin2hex($this->_request->getRequestUri());
$cache->start($id, array());
}
}
On instancie d’abord un cache Core avec un backend File pour stocker les métadonnées des pages de cache. Dans notre cas j’ai choisis de stocker le cache dans un dossier /data/cache/. A noter également qu’il est nécessaire pour le bon fonctionnement du cache d’activer le paramètre automatic_serialization.
Une fois que nous avons notre tagCache, nous allons instancier le cache Static : le frontend ne prends aucun paramètre particulier, tandis que le backend attends au moins deux paramètres : le dossier dans lequel stocker les fichiers de cache qui doit être publique (d’où le nom du paramètre : public_dir), ainsi que le cache Core pour les métadonnées (tag_cache).
Ensuite on s’assure de lancer le cache uniquement sur les actions désirées : dans notre cas uniquement l’action cached. On peut ainsi facilement mettre en cache d’autres actions en remplaçant la variable $actions :
$actions = array('index', 'cached');
La méthode start attends 3 paramètres dont 1 optionnel : l’id unique du cache, un tableau de tags à lui associer (pour faciliter la maintenance plus tard), et éventuellement une extension a donner au fichier de cache (par défaut ce sera .html). L’id unique est généré en récupérant l’équivalent hexadécimal de l’uri courante, je me suis sur ce point inspiré du code de l’action helper Cache (voir plus bas).
Vous pouvez lancer l’application et vérifier : l’appel de l’url /index/cached va générer le fichier html /public/cached/index/cached.html
Si le serveur est bien configuré, c’est ce fichier qui sera servit aux visiteurs, et non plus l’application.
Bien, maintenant on a vu comment mettre en place le cache Static « from scratch », il est donc temps de voir comment on peut aller beaucoup plus vite…
Car le Zend Framework offre deux aides précieuses :
* Le Cache Manager, qui permet de configurer vos caches depuis un fichier de configuration et de les récupérer facilement n’importe où dans votre application.
* L’Action Helper Cache dont le rôle semble dédié au cache Static. Je dis bien semble car en l’absence de documentation et / ou de commentaires explicites, c’est l’interprétation que je fais de son code source.
Le Cache Manager préconfigure un cache Static et son cache de métadonnées sous les clés ‘page’ (Zend_Cache_Manager::PAGECACHE
) et ‘pagetag’ (Zend_Cache_Manager:PAGETAGCACHE
).
Nous allons donc configurer ces caches depuis notre application.ini :
resources.cachemanager.pagetag.backend.options.cache_dir = APPLICATION_PATH "/../data/cache"
Et maintenant, nous allons remplacer le code de la méthode init pour utiliser l’action helper Cache :
{
$this->_helper->cache(array('cached'));
}
L’helper Cache accepte deux autres arguments optionnels : un tableau de tags, et une extension.
Comme vous pouvez le constater, au final le déploiement de ce cache se fait très simplement, mais une recherche préalable est nécessaire pour bien appréhender les différents éléments qui entrent en jeu. J’espère que ce billet aidera quelques développeurs à s’y retrouver
Si vous avez des questions, ou des remarques, ne vous privez pas !