Nouvelle balise : exemple avec #TMP

Exemple de création d’une balise #TMP pour mémoriser des infos et les réutiliser par la suite...

Cette contrib est avant tout un exemple de création d’une balise qui, je l’espère, pourra quand-même rendre service aux Spipeurs...

Bien-sûr, ce n’est qu’un exemple, il est donc fort probable que des améliorations soient souhaitables, voire nécessaires, pour optimiser son fonctionnement, n’hésitez donc pas à faire part de vos remarques et/ou suggestions dans le forum attaché à l’article...

Avant-Propos

Alors, voici le contexte :

  • Imaginons qu’on souhaite mémoriser quelques informations ou données pour être utilisées par la suite...
  • Imaginons aussi qu’on souhaite pouvoir disposer des données mémorisées partout dans les squelettes...
  • Enfin, imaginons qu’on souhaite disposer d’un système suffisamment souple permettant d’ajouter, modifier et supprimer des données...

Bien-sûr, certains diront qu’un tel mécanisme existe déjà, fourni par défaut dans Spip, sous la forme des balises #SET et #GET (voir documentation), mais ces balises sont un peu limités, c’est à dire qu’elles ne fonctionnent pas dans toutes les situations...

En effet, si on mémorise une variable à l’intérieur d’un squelette, les squelettes inclus (les « noisettes » donc) n’auront pas acces aux données des ces variables, ce qui peut s’avérer un peu embêtant dans certaines circonstances...

Il était donc peut-être intéressant de pouvoir disposer d’un système capable de fonctionner dans ce type de situations, c’est donc le rôle de cette balise #TMP (pour « tampon » ou « temporaire »).

Attention !

Au même titre que les balises #SET et #GET, cette balise #TMP n’aura aucun effet dans une boucle si la valeur à récupérer a été mémorisée dans la partie optionnelle de la même boucle. Ce comportement est dû au mode de fonctionnement des boucles Spip.

En effet, les parties optionnelles des boucles sont analysées et traitées après la partie principale de la boucle, et seulement si cette dernière retourne au moins une valeur, autrement dit, si la boucle principale ne retourne aucune valeur, les perties optionnelles ne seront pas considérées.

Utilisation

Passons à l’utilisation de la bête avant d’ouvrir le capot et d’examiner le moteur.

Voici toutes les utilisations possibles et leur syntaxe :

-  Mémoriser une Valeur

Deux méthodes pour mémoriser une valeur, soit en passant par la balise #TMP, soit en passant par le filtre |tmp.

La balise #TMP sert surtout à mémoriser des données libres, c’est à dire toute information non fournie par Spip, alors que le filtre |tmp est plutôt dédié à la mémorisation des données générées par Spip (les balises), ce que la balise #TMP ne permet pas. [1]

  • Avec la balise #TMP :

Si par exemple on souhaite mémoriser la valeur « Coucou les amis... » sous la clé « coucou », voici comment faire :

[(#TMP{coucou,'Coucou les amis...'})]

Dans cette méthode, la première valeur fournie est donc la clé d’identification sous laquelle les données seront mémorisées, puis la deuxième valeur est la donnée elle même (la syntaxe de cette balise est donc exactement la même que celle de la balise #SET de Spip).

  • Avec le filtre |tmp :

Le filtre ne permet pas, par défaut, de mémoriser des données libres, ceci est plutôt le rôle de la balise, mais le filtre |tmp est nécessaire pour mémoriser des données générées par Spip (les balises donc).

Si on souhaite par exemple afficher la date du jour à l’intérieur d’une boucle (ARTICLES) (c’est juste un exemple hein ;-)), on ne pourrait pas utiliser la balise #DATE de Spip, puisque cette balise retourne une valeur différente suivant le contexte, dans notre cas c’est la date de publication de l’article qui serait affichée et non pas la date du jour...

Pour y arriver, le filtre |tmp peut nous être utile, en mémorisant la date du jour, sous la clé « today » par exemple, avant le code de la boucle (ARTICLES), avec la syntaxe suivante :

[(#DATE|tmp{today})]

Dans cette méthode, la première valeur fournie au filtre, la balise #DATE donc, est la donnée à mémoriser, et l’argument fourni au filtre entre les accolades, {today}, est la clé sous laquelle la date du jour sera conservée.

-  Récupérer une Valeur

Pour retrouver une valeur mémorisée, rien de plus simple, la balise #TMP s’utilise de la même manière que son homologue #GET de Spip, avec la syntaxe :

[(#TMP{coucou})]

Cet exemple retournera notre « Coucou les amis... ». Autrement dit, il suffit de fournir, comme argument a la balise, la clé sous laquelle l’information a été enregistrée, quelque soit la méthode de mémorisation utilisée, par balise ou par filtre.

Notre second exemple retournera la date du jour au moment de la mémorisation de la valeur :

[(#TMP{today})]

-  Valeur par Défaut

Il peut être pratique de disposer d’une valeur par défaut à utiliser si la variable demandée n’est pas ou n’est plus disponible, ceci est possible de la manière suivante :

[(#TMP{coucou,'',defaut})]

La syntaxe à respecter nécessite donc de fournir 3 arguments à la balise, dont le premier correspond a la clé des données qu’on cherche à récupérer, le deuxième argument doit être vide, ce qu’on obtient en mettant deux guillemets, ou deux apostrophes, sans aucun contenu entre les deux, enfin, le troisième argument est la valeur par défaut à afficher si la clé du premier argument n’est pas trouvée.

Attention !

La valeur par défaut est une donnée libre, c’est à dire qu’il ne s’agit pas d’une clé servant à extraire d’autres données en mémoire, mais d’une valeur « brute » à afficher tel-quelle si la clé du premier argument est introuvable.

-  Modifier une Valeur

Il est aussi possible de modifier la valeur d’une variable déjà en mémoire, il suffit de mémoriser la nouvelle valeur avec exactement la même clé que la valeur originale, ceci fonctionne avec les deux méthodes : balise et filtre.

Suivant notre premier exemple, si au lieu d’avoir « Coucou les amis... » nous souhaitions afficher « Bonjour monde... » à la place, on ferait :

[(#TMP{coucou,'Bonjour monde..'})]

Dès lors, la clé « coucou » retournera cette nouvelle valeur.

-  Supprimer une Valeur

Pour ne pas saturer inutilement l’espace mémoire utilisé (voir chapitre « Mécanique ») avec des données qui ne sont plus nécessaires, il est possible de supprimer les valeurs obsolètes.

La méthode est très simple, il suffit de fournir deux arguments à la balise #TMP, dont le premier doit être vide et le deuxième correspondre à une clé existante en mémoire.

Par exemple, si on souhaite supprimer notre variable « coucou », il suffit d’utiliser la syntaxe suivante :

[(#TMP{'',coucou})]

Ici on fourni à la balise un premier argument vide, qu’on obtient en mettant deux guillemets, ou deux apostrophes, sans aucun contenu entre les deux, puis on fourni le nom de la clé correspondant aux données à supprimer.

Attention !

Ceci a pour effet de supprimer purement et simplement le couple « clé/valeur » concerné, les données seront donc perdues définitivement.

-  Supprimer Toutes les Valeurs

On peut aussi supprimer toutes les données mémorisées en une seule opération, et éviter ainsi de devoir faire autant de suppressions individuelles, épargnant du coup un peu de travail au serveur.

Pour supprimer donc toutes les entrées mémorisées, il suffit d’insérer la balise #TMP toute seule, sans aucun argument, soit :

[(#TMP)]

Attention !

Ceci a pour effet de supprimer purement et simplement toutes les entrées en mémoire gérées par la balise, les données seront donc perdues définitivement.

Résumons

  • Mémoriser une valeur :
    [(#TMP{cle,valeur})]
    ou
    [(#VALEUR|tmp{cle})]
  • Récupérer une valeur mémorisée :
    [(#TMP{cle})]
  • Valeur par défaut (au cas où) :
    [(#TMP{cle,'',defaut})]
  • Supprimer une valeur :
    [(#TMP{'',cle})]
  • Supprimer toutes les valeurs :
    [(#TMP)]

Mécanique

Le mécanisme mis en oeuvre est relativement simple, voire simpliste aux yeux de certains, mais il a le mérite d’assurer un bon fonctionnement.

Il s’agit donc d’utiliser la variable globale $GLOBALS pour y stocker des données et informations qui seront ensuite récupérées pour être exploités aux endroits souhaités dans les différents squelettes...

D’accord, de l’avis de certains, c’est « mal » d’utiliser cette variable globale un peu pour tout et parfois pour n’importe quoi, mais avouons qu’elle a le grand avantage d’être disponible et facilement accessible sans avoir à sortir la grosse artillerie des sauvegardes en base de données ou dans des fichiers... ce qui aurait été un peu lourd pour des besoins relativement modestes, tels qu’abordés dans cette contrib.

Donc, en gros, pour ceux qui lisent un peu du Php, il s’agit de créer des nouvelles entrées dans la variable $GLOBALS, sous la clé générique ['tmp'], avec la syntaxe suivante :

$GLOBALS['tmp'][$cle] = $valeur;

Pour ensuite être utilisable par :

return $GLOBALS['tmp'][$cle];

Moteur

Voyons donc ce qui se cache sous le capot...

La balise est construite avec trois fonctions Php :

  • La première fonction implémente la balise #TMP elle même, c’est le code appelé par Spip.
  • La deuxième fonction est juste un pont entre le code de la balise #TMP et celui du filtre |tmp.
  • Enfin, la troisième fonction correspond au filtre |tmp, et c’est elle qui gère les accès aux données.

Voici le code complet, avec quelques commentaires :

<?php
/**************************************************************************************/
/* Balise "#TMP" 1.0 */
/* ---------------------------------------------------------------------------------------------------- */
/* Infos : GPL - 02/08 (c) FredoMkb */
/* Utilisation : #TMP{cle,valeur,defaut} (voir details dans le code) */
/* Role : Memorise des valeurs pour pouvoir les utiliser ensuite */
/* ---------------------------------------------------------------------------------------------------- */
function balise_TMP($p) {
// Fonction de la balise "#TMP", c'est le code appele par Spip.

	// Initialisation des variables qui vont recevoir les arguments 
	$cle = "";
	$val = "";
	$dft = "";

	// Recuperation des paramettres fournis, 3 arguments reconnus 
	// On test d'abord l'existence de chaque parametre, s'il existe, 
	// alors on affecte l'argument a la variable correspondante. 
	if (isset($p->param[0][1])) { $cle .= ($p->param[0][1][0]->texte); }
	if (isset($p->param[0][2])) { $val .= ($p->param[0][2][0]->texte); }
	if (isset($p->param[0][3])) { $dft .= ($p->param[0][3][0]->texte); }

	// Comme on ne peut pas passer des "array", il faut generer du "string" exploitable
	$listargs = var_export(array($cle,$val,$dft), true);

	// Affectation du nom de la fonction a utiliser dans la balise, avec les arguments
	$p->code = "get_tmp($listargs)";

	// Le statut 'php' est sur, le statut 'html' passe le retour par le filtre 'interdire_scripts'.
	// On peut aussi faire : '$p->interdire_scripts = false;' ou 'true'
	$p->statut = 'html';

	// Retour du resultat
	return $p;
}
function get_tmp($listargs) {
// Fonction pour faire le "pont" entre la balise "#TMP" et le filtre "|tmp".
	$cle = $listargs[0];
	$val = $listargs[1];
	$dft = $listargs[2];

	// On change un peu l'ordre des arguments afin que le filtre
	// puisse trouver la variable $val en premiere place
	return tmp($val, $cle, $dft);
}
function tmp($val='', $cle='', $dft='') {
// Fonction du filtre "|tmp", gere les donnees, pour memoriser ou retourner une valeur.

	if (empty($cle) && empty($val) && empty($dft)) {
		// Si les trois arguments sont vides, alors on supprime la GLOBALE "tmp"
		// Utilisation : [(#TMP)]
		unset($GLOBALS['tmp']);

	} elseif (empty($cle) && !empty($val)) {
		// Si la cle est vide mais pas la valeur, alors un utilise cette derniere comme cle 
		// pour supprimer l'entree ayant comme cle la valeur fournie
		// Utilisation : [(#TMP{'',cle})]
		unset($GLOBALS['tmp'][$val]);

	} elseif (!empty($cle) && !empty($val)) {
		// Si la cle et la valeur sont fournies, alors on memorise ces infos 
		// Utilisation : [(#TMP{cle,valeur})] ou [(#VALEUR|tmp{cle})]
		$GLOBALS['tmp'][$cle] = $val;

	} elseif (!empty($cle) && isset($GLOBALS['tmp'][$cle])) { 
		// Si la cle seule est fournie, alors on tente de retourner les donnees correspondantes 
		// Utilisation : [(#TMP{cle})]
		return $GLOBALS['tmp'][$cle];

	} else {
		// Si rien de ce qui precede ne fonctionne, alors on retourne la valeur par defaut fournie
		// Utilisation : [(#TMP{cle,'',defaut})]
		return $dft;
	}
}
/**************************************************************************************/
?>

Installation

Pour éviter d’éventuels problèmes d’encodage du texte, vous pouvez récupérer le code complet en téléchargeant l’archive suivante :

Balise #TMP

Pour installer la balise, deux méthodes :

-  Par Copier/Coller

Éditez simultanément le fichier téléchargé « spip_balise_tmp.php » et le fichier
« mes_fonctions.php » qui se trouve dans le dossier « squelettes » de votre site.

Faites un simple copier/coller de l’ensemble du code de la balise, fichier « spip_balise_tmp.php » donc, dans le fichier « mes_fonctions.php », au début ou à la fin du fichier.

Assurez-vous juste de ne pas copier les balises Php <?php et ?> qui se trouvent respectivement en début et fin du code.

-  Par Inclusion

Cette méthode est plus simple et donc plus recommandée.

Il s’agit de placer le fichier téléchargé « spip_balise_tmp.php » au même niveau que le fichier « mes_fonctions.php », dans le même répertoire donc.

Il faut ensuite éditer le fichier « mes_fonctions.php » pour y placer, au début (juste après la balise d’ouverture Php), le bout de code suivant :

include('spip_balise_tmp.php');

Dans les deux cas, enregistrez les modifications du fichier « mes_fonctions.php » puis videz le cache de Spip (espace privé => Configuration => Vider le cache).

Limitations

La balise #TMP, en l’état, n’est pas capable de traiter dans ses arguments des valeurs dynamiques, d’autres balises par exemple, il s’agit donc pour l’instant d’une simple balise « statique ».

Le filtre |tmp tente de pallier, en partie, à cette restriction de la balise, mais son champ d’action reste assez limité tout de même, puisqu’il n’est pas possible, par exemple, d’insérer du code Html de manière simple et directe (voir chapitre « Astuces »).

Enfin, l’utilisation de la variable globale $GLOBALS, pour mémoriser les variables, n’autorise pas la manipulation de grands volumes de données, ni de données aux contenus complexes, il faut donc se limiter à une utilisation modeste, voire basique, de cette solution.

Astuces

-  Mini Config

Une idée d’utilisation de cette balise #TMP serait, par exemple, de préparer une « noisette » contenant un ensemble de valeurs à mémoriser, pour qu’elles soient disponibles dans toutes les pages du squelette, un peu comme s’il s’agissait d’un mini système de configuration...

Par exemple, on crée un fichier Html nommé « inc-mini-config.html », dans lequel on insère autant de balises #TMP que des infos à mémoriser, sans aucun autre code Html.

Il suffit d’insérer cette noisette, par un simple #INCLURE (voir encadré ci-dessous), dans le fichier d’en tête du site « inc-head.html », pour avoir toutes ces données accessibles sur l’ensemble des fichiers du squelette.

Bien-sûr, il ne faut pas trop abuser de cette technique, il vaut mieux se tourner vers d’autres solutions de configuration, comme le plugin CFG par exemple, pour disposer d’un système de configuration plus souple et puissant.

Attention !

Spip possède deux mécanismes différents pour inclure des fichiers externes dans un fichier hôte, soit par la balise #INCLURE (depuis Spip 1.9.1) soit par la méthode historique <INCLURE> (voir documentation).

Or, ces deux techniques ne travaillent pas exactement de la même manière, notamment en ce qui concerne la gestion du cache, ce qui restreint l’utilisation de notre balise #TMP à travailler uniquement avec les inclusions de type #INCLURE pour pouvoir bénéficier d’une disponibilité efficace des valeurs mémorisées dans la variable $GLOBALS.

Pour tenter de résumer en deux mots, disons que les fichiers hôte qui travaillent avec des balises de type #INCLURE génèrent un seul fichier de cache avec les données locales et celles issues des fichiers externes inclus, alors que la méthode par <INCLURE> génère autant de fichiers de cache différents que des fichiers traités, autrement dit, ils ne partagent pas forcément le même espace mémoire de travail et n’auront donc pas accès aux données correspondantes.

Ces remarques ne font que reprendre des explications for instructives partagées par Matthieu Marcillaud quant au fonctionnement des inclusions et à l’efficacité relative de cette contrib avec elles, merci à lui pour tout ces éclaircissements.

-  Balises et Html

Comme expliqué dans le chapitre « Limitations », la balise n’est pas adaptée à traiter des données dynamiques, et le filtre est incapable en l’état de mélanger des balises Spip a du code Html... voici donc une astuce pour contourner cette restriction.

Il s’agit d’utiliser la balise #REM pour lui forcer à afficher quelque chose (technique expliquée dans l’article « Appliquer un filtre sur autre chose qu’une balise »), puis d’appliquer le filtre sur ce résultat.

Imaginons que nous désirons insérer, un peu partout sur nos squelettes, un lien complet avec l’adresse du site, il peut être sympa de ne pas avoir à mettre toujours le même code Html, voici donc comment mémoriser un tel code afin de l’utiliser autant de fois qu’on le souhaite :

[(#REM|sinon{<a href="#URL_SITE_SPIP" title="[(#DESCRIPTIF_SITE_SPIP|couper{50})]">
<:bienvenue_sur:>#NOM_SITE_SPIP</a>}|tmp{url})]

Petite explication, on force une balise #REM à afficher quelque chose grâce au filtre |sinon (voir documentation), puis on applique notre filtre |tmp en lui fournissant la cle d’identification du contenu à mémoriser.

Comme le montre l’exemple, on peut mettre des balises, des filtres et même des textes localisés entre les accolades du filtre |sinon, mais il ne faut pas trop compliquer le contenu à mémoriser, il n’est pas garanti que Spip puisse gérer de telles acrobaties de calcul avec des codes trop complexes.

Enfin, pour placer ce code dans les squelettes, rien de plus simple :

[(#TMP{url})]

Un petit avantage de cette méthode est que le code Html est calculé une seule fois, puis le résultat est conservé en mémoire en l’état, ce qui permet de l’utiliser autant de fois que souhaité sans avoir à les recalculer.

Avertissements

Comme signalé à plusieurs reprises dans l’article, ne tentez pas d’utiliser cette technique pour des traitements lourds, elle n’est pas adaptée, préférez vous tourner vers de plugins capables d’une meilleur interaction avec Spip.

Enfin, veillez à nommer de manière la plus simple possible vos clés d’identification pour les données à mémoriser, évitez par exemple d’utiliser des caractères accentués ou spéciaux et, dans la mesure du possible, préférez les tirets bas « _ » aux espaces pour séparer les différents mots de la clé.

Remerciements

Comme d’hab, à toute la communauté de Spipiens pour toutes leurs contributions, les unes plus enrichissantes que les autres, et aussi, surtout, à tout ceux qui se démènent (jour et nuit) pour développer ce magnifique CMS qu’est SPIP.

Un grand merci à Matthieu Marcillaud pour toutes ces explications à propos du fonctionnement des différentes méthodes d’inclusion.

Notes

[1Ceci est dû au fait que #TMP n’est pas une balise dynamique (ce que je n’aurais pas su réaliser vu mon niveau en développement).

Discussion

Une discussion

  • Bonjour j’ai utilisé votre balise pour passer une variable avec le plugin agenda et le mini calendrier. Seulement cela ne marche que lorsque je suis identifié en temps que modérateur et que je recalcule la page.

    Merci d’avance cordialement,
    Mathieu.

    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