Du php dans le squelette à la place de #SESSION ou #CACHE 0

Ceci est une « contribution pédagogique », qui montre par l’exemple comment développer une nouvelle fonctionnalité pour SPIP.

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.

Notes

[1et non pas avec #INCLURE car alors la noisette inclue n’a pas de cache propre

[2à confirmer

Liens utiles


-  Plugin macrosession : fournit des macros PHP à utiliser dans les squelettes SPIP, pour simplifier l’application des recommandations faites dans cet article.
-  Plugin xray : permet de passer le cache aux rayons X, et d’étudier en détail comment SPIP gère les caches, sessionnés ou non, de votre site.
-  Optimisation des inclusions sessionnées : Utilisation de la balise #SESSION et optimisation

Discussion

Aucune discussion

Ajouter un commentaire

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparaît.

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.

Qui êtes-vous ?
[Se connecter]

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Ajouter un document

Suivre les commentaires : RSS 2.0 | Atom