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.
Discussions par date d’activité
7 discussions
Bonjour,
j’aimerais intégrer à SPIP un compteur de fan fb.
ce code fonctionne sur un site classique, pas sur spip. Comment faire ?
Répondre à ce message
pour info : précisions de Cedric (citations sur la liste spip-zone le 13/09/2016)
Pour un site qui n’a presque pas de traffic, les visiteurs ne tomberont presque jamais sur un cache et in fine ça ne fait pas beaucoup de différence qu’il soit sessionné ou pas.
Pour un site qui a beaucoup de traffic, a contrario il est très bénéfique de partager le cache. Et si en plus une grande partie du traffic est faite par des visiteurs identifiés, ça devient capital pour éviter d’avoir une explosion du cache sessionné.
>
>> > Je rappelle encore une fois que les affichages conditionnés au visiteur
>> > connecté sont LA seule raison qui justifie l’usage de PHP, et c’est même
>> > une bonne pratique dans ce cas.
>
> D’accord, mais je me demande toujours si l’appel du PHP,
> qui sert à tester la présence d’un admin à chaque hit, reste sans
> répercussion sur le comportement et la performance du cache.
>
Justement, l’intérêt d’utiliser du PHP dans le squelette pour faire ça, c’est que le PHP est conservé tel quel dans le cache (qui par contre contient toutes les boucles et balises remplacées par leur résultat), 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 dedans.
Et de cette façon le squelette ne génère qu’un seul cache, partagé par tous les utilisateurs.
Cédric
> Salut Cédric et merci pour ton explication :-)
J’ai intégré ces explications dans l’article dans une partie « performances pour gros traffics », ainsi que d’autres éléments d’explications apportés par marcimat aujourd’hui sur spip-zone.
Répondre à ce message
Hello,
Et si on a le plugin SPIP Bonux, on peut faire ça :
:-D
#EVAL n’est pas propre a bonus me semble-t-il ? je ne suis pas sûr que du coup on y gagne en terme de perf...
Je ne sais pas vous, mais moi, je dis merci Teddy !
Ta proposition remplit exactement la fonction que je cherchais, l’accès automatique à une partie du site protégée, que l’on veut un tant soit peu limité à des visiteurs « enregistrés ».
j’ai ajouté les plugins « Inscriptions 3 » et « Champs Extra » pour personnaliser le formulaire d’entrée des visiteurs, le tour était joué.
Mériterait de figurer dans le tuto « Accès Restreint », dont c’est justement la faiblesse (les visiteurs doivent manifestement obligatoirement être validés manuellement pour avoir accès aux parties protégées puisque Jaz ne semble pas fonctionner... je viens d’y passer des heures...).
Bref, encore merci Teddy !
Hello Teddy, (suite à discussion sur irc) as tu testé ce que tu proposes et confirmes tu que ça marche ? Car la balise #EVAL met en cache le résultat de son premier eval normalement, et le SI est évalué une seule fois avant l’exécution de la boucle.
Oui après test, je confirme que ça ne
#EVAL{get_session}
ne dépend pas de la session... que ce soit dans ou en dehors d’un critère de boucle, car la valeur est mise dans le cache.Le squelette de test est dans le carnet wiki
Bonjour,
@maieul, en effet, #EVAL ne fait pas parti de SPIP Bonux. Mais la boucle ’CONDITION’ est bien offerte par SPIP Bonux.
@JLuc, Euh… oui ça marche chez moi. Remarques bien la différence par rapport à ton code, c’est qu’il y a un échappement des crochets et accolades.
Si tu désires utiliser session_get, tu peux l’utiliser en filtre je crois (là, pas testé) :
Car en principe, toutes fonctions PHP (ou presque) de SPIP peuvent être appliqués en filtre. Mais là, je prends des pincettes sur ce dernier bout de code.
Je t’assure Teddy que le coup du
{si session…}
en critère ne fonctionne pas, quelque soit la méthode que tu veux utiliser.Donc si ça marche chez toi c’est que soit ton squelette n’a pas de cache (ou ton SPIP), ou que tu passes un paramètre différent à ce squelette pour chaque visiteur, ou que tu utilises
#SESSION
, ou#AUTORISER
ou quelque balise ou boucle ou critère qui demande un cache sessionné … avec le plugin Accès Restreint peut être.Le truc c’est que le critère SI écrit le résultat de l’expression pour la transmettre ensuite à l’itérateur. Il écrit le résultat, donc au moment du calcul du squelette. Si le squelette n’est pas recalculé, le résultat ne changera pas.
Il faudrait peut être un critère
{si_eval EXP}
qui récupérerait l’expression, et l’évaluerait au moment du passage dans l’itérateur. Ça nécessiterait de modifier public/iterateur.php après le if de la ligne 27 donc.En fait même un
{si_eval EXP}
ne fonctionnerait pas. On est obligé de faire calculer la boucle, et de mettre le PHP de test, ouvrant et fermant autour de la boucle. Autant faire une inclusion du coup.Répondre à ce message
J’ai complété l’article avec la possibilité d’utiliser la fonction
session_get
dans le php, à la place des péniblesif (isset($GLOBALS["visiteur_session"]['statut']) AND $GLOBALS["visiteur_session"]['statut'])
comme le fait remarquer marcimat sur ircMais cela suppose d’avoir inclu le fichier ecrire/inc/session.php dans le mes_options ou dans les options du plugin :
include_spip ('inc/session');
Répondre à ce message
Bonjour,
Je comprend bien que l’utilisation d’éléments comme
oblige le cache du squelette qui le contient à se multiplier pour disposer d’une version correspondant à chaque auteur qui se connecte.
Qu’en est il si on veut seulement contrôler l’affichage d’une partie de squelette en fonction du statut de connexion du visiteur, comme par exemple :
Une seule version du chache serait pertinente pour tous les utilisateurs connectés, puisqu’on leur sert la même chose, mais est ce que celà fonctionne comme celà ?
Répondre à ce message
J'ai un doute sur le dernier paragraphe de l'article, à propos de #CACHE{0} : lorsque l'on utilise pas la session, n'est-il pas souhaitable de mettre des #CACHE{0} dans toutes les noisettes incluses (SAUF celles incluses EN AJAX ou dépendantes de la session) et de mettre un cache non nul uniquement pour la page ? Bref, enlever #CACHE{0} sur des noisettes incluses ne revient-il pas à mettre en cache 2 fois le même contenu, une fois en mettant en cache la noisette et une fois en mettant en cache la page qui la contient ?
non, pas du tout. Si vous faites un
<INCLURE>
le cache de la page incluse est indépendant du cache de la page incluante. Si vous faite un#INCLURE
le#CACHE
de la page incluse n’est pas prise en compte.Voir http://geekographie.maieul.net/Les-differents-modes-d-inclusion
Répondre à ce message
Est-ce que la balise #SESSION_SET fait également parti des balises demandant à créer un cache différent par session ?
Répondre à ce message
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 : |