Utiliser une variable php dans une boucle d’un squelette

Ceci est une ARCHIVE, peut-être périmée. Vérifiez bien les compatibilités !

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

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 <BOUCLE... en php, mais rien y fait !!!

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

      Merci d’avance

    Répondre à ce 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)

    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 :

  • 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