Bonnes pratiques et règles générales
- Pour une efficacité maximale du cache ne jamais mettre de php dans les squelettes. En effet, cela mine l’efficacité du cache puisque cela oblige à lancer l’interpréteur php et à refaire les calculs à chaque page servie.
- À la place d’inclure directement du php dans un squelette, il est la plupart du temps possible de créer une fonction php dans le fichier mes_fonctions.php
et de l’appeler en tant que filtre. Ainsi, le résultat de l’appel est mis en cache.
- Ne jamais utiliser #CACHE{0}
si ce n’est pas absolument nécessaire : en effet, sans le cache, le service des milliers de pages spip demandées par les utilisateurs devient très consommatrice en CPU, puisque dans ce cas, il faut relancer le compilateur SPIP à chaque fois.
- Utiliser les balises #SESSION
ou #AUTORISER
à bon escient car ces balises créent un cache par utilisateur logué (et un autre pour tous les utilisateurs non logués). Le cache pour un utilisateur d’un squelette qui utilise une de ces balises ne bénéficie donc pas à un autre utilisateur et il faut 1) recalculer le cache pour chaque utilisateur (consommation CPU) et 2) stocker d’innombrables versions du cache (consommation d’espace disque).
- Lorsqu’il faut tout de même utiliser ces balises, il faut les utiliser dans des noisettes aussi légères que possible, réduites au minimum, et inclues avec <INCLURE ...>
pour que la noisette inclue ait un cache indépendant de la page qui l’inclut [1]. Cette optimisation est détaillée dans l’article Utilisation de la balise #SESSION et optimisation.
Comme dans toute règle générale, il y a des exceptions, ou des nuances.
Dans ce qui suit, on se sert de 2 commits de cerdic et de la discussion qui s’ensuit pour affiner la description des bonnes pratiques.
Exemple d’exception (avec son exégèse)
- z87378 est commenté "suppression de #CACHE{0}
c’est très mal surtout sur les home page"
- z87379 est commenté « gestion du cache sur les noisettes connexion/deconnexion : utiliser un peu de PHP seul cas qui se justifie, et rétablir le cache »
Dans z87379, le code :
#CACHE{0}
[<div class="bloc" id="inc_sedeconnecter">
<h3 class="titre">(#SESSION{nom})</h3>
...
</div>]
est remplacé par :
<?php
if (isset($GLOBALS['visiteur_session']['nom'])
AND $GLOBALS['visiteur_session']['nom'] )
{ ?>
<div class="bloc" id="inc_sedeconnecter">
<h3 class="titre">
<?=$GLOBALS['visiteur_session']['nom'] ?>
</h3>
...
</div>
<?php } ?>
Explications
1) #SESSION{nom}
créant et devant utiliser un cache spécifique pour chaque personne loguée, le cache perd toute son efficacité à épargner le CPU ;
et par contre, les caches se multiplient et risquent de devenir envahissants.
Ici, de toute façon, il n’y avait pas de cache puisque #CACHE{0}
.
2) Il est préférable de remplacer l’appel à #SESSION
par un appel à PHP pour renvoyer et tester la même chose.
Ce code php est très léger (juste récupérer et tester la valeur d’une variable) comparé à au code très complexe d’une compilation SPIP.
3) Comme ce code compilé est le même pour tout le monde, (logué ou pas, seul le résultat du php changeant selon l’internaute) il est possible de supprimer le #CACHE{0}
et de mettre un #CACHE
normal (par défaut).
Au CPU il est ainsi épargné le gros boulot de compiler le code SPIP pour chaque appel, puisque ce n’est fait qu’une fois pour tous.
Par ailleurs, ailleurs dans ce même commit, le code :
#CACHE{0}
<?php if ($GLOBALS["visiteur_session"]['statut']) { ... }
est remplacé par :
<?php
if (isset($GLOBALS["visiteur_session"]['statut'])
AND $GLOBALS["visiteur_session"]['statut'])
{ ... }
?>
c’est à dire que le #CACHE{0}
est inutile puisque c’est le code php qui est caché et non son résultat
Remarque :
- Le plugin macrosession a pour objectif de rendre ce type de code plus simple à écrire et plus lisible, puisqu’alors on peut l’écrire au moyen des balises #_SESSION_SI
et #_SESSION_FIN
. Et le code devient alors :
#_SESSION_SI{statut}
...
#_SESSION_FIN
- Dans le code php, on peut aussi utiliser session_get
, qui fait exactement l’équivalent. Le code est alors un peu plus lisible :
<?php
include_spip('inc/session');
if (session_get('statut'))
{...}
?>
Quand peut-on donc utiliser du php dans un squelette à la place de #SESSION
?
- Éviter #SESSION
partout où c’est possible car ça génère un cache par utilisateur, ce qui n’est pas génial.
- Il en va de même pour la balise #AUTORISER
, qui tout comme #SESSION
, crée un cache pour chaque visiteur identifié (et un autre identique pour tous les visiteurs non identifiés).
- La balise #SESSION
peut toutefois être nécessaire si il faut utiliser une information de session dans un critère de boucle, et il n’y a pas d’alternative générique.
Et #CACHE{0}
?
Encore pire que les balises #SESSION
ou #AUTORISER
, l’absence totale de cache (#CACHE{0}
) doit toujours être bannie sur un squelette affiché aux visiteurs non connectés.
En effet un squelette affiché aux visiteurs non connecté est souvent visité par un grand nombre de visiteurs, et notamment par des robots goulus : il ne faut pas que leur avidité mette le serveur à genoux.
Pour éviter cette absence de cache dans un squelette exposé au public, il est préférable d’utiliser un petit code php léger comme dans l’exemple donné plus haut.
Vérification : Quand on joue avec les sessions dans les plugins et les squelettes, il faut toujours vérifier que, au final, un curl anonyme sur le site est servi sans aucun « Calcul » et qu’aucun log « Ecriture du cache » ne figure dans spip.log.
En bref, pour les sessions et les inclusions
Pour le choix entre <INCLURE>
(inclusion avec cache indépendant) et #INCLURE
(inclusion dans le cache du contexte d’appel) et pour l’usage de #SESSION
voici une régle de base :
- règle N°1 : utiliser <INCLURE>
partout
- règle N°2 : ne jamais utiliser #INCLURE
ni #MODELE
, surtout lorsque le squelette de l’inclusion est un peu complexe.
- règle N°3 : utiliser #INCLURE
ou #MODELE
uniquement si on a besoin de conditionner l’affichage au moment de l’inclusion et utiliser les parties conditionnelles avant/après de la balise #INCLURE
, ou besoin d’appliquer un filtre au résultat.
- Exemple : pour faire
[Un texte avant (#INCLURE{fond=unenoisette}|unfiltre) un texte après]
car on ne peut pas faire aussi simplement avec un<INCLURE>
. - Mais attention : n’insérez aucun contenu dynamique dans un
#INCLURE
et dans un#MODELE
, donc notamment on ne peut pas y mettre d’appel de formulaire#FORMULAIRE
ou à#SESSION ou #AUTORISER
- règle N°4 : éviter #SESSION
.
- Pour les infos liées à la session, utiliser du PHP est la meilleure solution comme expliqué ci dessus.
-
#SESSION
a un intérêt dans des cas très particuliers : pour ajouter un critère{truc=#SESSION{chose}}
en critère d’une boucle par exemple, car on ne peut pas le faire avec du PHP.
Rq : les <modeles|arg=un arg>
insérés dans le corps d’un article sont #INCLUs
dans le cache de l’appelant (et non <INCLUS>
) et n’ont pas de cache propre. Depuis SPIP3.0, leur code peut contenir l’instruction #CACHE{unedurée}
, ce qui détruit le cache du squelette appelant [2]. Un #CACHE{0}
est donc à éviter !
Reformulation et précisions
Ainsi que Marcimat me reformule, utiliser du php dans le squelette est utile pour réutiliser les mêmes fichiers de cache au lieu de les multiplier.
Utilisation de #SESSION
ou #AUTORISER
#SESSION
, tout comme #AUTORISER
vont créer dans le squelette qui les utilise :
- 1 cache pour les visiteurs non identifiés
- 1 cache par visiteur identifié (ie : si 3 auteurs => 3 caches)
C’est approprié lorsque le résultat du squelette dépend de l’auteur connecté. Par exemple pour des boucles qui utilisent l’id_auteur de la personne connectée, tel qu’un cadre « Mes articles ».
Recours au php
Au lieu des balises #SESSION
, on peut insérer un code php qui récupére et teste $GLOBALS['visiteur_session']
, ou qui utilise include_spip('inc/session'); session_get();
. Avec ce code, le cache de ce squelette n’est pas multiplié : un seul cache est créé par tous les utilisateurs. identifiés comme non identifiés.
C’est mieux pour le cache si le résultat du squelette dépend juste du fait que la personne soit connectée ou non, ou s’il est possible de récupérer toutes les informations utiles simplement dans la globale $GLOBALS['visiteur_session']
. Par exemple $GLOBALS['visiteur_session']['nom']
équivalent à session_get('nom')
.
Ou bien on a recours aux balises #_SESSION, #_SESSION_SI etc définies par le plugin macrosession dont le seul rôle est d’insérer à votre place les codes php utiles dans le squelette SPIP.
Performance pour gros trafic
Plus le site a de trafic, plus il est intéressant d’utiliser le PHP pour ces tests liés à la session plutôt qu’une balise #SESSION.
- En effet, pour un site qui n’a presque pas de trafic, les visiteurs ne tomberont pas souvent sur un cache déjà existant et in fine ça ne fait pas beaucoup de différence qu’il soit sessionné ou pas.
- Pour un site qui a beaucoup de trafic, a contrario il est très bénéfique de partager le cache. Et si en plus une grande partie du trafic est faite par des visiteurs identifiés, ça devient capital pour éviter d’avoir une explosion du cache sessionné.
Les affichages conditionnés au visiteur connecté sont LA seule raison qui justifie l’usage de PHP.
Par exemple il est préférable d’utiliser du PHP lorsque c’est seulement pour tester la présence d’un admin à chaque hit, car le PHP est conservé tel quel dans le cache (qui par contre ne contient plus les boucles et balises, qui ont été remplacées par leur résultat HTML. Cf « SPIP, PHP et javascript sont dans un bateau » ), et au service de la page on a juste un eval()
sur le cache pour évaluer ce PHP de test qui reste. Comme il s’agit de quelques lignes de test par des if()
c’est négligeable en terme de performance - en pratique tous les squelettes en cache provoquent déjà un eval()
dès lors qu’ils ont un <INCLURE>
dedans. Et de cette façon le squelette ne génère qu’un seul cache, partagé par tous les utilisateurs.
Annexe : Calcul du cache et inclusions statiques ou dynamiques
[La durée d’]un cache est déterminé par le squelette lui même, jamais par son mode d’appel : c’est le squelette qui détermine [la durée de validité] de son propre cache, toujours, soit par une balise #CACHE
soit par le nom du dossier (pour les modèles).
1) un modèle (= un squelette dans le dossier /modeles
) n’a pas de cache qui lui soit propre car il est inclu statiquement.
2) Quand une noisette contient un #INCLURE
ou un #MODELE
, c’est une inclusion statique qui est demandée : SPIP calcule le contenu et met le résultat dans le cache courant. Autrement dit, #INCLURE
et #MODELE
stockent le résultat de l’inclusion (le HTML) dans le cache de l’appelant. À la sortie, c’est toujours du HTML, c’est à dire du texte statique. On perd tout le dynamique [NDJL : sauf si il y a du php dans le squelette].
Note : Plus précisément, le résultat du calcul d’une noisette inclue statiquement est stocké à la fois dans un fichier cache propre à la noisette ET dans le cache du squelette incluant (ainsi que dans tous les éventuels autres squelettes de niveau supérieur qui incluent statiquement ce dernier). Un abus de l’emploi des #INCLURE
(statiques) risque donc d’augmenter le volume de cache et il faut éviter d’y avoir recours et ne les employer que quand on a pas le choix, c’est à dire
- en cas d’affichage conditionnel [ avant(#INCLURE)apres]
- ou en cas d’appel d’un filtre [(#INCLURE|filtrer)]
.
3) <INCLURE>
(avec des chevrons <...>
et non #
) déclenche l’inclusion au moment du service de la page (à chaque hit) : c’est une inclusion dynamique. Ça écrit dans le cache « Au moment du service de la page, il faudra aller chercher telle inclusion ».
Ces 3 règles simples peuvent se combiner dans tous les sens. Par exemple, quand on fait l’inclure dynamique d’un modèle, avec <INCLURE{fond=modele/unmodele}>
, comme le modele lui meme n’a pas de cache, il sera calculé à chaque hit.
Aucune discussion
Ajouter un commentaire
Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :
Merci d’avance pour les personnes qui vous aideront !
Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.
Suivre les commentaires : |