Il arrive parfois que les possibilités offertes par les balises dynamiques ne permettent pas de pallier un petit défaut. C’est le cas lorsque vous avez besoin d’accéder à des détails de l’AST -peut-être le nom du fichier de squelette, peut-être quelque chose d’autre- pour générer votre contenu.
Dans cet article je vais décrire l’évidente (mais mauvaise) méthode de transmission des valeurs dans une balise dynamique et une autre technique qui, elle, fonctionne réellement.
Contrairement aux balises statiques -qui sont mises en œuvre par une seule fonction- les balises dynamiques sont composées d’un ensemble de trois fonctions appelées, si nécessaire, par le moteur de SPIP.
La première de ces trois fonctions (identique à la fonction unique des balises statiques) est appelée balise_TRUC()
et a pour but d’appeler les deux autres : balise_TRUC_stat()
-qui effectue les calculs statiques nécessaires- et balise_TRUC_dyn()
qui effectue les calculs dynamiques et renvoie le contenu final.
Exemple :
Supposons que je crée une balise #HITS
, pour afficher le nombre de visites d’un article.
Ma fonction balise_HITS
préparera SPIP à appeler les fonctions balise_HITS_xxxxx
de la façon suivante :
- appel de
balise_HITS_stat
pour déterminer les paramètres statiques de la balise (par exemple :"id_article = 36"
). - appel de
balise_HITS_dyn
en lui passant les paramètres statiques tels que déterminés parbalise_HITS_stat
- utilisation par
balise_HITS_dyn
des paramètres statiques pour interroger la base de données, etc et produire la sortie appropriée.
Le code à mettre en œuvre pour la balise #HITS
pourrait ressembler à :
<?php
function balise_HITS ($p, $nom = 'HITS') {
$args = array('id_article');
return calculer_balise_dynamique($p ,$nom ,$args);
}
function balise_HITS_stat($args, $filtres) {
return array($args[0]);
}
function balise_HITS_dyn($id_article) {
$maintenant = date ('c');
return "L'article $id_article a reçu 1 000 000 visites
à la date du $maintenant !";
}
?>
Mais ceci ne fonctionnera plus si je veux, par exemple, associer des détails de mon squelette aux paramètres statiques : à l’instant où je détermine les paramètres, je n’ai plus accès au nœud de l’arbre de syntaxe abstraite passé à balise_BIDULE ; tout ce que j’ai reçu à ce moment là, est un tableau d’arguments à passer à la balise et un tableau de filtres à lui appliquer. [1]
La solution évidente (mais fausse)
La solution évidente serait d’ajouter ces valeurs au tableau $args
que je passe à calculer_balise_dynamique()
.
Hélas, cela ne fonctionnera pas parce que $args
n’est pas (en dépit de son nom) un tableau d’arguments. C’est, en fait, un tableau des noms des arguments que SPIP est en train de récupérer automatiquement avant de les passer lors de l’appel de balise_xxxxx_stat()
.
Ajouter le nom du fichier squelette ("squelettes/truc.html"
par exemple) à $args
demanderait à SPIP de rechercher une variable appelée squelettes/truc.html dans le contexte à l’intérieur duquel la balise est utilisée et de la transmettre à la fonction suivante...
Inutile de dire que cela ne fonctionne pas.
La bonne solution
La bonne solution à ce problème est de prendre des dispositions pour que SPIP ajoute les valeurs que vous voulez au tableau $arguments
(ou peut-être au tableau $filtres
, mais ce n’est pas forcément une bonne idée).
Ce tableau contient les valeurs que j’ai demandées dans l’appel à calculer_balise_dynamique
en plus des valeurs des paramètres passés dans la balise dans le squelette.
« Si je ne peux pas utiliser la route principale, alors je prendrais un chemin de traverse » - j’ai besoin d’ajouter un autre paramètre « truqué » au nœud de l’AST avant d’appeler calculer_balise_dynamique
(Oui, je suis d’accord : il s’agit d’une drôle de façon d’arriver à ses fins, mais ça se passe comme çà chez SPIP-land).
Il y a deux parties dans l’AST de SPIP qui sont pertinentes ici : le nœud « Champ » qui représente les balises, et le nœud « Texte » qui représente les chaines (entre autres).
Le $p
qui est passé à mes fonctions balise_xxxxx est une instance de la class « Champ » et je vais ajouter une nouvelle instance à la class « Texte » qui sera mon nouveau paramètre « truqué ».
Créer le nouvel objet est assez simple, il suffit de le construire et de définir ses attributs type et texte. L’exemple suivant ajoute ainsi un nouveau paramètre contenant le nom du fichier squelette :
<?php
function balise_TEMPLATE ($p, $nom = 'TEMPLATE') {
$file = $p->descr['sourcefile'];
// Créer le nouvel objet
$t = new Texte;
$t->type = 'texte'; // (pas vraiment utile : 'texte' est le type par défaut-ndt)
$t->texte = $file;
// S'assurer que $p-param est un tableau
// (aux bonnes dimensions)
if (!is_array($p->param)) {
$p->param = array(array(0=>NULL));
}
// Ajouter l'objet aux paramètres de la balise
$p->param[0][] = array($t);
// Appel de la balise dynamique
$args = array();
return calculer_balise_dynamique($p, $nom, $args);
}
?>
- En premier lieu, ce code récupère le nom du fichier squelette depuis le nœud de l’AST pour la balise en cours de traitement.
- Puis il crée un nouvel objet « Texte » avec ce nom de fichier comme valeur. Il veille à ce que l’attribut
$p->param
de l’objet « Champ » soit un tableau (et oui, il semble bien qu’il commence avec unNULL
de sorte que nous pouvons prétendre que les tableaux démarrent à l’index 1), puis lui ajoute le nouvel objet. - Il ne reste plus qu’à appeler
calculer_balise_dynamique
comme d’habitude.
- Ceci fait, la valeur du nouveau paramètre truqué sera évaluée et transmise, dans le tableau
$args
, lors de l’appel debalise_TEMPLATE_stat()
puis (si c’est mon choix) lors de l’appel debalise_TEMPLATE_dyn()
.
Conclusion
Cette technique me semble encore un peu bizarre, mais c’est la seule façon que je vois de mettre en œuvre cet effet sans introduire de variables globales.
À mon avis, calculer_balise_dynamique
devraient pouvoir prendre un tableau de valeurs comme quatrième argument facultatif, mais le besoin de faire ce genre de chose est probablement assez rare (bien que je l’ai vu dans le code pour une ou deux balises). [1]
Mais si cette technique était la « Right Way™ » pour passer des valeurs supplémentaires, alors il serait vraiment nécessaire d’avoir une fonction auxiliaire comme interprete_argument_balise
plutôt que de pourrir l’AST pour chaque balise qui en a besoin.