SPIP-Contrib

SPIP-Contrib

عربي | Deutsch | English | Español | français | italiano | Nederlands

286 Plugins, 197 contribs sur SPIP-Zone, 220 visiteurs en ce moment

Accueil > Optimisation et performances > Du php dans le squelette à la place de #SESSION ou #CACHE0

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

7 février 2015 – par JLuc – 15 commentaires

20 votes

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 minerait l’efficacité du cache puisqu’il faudrait alors lancer l’interpréteur php à 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 balise 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).
Une recommandation est donc, lorsqu’il faut utiliser ces balises, de les utiliser dans des petites noisettes, réduites au minimum, et inclues avec <INCLURE ...> pour que la noisette inclue ait un cache indépendant de la page qui l’inclut (et non pas avec #INCLURE car alors la noisette inclue n’a pas de cache propre).

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

-  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 :

  1. #CACHE{0}
  2. [<div class="bloc" id="inc_sedeconnecter">
  3. <h3 class="titre">(#SESSION{nom})</h3>
  4. ...
  5. </div>]

Télécharger

est remplacé par :

  1. <?php
  2. if (isset($GLOBALS['visiteur_session']['nom'])
  3. AND $GLOBALS['visiteur_session']['nom'] )
  4. { ?>
  5. <div class="bloc" id="inc_sedeconnecter">
  6. <h3 class="titre">
  7. <?=$GLOBALS['visiteur_session']['nom'] ?>
  8. </h3>
  9. ...
  10. </div>
  11. <?php } ?>

Télécharger

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 :

  1. #CACHE{0}
  2. <?php if ($GLOBALS["visiteur_session"]['statut']) { ... }

Télécharger

est remplacé par :

  1. <?php
  2. if (isset($GLOBALS["visiteur_session"]['statut'])
  3. AND $GLOBALS["visiteur_session"]['statut'])
  4. { ... }
  5. ?>

Télécharger

c’est à dire que le #CACHE{0} est inutile puisque c’est le code php qui est caché et non son résultat

Remarque :
Dans le code php, on peut aussi utiliser session_get, qui fait exactement l’équivalent. Le code est alors un peu plus lisible :

  1. <?php
  2. if (session_get('statut'))
  3. {...}
  4. ?>

Télécharger

Mais cela suppose d’avoir inclu inc/session.php dans le fichier d’option : include_spip('inc/session');, afin que la fonction soit toujours définie.

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.

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.

-  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).

Et #CACHE{0} ?

Encore pire que les balises #SESSION ou #AUTORISER, l’absence totale de cache (#CACHE{0}) doit toujours être bannie sur tout 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 ci dessus.

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 pour limiter le risque de mettre le serveur à genoux, sans trop se poser trop de questions :

-  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. 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').

Performance pour gros traffics

Plus le site a de traffic, 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 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é.
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 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

Le cache est déterminé par le squelette lui même, jamais par son mode d’appel : c’est le squelette qui détermine 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 [NDJL : sauf depuis SPIP3 si on met #CACHE{3600} dedans...)

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].

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.

Dernière modification de cette page le 29 novembre 2016

Retour en haut de la page

Vos commentaires

  • Le 14 septembre à 20:55, par YannX En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

    pour info : précisions de Cedric (citations sur la liste spip-zone le 13/09/2016)

    Je dirais que plus le site a de traffic, plus il est important/intéressant d’utiliser le PHP pour ces tests liés à la session plutôt qu’une balise #SESSION.

    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 :-)

    • Le 29 novembre à 12:57, par JLuc En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

      J’ai intégré ces explications dans l’article dans une partie « performances pour gros traffics »,
      ainsi que les éléments d’explications apportées par marcimat aujourd’hui sur spip-zone.

      Dans la citation de cerdic il y a « de toute façon il y a eval() du cache dés lors qu’il y a un dedans » mais on sait pas c’est quoi le « un ». C’est « formulaire » ?

    Répondre à ce message

  • Le 9 février 2015 à 14:45, par Teddy Payet En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

    Hello,

    Et si on a le plugin SPIP Bonux, on peut faire ça :

    <BOUCLE_visiteur_session (CONDITION) {si #EVAL{$GLOBALS\['visiteur_session'\]\['nom'\]}|oui}>
    <div class="bloc" id="inc_identification_sedeconnecter">
    <h3 class="titre">[(#EVAL{$GLOBALS\['visiteur_session'\]\['nom'\]})]</h3>
    ...
    </div>
    </BOUCLE_visiteur_session>

     :-D

    • Le 14 février 2015 à 13:30, par Maïeul En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

      #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...

    • Le 3 septembre 2015 à 18:52, par Beno En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

      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 !

    • Le 14 septembre à 18:17, par JLuc En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

      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.

    • Le 14 septembre à 18:52, par JLuc En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

      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

    • Le 14 septembre à 19:04, par Teddy Payet En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

      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é) :

      <BOUCLE_articles(ARTICLES){si #VAL{'nom'}|session_get}>
         #ID_ARTICLE <br />
      </BOUCLE_articles>

      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.

    • Le 14 septembre à 19:34, par Matthieu Marcillaud En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

      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.

    • Le 14 septembre à 19:59, par Matthieu Marcillaud En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

      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

  • Le 14 septembre à 17:51, par JLuc En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

    J’ai complété l’article avec la possibilité d’utiliser la fonction session_get dans le php, à la place des pénibles if (isset($GLOBALS["visiteur_session"]['statut']) AND $GLOBALS["visiteur_session"]['statut']) comme le fait remarquer marcimat sur irc

    Mais 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

  • Le 27 juillet 2015 à 17:28, par livier En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

    Bonjour,

    Je comprend bien que l’utilisation d’éléments comme

    1. #SESSION{nom}

    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 :

    1. [(#SESSION{id_auteur}|oui)
    2. [(#PS)]
    3. ]

    Télécharger

    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

  • Le 13 février 2015 à 23:44, par Minga En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

    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 ?

    Répondre à ce message

  • Le 14 février 2015 à 11:40, par peetdu En réponse à : Du php dans le squelette à la place de #SESSION ou #CACHE{0}

    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

Répondre à cet article

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 Les choses à faire avant de poser une question (Prolégomènes aux rapports de bugs. )
Ajouter un document

Retour en haut de la page

Ça discute par ici

  • Import ICS 2 (agenda distant)

    2 août – 36 commentaires

    La version 2 du plugin « import ICS » en reprend la principale fonctionnalité, à savoir l’ajout automatique d’évènements distants dans la liste des évènements d’un site. À la différence de la première version, elle ne dépend pas du plugin « Séminaire » et est (...)

  • GIS 4

    11 août 2012 – 1286 commentaires

    Présentation et nouveautés La version 4 de GIS abandonne la libraire Mapstraction au profit de Leaflet. Cette librairie permet de s’affranchir des librairies propriétaires tout en gardant les mêmes fonctionnalités, elle propose même de nouvelles (...)

  • Médiathèque

    20 novembre 2008 – 292 commentaires

    Un plugin pour gérer tous vos documents de façon centralisée.

  • Metas +

    3 décembre – commentaires

    Améliorez l’indexation de vos articles dans les moteurs et leur affichage sur les réseaux sociaux grâce aux métadonnées Dublin Core, Open Graph et Twitter Card. Installation Activer le plugin dans le menu dédié. Dans le panel de configuration, (...)

  • Newsletters

    16 janvier 2013 – 374 commentaires

    Ce plugin permet de composer des Info-lettres. Par info-lettre, on désigne ici le contenu éditorial qui va être composé et envoyé par courriel à une liste d’inscrits. Le plugin permet de composer une info-lettre à partir d’un modèle pré-composé, (...)

Ça spipe par là