Utiliser une variable php dans une boucle d’un squelette

All contributions published for previous SPIP versions

Qui n’a pas déjà voulu utiliser une variable php dans une boucle d’un squelette ?

Par ex, on pourrait vouloir utiliser un paramètre supplémentaire à l’URL de l’article demandé dans un critère de boucle. Supposons qu’on demande la page “http://monsite.org/article.php3?id_article=1&monparam=abc” et que l’on désire utiliser le paramètre monparam dans un critère d’une boucle pour afficher les articles dont le titre commence par une des lettres du paramètre.

On pense naturellement à mettre le code php dans la boucle en le délimitant par les balises habituelles du php. Par ex:

<BOUCLE_articles(ARTICLES) {id_rubrique} {titre==^[<?php echo $monparam; ?>]} {par titre}>
...
</BOUCLE_articles>

Mais ça ne marche pas, car le code php est exécuté après que les boucles et autres balises de SPIP ne soient analysées. Alors qu’il faudrait que le code soit exécuté avant l’analyse des balises (en réalité chaque fois que le fichier cache d’une page est recalculé).

Une manière de contourner cette limitation de SPIP consiste à mémoriser les résultats d’une boucle dans un tableau php que l’on traite ensuite pour n’afficher que les résultats qui nous intéressent. Mais je trouve cette manière de faire peu élégante et assez lourde, car chaque page reprend tous les résultats à traiter. Le plus souvent on peut utiliser la méthode décrite ici pour alléger et simplifier le code du squelette qui sera aussi à mon avis plus lisible et facile à maintenir.

Pour ce qui est du cache, il y a deux types de fichiers qui sont générés/recalculés chacun au besoin puis exécutés:
-  les fichiers correspondant aux squelettes. Ex “CACHE/skel_article.php3”. Ils sont générés quand votre squelette est modifié et exécutés quand le cache de la page est généré.
-  les fichiers correspondant aux articles, rubriques, brèves... Ex “CACHE/c/SPIP-article-13.6d54f5”. Ils sont générés quand l’article est exipré et exécutés chaque fois que la page est demandée.

Comme on le sait, il n’y a aucun problème pour exécuter du code php quand un article est demandé, il suffit de le délimiter par des balises php classiques <?php ... ?>. Le code se retrouve textuellement dans le fichier cache de l’article et est exécuté chaque fois que l’article est demandé.

Mais si on désire utiliser un paramètre personnel de l’URL dans une boucle du squelette, c’est plus problématique. Il faut idéalement exécuter du code php quand le fichier cache du squelette est exécuté, càd quand le cache de l’article est généré. On ne peut pas l’exécuter au moment où le squelette html est compilé (analysé et son cache généré). En effet, le cache du squelette n’est recalculé que si la source html de votre squelette est modifiée (ou bien sûr si le cache n’existe pas ou a été effacé en le vidant dans la partie admin). Mais il faut exécuter notre code chaque fois que le code du squelette en cache est exécuté, càd chaque fois que le fichier cache d’un article est généré, donc quand le cache d’un article est expiré ou que vous forcez à le recalculer.

Voici comment j’ai procédé pour résoudre mon problème. C’est un peu ardu, alors vous pouvez passer ce paragraphe. J’ai essayé d’abord de mettre simplement la variable php dans le critère, dans notre exemple <BOUCLE_articles(ARTICLES) {id_rubrique} {titre==^[$monparam]} {par titre}>
En examinant le cache du squelette “CACHE/skel_...php3”, on voit que $monparam est repris sans modification dans la requête SQL correspondante. C’est parfait car le calcul de la requête SQL va substituer la variable $monparam par son contenu. Malheureusement ce calcul se fait dans une fonction qui n’a pas accès à cette variable globale $monparam. Si on exécute cette page, on aura une erreur de syntaxe SQL car la variable locale $monparam n’est pas définie et donc vide. J’ai alors remplacé dans la boucle du squelette cette variable par $HTTP_GET_VARS[’monparam’] ou $HTTP_GET_VARS[“monparam”]. Mais ça provoque une erreur de syntaxe dans l’analyse du squelette. J’aurais pu essayer d’adapter l’analyseur syntaxique des boucles pour qu’il accepte ces variales, mais je ne voulais pas m’arracher les cheveux avec les expressions régulières (ereg) déjà bien tirées par les cheveux ;-) J’ai donc cherché un autre moyen de récupérer cette variable. Chacune des fonctions du squelette en cache a un paramètre $contexte commun qui est un tableau de variables, chacune de ses variables est définie en local au début des fonctions. Il suffirait alors d’ajouter notre variable à ce tableau $contexte pour que, bingo, elle soit disponible localement dans chaque fonction du squelette. Pour celà, il faut du code qui ajoute le paramètre dans le tableau $contexte au moment où la fonction de la boucle principale est appelée. On ne peut pas encadrer le code de balises php classiques <?php ... ?> car il serait exécuté au moment où l’on ouvre le cache de l’article et non celui du squelette comme on en a besoin. On pourrait alors le délimiter par d’autres balises spécifiques. J’ai opté pour les balises <SPIP_PHP>...</SPIP_PHP> qui respectent une syntaxe bien connue. Ce code ainsi identifié doit être repris dans la fonction de la boucle principale et exécuté au moment où elle est appelée. Pour cela il faut modifier la fonction “calculer_texte()” du fichier “inc-calcul-squel.php3”, pour y extraire le code entre nos balises et l’ajouter au code de la fonction et non le reprendre textuellement en sortie comme calculer_texte() le fait normalement.

Si vous n’avez pas compris mes explications ci dessus, ce n’est pas grave, on en vient enfin à la partie pratique.
Les modifications à apporter à “inc-calcul-squel.php3”:
-  Renommer la fonction “calculer_texte” en “calculer_texte1”
-  Ajouter cette nouvelle fonction “calculer_texte”:

//
// Extraire du texte le code php entre balises <SPIP_PHP> et </SPIP_PHP>
//
// extension par Jean-Christophe Godart
//

function calculer_texte($texte)
{
        $code = "";

        while(($b1 = strpos($texte, '<SPIP_PHP>')) !== false) {
                $b2 = $b1 + strlen('<SPIP_PHP>');
                if(($e1 = strpos($texte, '</SPIP_PHP>', $b2)) === false) {
                        break;        // erreur : pas de balise de fin
                }
                $e2 = $e1 + strlen('</SPIP_PHP>');
                
                $code .= calculer_texte1(substr($texte, 0, $b1));
                $code .= substr($texte, $b2, $e1-$b2);
                $texte = substr($texte, $e2);
        }
        $code .= calculer_texte1($texte);

        return $code;
}

Dans le squelette html, il faut ajouter au tableau $contexte[] les variables utilisées plus loin. Attention à ne pas écraser de variable de spip, sinon c’est la cata. On peut simplement y transférer les paramètres de la page appelée. Par ex dans “article.html”, ajouter un code du genre:

<SPIP_PHP>
global $HTTP_GET_VARS;
$contexte['monparam'] = $HTTP_GET_VARS['monparam'];
</SPIP_PHP>

Il faut ajouter ce code avant que la variable ne soit utilisée par une boucle pour qu’elle y soit disponible. Au besoin, vous pouvez faire des calculs, appels de fonctions ou tout autre code php pour la calculer. Ce qui importe, c’est que la variable se retrouve dans le tableau $contexte avec comme indice son nom, dans notre exemple $contexte[’monparam’]. Elle sera alors normalement disponible dans toutes les boucles qui suivent en tant variable locale, dans notre exemple $monparam.

Pour utiliser la variable dans une boucle de votre squelette html, il suffira de la reprendre comme “$monparam”. Par ex dans “article.html” :

<BOUCLE_articles(ARTICLES) {id_rubrique} {titre==^[$monparam]} {par titre}>
...
</BOUCLE_articles>

Je n’ai essayé cette modification que dans un critère de boucle et cela marche bien pour moi. Je n’ai pas essayé pour d’autres balises SPIP, mais c’est possible que cela marche aussi. Néanmoins, la commande <INCLURE()> de SPIP, est traitée différemment mais on peut tout de même passer le paramètre, encadré d’accolades comme n’importe quel autre : <INCLURE(...){monparam}>

Je me suis basé sur la version 1.6 de SPIP. Il est possible que d’autres versions fonctionnent autrement.

La config php que j’ai utilisé est celle de apinc.org (php 4.3.1), mais le code est très simple et devrait tourner sous d’autres versions de php, y compris php 3.

Cette modification du code n’a pas été testée en profondeur, mais elle ne devrait produire aucun effet désatreux, sauf peut être si votre code php entre balises <SPIP_PHP> est désastreux !

En espérant être utile,

Jean-Christophe Godart

Pour m’écrire : http://bonsaimi.apinc.org/mail.php

updated on 26 November 2003

Discussion

2 discussions

  • 1

    Bonjour,
    la fonction calculer_texte() n’existe pas dans SPIP version 1.8.2, est ce que qlq pourrait m’indiquer comment faire pour patcher cette version de SPIP pour qu’il reconnaisse la balise ?
    merci pour votre aide

    • vincseize

      Merci pour les tutos;

      Néanmoins j’ai une question qui va vous paraitre simple cer
      rien ne passe chez moi (spip 1.9.2d)

      Je souhaite changer de mode de tri dans une boucle:

      if telle URL

      <BOUCLE_messages(FORUMS){id_article}{plat}{ par titre }{pagination #CONFIG{spipbb/fixlimit}}>

      else
      <BOUCLE_messages(FORUMS){id_article}{plat}{ par date_thread }{pagination #CONFIG{spipbb/fixlimit}}>

      J’ai tenté de recupérer les variables en php, pour les utiliser à la place de par titre ou par date_thread

      ou de carrement reécrire la ligne p>

      Quelqu un pourrait il m’expliquer l’astuce pour une utilisation de Boucle Conditionnelles sous Spip, le bon vieux, IF ELSE !?

      Merci d’avance

    Reply to this message

  • 4
    Philippe Simonin

    Tout d’abord un grand merci pour ta contrib. (Je t’ai envoyé un message à l’adresse que tu donnes ci-dessus, mais je ne suis pas sûr qu’il ait été pris en compte, j’ai été redirigé après validation. Je me permets donc de le recopier ici)
    Je me heurte à un problème :
    Mes articles sont en fait des fiches produits. Je souhaite lier ces fiches. J’utilise donc le champ #NOM_SITE que je détourne, puisqu’il ne me sert à rien en tant que tel (pas de site à lier). J’y stocke les numéros des articles liés séparés par |.
    Dans mon squelette, grâce à ta contrib, j’écris :

    <BOUCLE_art(ARTICLES){id_rubrique}>
    <SPIP_PHP>
    $contexte['indisp']=#NOM_SITE;
    </SPIP_PHP>
    <BOUCLE_art_dep(ARTICLES){id_article==^[$indisp]}>
    #TITRE<br>#CHAPO<br>
    </BOUCLE_art_dep>
    </BOUCLE_art>

    Et là, patatrac ! J’obtiens :
    $contexte[’indisp’]=2;
    et une erreur mySQL sur la boucle art_dep :
    Got error ’brackets ([ ]) not balanced’ from regexp

    Par contre, si je remplace #NOM_SPIP par 2|6 par exemple, ça marche.

    Si tu as un peu de temps à m’accorder et une bonne idée là-dessus, ça m’aiderait !

    Merci d’avance,

    Philippe

    • Est-ce que tu as trouvé la solution, car j’ai le même problème ?

      merci

    • Je suis passé sous 1.8 depuis. Et elle introduit une nouvelle balise #ENV dont je me sers. J’ai fait une contrib - en évaluation pour le moment - ici : http://www.spip-contrib.net/ecrire/articles.php3?id_article=875

    • Salut, j’ai le même probleme que toi as tu trouvé un solution qui marche
      avec la version 1.7.2????
      Merci de bien vouloir me répondre.

    • Le balises

      #ENV

      ne permettent pas d’affecter la valeur d’une variable PHP (

      #SETENV{var, $var_php}

      ne marche pas)
      Par contre, il y a un moyen de récupérer facilement la valeur d’une variable PHP dans SPIP: en utilisant les filtres

      Ex:

      // Pour renvoyer la valeur de $var_php
      function mon_filtre($dummy) {
        return $var_php;
      }


      Utilisation dans une boucle: par ex, pour récupérer les

      $var_php

      1ers articles d’une rubrique:

      <BOUCLE_rubrique(RUBRIQUES) {id_rubrique}>
      
      <BOUCLE_articles(ARTICLES) {0, #SELF|mon_filtre}>
      ...
      </BOUCLE_articles>
      
      </BOUCLE_rubrique>

      Dans cet exemple, on peut remplacer

      #SELF

      par quasiment n’importe quoi (puisqu’on ignore

      $dummy

      dans le code du filtre)

    Reply to this message

Comment on this article

Who are you?
  • [Log in]

To show your avatar with your message, register it first on gravatar.com (free et painless) and don’t forget to indicate your Email addresse here.

Enter your comment here

This form accepts SPIP shortcuts {{bold}} {italic} -*list [text->url] <quote> <code> and HTML code <q> <del> <ins>. To create paragraphs, just leave empty lines.

Add a document

Follow the comments: RSS 2.0 | Atom