3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2019 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
14 * Fonctions de recherche et de reservation dans l'arborescence des boucles
16 * @package SPIP\Core\Compilateur\References
18 if (!defined('_ECRIRE_INC_VERSION')) {
23 * Retrouver l'index de la boucle d'une balise
25 * Retrouve à quelle boucle appartient une balise, utile dans le cas
26 * où une référence explicite est demandée
28 * - `#MABALISE` : l'index est celui de la première boucle englobante
29 * - `#_autreboucle:MABALISE` : l'index est celui de la boucle _autreboucle si elle est bien englobante
32 * Dans une balise dynamique ou calculée :
34 * $idb = index_boucle($p);
37 * @param Champ $p AST au niveau de la balise
40 * - Identifiant de la boucle possédant ce champ.
41 * - '' si une référence explicite incorrecte est envoyée
43 function index_boucle($p) {
46 $explicite = $p->nom_boucle
;
48 if (strlen($explicite)) {
49 // Recherche d'un champ dans un etage superieur
50 while (($idb !== $explicite) && ($idb !== '')) {
51 $idb = $p->boucles
[$idb]->id_parent
;
59 * Retourne la position dans la pile d'un champ SQL
61 * Retourne le code PHP permettant de récupérer un champ SQL dans
62 * une boucle parente, en prenant la boucle la plus proche du sommet de pile
65 * Si on ne trouve rien, on considère que ça doit provenir du contexte
66 * (par l'URL ou l'include) qui a été recopié dans Pile[0]
67 * (un essai d'affinage a débouché sur un bug vicieux)
69 * Si ca référence un champ SQL, on le mémorise dans la structure $boucles
70 * afin de construire un requête SQL minimale (plutôt qu'un brutal 'SELECT *')
72 * @param string $idb Identifiant de la boucle
73 * @param string $nom_champ Nom du champ SQL cherché
74 * @param array $boucles AST du squelette
75 * @param string $explicite
76 * Indique que le nom de la boucle est explicite dans la balise #_nomboucletruc:CHAMP
77 * @param null|string $defaut
78 * Code par defaut si le champ n'est pas trouvé dans l'index.
79 * Utilise @$Pile[0][$nom_champ] si non fourni
80 * @param bool $remonte_pile
81 * Permettre de remonter la pile des boucles ou non (dans ce cas on
82 * ne cherche que danss la 1ère boucle englobante)
84 * Pour ajouter au select de la boucle, par defaut true
86 * Code PHP pour obtenir le champ SQL
97 if (!is_string($defaut)) {
98 $defaut = '@$Pile[0][\'' . strtolower($nom_champ) . '\']';
102 if (strlen($explicite)) {
103 // Recherche d'un champ dans un etage superieur
104 while (($idb !== $explicite) && ($idb !== '')) {
105 # spip_log("Cherchexpl: $nom_champ '$explicite' '$idb' '$i'");
107 $idb = $boucles[$idb]->id_parent
;
111 # spip_log("Cherche: $nom_champ a partir de '$idb'");
112 $nom_champ = strtolower($nom_champ);
113 $conditionnel = array();
114 // attention: entre la boucle nommee 0, "" et le tableau vide,
115 // il y a incoherences qu'il vaut mieux eviter
116 while (isset($boucles[$idb])) {
118 // modifie $joker si tous les champs sont autorisés.
119 // $t = le select pour le champ, si on l'a trouvé (ou si joker)
120 // $c = le nom du champ demandé
121 list($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles, $joker);
123 if ($select and !in_array($t, $boucles[$idb]->select
)) {
124 $boucles[$idb]->select
[] = $t;
126 $champ = '$Pile[$SP' . ($i ?
"-$i" : "") . '][\'' . $c . '\']';
128 return index_compose($conditionnel, $champ);
131 // tant que l'on trouve des tables avec joker, on continue
132 // avec la boucle parente et on conditionne à l'exécution
133 // la présence du champ. Si le champ existe à l'exécution
134 // dans une boucle, il est pris, sinon on le cherche dans le parent...
135 $conditionnel[] = "isset($champ)?$champ";
139 # spip_log("On remonte vers $i");
140 // Sinon on remonte d'un cran
141 $idb = $boucles[$idb]->id_parent
;
148 # spip_log("Pas vu $nom_champ");
149 // esperons qu'il y sera
150 // ou qu'on a fourni une valeur par "defaut" plus pertinent
151 return index_compose($conditionnel, $defaut);
155 * Reconstuire la cascade de condition de recherche d'un champ
157 * On ajoute la valeur finale par défaut pour les balises dont on ne saura
158 * qu'à l'exécution si elles sont definies ou non (boucle DATA)
160 * @param array $conditionnel Liste de codes PHP pour retrouver un champ
161 * @param string $defaut Valeur par défaut si aucun des moyens ne l'a trouvé
162 * @return string Code PHP complet de recherche d'un champ
164 function index_compose($conditionnel, $defaut) {
165 while ($c = array_pop($conditionnel)) {
166 // si on passe defaut = '', ne pas générer d'erreur de compilation.
167 $defaut = "($c:(" . ($defaut ?
$defaut : "''") . "))";
174 * Cherche un champ dans une boucle
176 * Le champ peut être :
178 * - un alias d'un autre : il faut alors le calculer, éventuellement en
179 * construisant une jointure.
180 * - présent dans la table : on l'utilise
181 * - absent, mais le type de boucle l'autorise (joker des itérateurs DATA) :
182 * on l'utilise et lève le drapeau joker
183 * - absent, on cherche une jointure et on l'utilise si on en trouve.
186 * Ici la recherche de jointure sur l'absence d'un champ ne cherche
187 * une jointure que si des jointures explicites sont demandées,
188 * et non comme à d'autres endroits sur toutes les jointures possibles.
189 * Il faut homogénéiser cela.
192 * @param string $idb Identifiant de la boucle
193 * @param string $nom_champ Nom du champ SQL cherché
194 * @param Boucle $boucles AST du squelette
196 * Le champ peut-il être inconnu à la compilation ?
197 * Ce drapeau sera levé si c'est le cas.
199 * Liste (Nom du champ véritable, nom du champ demandé).
200 * Le nom du champ véritable est une expression pour le SELECT de
201 * la boucle tel que "rubriques.titre" ou "mots.titre AS titre_mot".
202 * Les éléments de la liste sont vides si on ne trouve rien.
204 function index_tables_en_pile($idb, $nom_champ, &$boucles, &$joker) {
206 $r = $boucles[$idb]->type_requete
;
207 // boucle recursive, c'est foutu...
208 if ($r == TYPE_RECURSIF
) {
212 $joker = false; // indiquer a l'appelant
213 # continuer pour chercher l'erreur suivante
214 return array("'#" . $r . ':' . $nom_champ . "'", '');
217 $desc = $boucles[$idb]->show
;
218 // le nom du champ est il une exception de la table ? un alias ?
219 $excep = isset($GLOBALS['exceptions_des_tables'][$r]) ?
$GLOBALS['exceptions_des_tables'][$r] : '';
221 $excep = isset($excep[$nom_champ]) ?
$excep[$nom_champ] : '';
224 $joker = false; // indiquer a l'appelant
225 return index_exception($boucles[$idb], $desc, $nom_champ, $excep);
226 } // pas d'alias. Le champ existe t'il ?
228 // le champ est réellement présent, on le prend.
229 if (isset($desc['field'][$nom_champ])) {
230 $t = $boucles[$idb]->id_table
;
231 $joker = false; // indiquer a l'appelant
232 return array("$t.$nom_champ", $nom_champ);
234 // Tous les champs sont-ils acceptés ?
235 // Si oui, on retourne le champ, et on lève le flag joker
236 // C'est le cas des itérateurs DATA qui acceptent tout
237 // et testent la présence du champ à l'exécution et non à la compilation
238 // car ils ne connaissent pas ici leurs contenus.
239 elseif (/*$joker AND */
240 isset($desc['field']['*'])
242 $joker = true; // indiquer a l'appelant
243 return array($nom_champ, $nom_champ);
245 // pas d'alias, pas de champ, pas de joker...
246 // tenter via une jointure...
248 $joker = false; // indiquer a l'appelant
249 // regarder si le champ est deja dans une jointure existante
250 // sinon, si il y a des joitures explicites, la construire
251 if (!$t = trouver_champ_exterieur($nom_champ, $boucles[$idb]->from
, $boucles[$idb])) {
252 if ($boucles[$idb]->jointures_explicites
) {
253 // [todo] Ne pas lancer que lorsque il y a des jointures explicites !!!!
254 // fonctionnel, il suffit d'utiliser $boucles[$idb]->jointures au lieu de jointures_explicites
255 // mais est-ce ce qu'on veut ?
256 $jointures = preg_split("/\s+/", $boucles[$idb]->jointures_explicites
);
257 if ($cle = trouver_jointure_champ($nom_champ, $boucles[$idb], $jointures)) {
258 $t = trouver_champ_exterieur($nom_champ, $boucles[$idb]->from
, $boucles[$idb]);
263 // si on a trouvé une jointure possible, on fait comme
264 // si c'était une exception pour le champ demandé
265 return index_exception($boucles[$idb],
268 array($t[1]['id_table'], reset($t[2])));
271 return array('', '');
278 * Retrouve un alias d'un champ dans une boucle
280 * Référence à une entite SPIP alias d'un champ SQL.
281 * Ça peut même être d'un champ dans une jointure qu'il faut provoquer
284 * @param Boucle $boucle Boucle dont on prend un alias de champ
285 * @param array $desc Description de la table SQL de la boucle
286 * @param string $nom_champ Nom du champ original demandé
287 * @param array $excep
288 * Description de l'exception pour ce champ. Peut être :
290 * - string : nom du champ véritable dans la table
292 * - liste (table, champ) indique que le véritable champ
293 * est dans une autre table et construit la jointure dessus
294 * - liste (table, champ, fonction) idem, mais en passant un
295 * nom de fonction qui s'occupera de créer la jointure.
297 * Liste (nom du champ alias, nom du champ). Le nom du champ alias
298 * est une expression pour le SELECT de la boucle du style "mots.titre AS titre_mot"
300 function index_exception(&$boucle, $desc, $nom_champ, $excep) {
301 static $trouver_table;
302 if (!$trouver_table) {
303 $trouver_table = charger_fonction('trouver_table', 'base');
306 if (is_array($excep)) {
307 // permettre aux plugins de gerer eux meme des jointures derogatoire ingerables
309 if (count($excep) == 3) {
310 $index_exception_derogatoire = array_pop($excep);
311 $t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep);
314 list($e, $x) = $excep; #PHP4 affecte de gauche a droite
315 $excep = $x; #PHP5 de droite a gauche !
316 $j = $trouver_table($e, $boucle->sql_serveur
);
318 return array('', '');
321 if (!$t = array_search($e, $boucle->from
)) {
322 $k = $j['key']['PRIMARY KEY'];
323 if (strpos($k, ',')) {
324 $l = (preg_split('/\s*,\s*/', $k));
325 $k = $desc['key']['PRIMARY KEY'];
326 if (!in_array($k, $l)) {
327 spip_log("jointure impossible $e " . join(',', $l));
329 return array('', '');
332 $k = array($boucle->id_table
, array($e), $k);
333 fabrique_jointures($boucle, array($k));
334 $t = array_search($e, $boucle->from
);
338 $t = $boucle->id_table
;
340 // demander a SQL de gerer le synonyme
341 // ca permet que excep soit dynamique (Cedric, 2/3/06)
342 if ($excep != $nom_champ) {
343 $excep .= ' AS ' . $nom_champ;
346 return array("$t.$excep", $nom_champ);
350 * Demande le champ '$champ' dans la pile
352 * Le champ est cherché dans l'empilement de boucles, sinon dans la valeur
353 * par défaut (qui est l'environnement du squelette si on ne la précise pas).
356 * @param string $champ
359 * AST au niveau de la balise
360 * @param null|string $defaut
361 * Code de la valeur par défaut si on ne trouve pas le champ dans une
362 * des boucles parentes. Sans précision, il sera pris dans l'environnement
364 * Passer $defaut = '' pour ne pas prendre l'environnement.
365 * @param bool $remonte_pile
366 * Permettre de remonter dans la pile des boucles pour trouver le champ
368 * Code PHP pour retrouver le champ
370 function champ_sql($champ, $p, $defaut = null, $remonte_pile = true) {
371 return index_pile($p->id_boucle
, $champ, $p->boucles
, $p->nom_boucle
, $defaut, $remonte_pile);
376 * Calcule et retourne le code PHP d'exécution d'une balise SPIP et des ses filtres
378 * Cette fonction qui sert d'API au compilateur demande à calculer
379 * le code PHP d'une balise, puis lui applique les filtres (automatiques
380 * et décrits dans le squelette)
382 * @uses calculer_balise()
383 * @uses applique_filtres()
386 * AST au niveau de la balise
388 * Code PHP pour d'exécution de la balise et de ses filtres
390 function calculer_champ($p) {
391 $p = calculer_balise($p->nom_champ
, $p);
393 return applique_filtres($p);
398 * Calcule et retourne le code PHP d'exécution d'une balise SPIP
400 * Cette fonction qui sert d'API au compilateur demande à calculer
401 * le code PHP d'une balise (cette fonction ne calcule pas les éventuels
402 * filtres de la balise).
404 * Pour une balise nommmée `NOM`, elle demande à `charger_fonction()` de chercher
405 * s'il existe une fonction `balise_NOM` ou `balise_NOM_dist`
406 * éventuellement en chargeant le fichier `balise/NOM.php.`
408 * Si la balise est de la forme `PREFIXE_SUFFIXE` (cf `LOGO_*` et `URL_*`)
409 * elle fait de même avec juste le `PREFIXE`.
411 * S'il n'y a pas de fonction trouvée, on considère la balise comme une référence
412 * à une colonne de table SQL connue, sinon à l'environnement (cf. `calculer_balise_DEFAUT_dist()`).
414 * Les surcharges des colonnes SQL via charger_fonction sont donc possibles.
416 * @uses calculer_balise_DEFAUT_dist()
417 * Lorsqu'aucune fonction spécifique n'est trouvée.
418 * @see charger_fonction()
419 * Pour la recherche des fonctions de balises
424 * AST au niveau de la balise
426 * Pile complétée par le code PHP pour l'exécution de la balise et de ses filtres
428 function calculer_balise($nom, $p) {
430 // S'agit-t-il d'une balise_XXXX[_dist]() ?
431 if ($f = charger_fonction($nom, 'balise', true)) {
432 $p->balise_calculee
= true;
434 if ($res !== null and is_object($res)) {
439 // Certaines des balises comportant un _ sont generiques
440 if ($f = strpos($nom, '_')
441 and $f = charger_fonction(substr($nom, 0, $f +
1), 'balise', true)
444 if ($res !== null and is_object($res)) {
449 $f = charger_fonction('DEFAUT', 'calculer_balise');
456 * Calcule et retourne le code PHP d'exécution d'une balise SPIP non déclarée
458 * Cette fonction demande à calculer le code PHP d'une balise qui
459 * n'a pas de fonction spécifique.
461 * On considère la balise comme une référence à une colonne de table SQL
462 * connue, sinon à l'environnement.
465 * Pour la recherche de la balise comme colonne SQL ou comme environnement
467 * Le texte de la balise est retourné si il ressemble à une couleur
468 * et qu'aucun champ correspondant n'a été trouvé, comme `#CCAABB`
473 * AST au niveau de la balise
475 * Code PHP pour d'exécution de la balise et de ses filtres
477 function calculer_balise_DEFAUT_dist($nom, $p) {
479 // ca pourrait etre un champ SQL homonyme,
480 $p->code
= index_pile($p->id_boucle
, $nom, $p->boucles
, $p->nom_boucle
);
482 // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour
483 // il faut recracher {...} quand ce n'est finalement pas des args
484 if ($p->fonctions
and (!$p->fonctions
[0][0]) and $p->fonctions
[0][1]) {
485 $code = addslashes($p->fonctions
[0][1]);
486 $p->code
.= " . '$code'";
489 // ne pas passer le filtre securite sur les id_xxx
490 if (strpos($nom, 'ID_') === 0) {
491 $p->interdire_scripts
= false;
494 // Compatibilite ascendante avec les couleurs html (#FEFEFE) :
495 // SI le champ SQL n'est pas trouve
496 // ET si la balise a une forme de couleur
497 // ET s'il n'y a ni filtre ni etoile
498 // ALORS retourner la couleur.
499 // Ca permet si l'on veut vraiment de recuperer [(#ACCEDE*)]
500 if (preg_match("/^[A-F]{1,6}$/i", $nom)
504 $p->code
= "'#$nom'";
505 $p->interdire_scripts
= false;
512 /** Code PHP d'exécution d'une balise dynamique */
513 define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s',
519 * Calcule le code PHP d'exécution d'une balise SPIP dynamique
521 * Calcule les balises dynamiques, notamment les `formulaire_*`.
523 * Inclut le fichier associé à son nom, qui contient la fonction homonyme
524 * donnant les arguments à chercher dans la pile, et qui sont donc compilés.
526 * On leur adjoint les arguments explicites de la balise (cf `#LOGIN{url}`)
527 * et d'éventuelles valeurs transmises d'autorité par la balise.
528 * (cf http://core.spip.net/issues/1728)
530 * La fonction `executer_balise_dynamique()` définie par la
531 * constante `CODE_EXECUTER_BALISE` recevra à l'exécution la valeur de tout ca.
533 * @uses collecter_balise_dynamique()
534 * Qui calcule le code d'exécution de chaque argument de la balise
535 * @see executer_balise_dynamique()
536 * Code PHP produit qui chargera les fonctions de la balise dynamique à l'exécution,
537 * appelée avec les arguments calculés.
539 * AST au niveau de la balise
541 * Nom de la balise dynamique
543 * Liste des noms d'arguments (balises) à collecter
545 * Liste de données supplémentaires à transmettre au code d'exécution.
547 * Balise complétée de son code d'exécution
549 function calculer_balise_dynamique($p, $nom, $l, $supp = array()) {
551 if (!balise_distante_interdite($p)) {
556 // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour
557 // il faut recracher {...} quand ce n'est finalement pas des args
558 if ($p->fonctions
and (!$p->fonctions
[0][0]) and $p->fonctions
[0][1]) {
559 $p->fonctions
= null;
562 if ($p->param
and ($c = $p->param
[0])) {
563 // liste d'arguments commence toujours par la chaine vide
565 // construire la liste d'arguments comme pour un filtre
566 $param = compose_filtres_args($p, $c, ',');
570 $collecte = collecter_balise_dynamique($l, $p, $nom);
572 $p->code
= sprintf(CODE_EXECUTER_BALISE
, $nom,
573 join(',', $collecte),
574 ($collecte ?
$param : substr($param, 1)), # virer la virgule
575 memoriser_contexte_compil($p),
576 (!$supp ?
'' : (', ' . join(',', $supp))));
578 $p->interdire_scripts
= false;
585 * Construction du tableau des arguments d'une balise dynamique.
587 * Pour chaque argument (un nom de balise), crée le code PHP qui le calculera.
590 * Ces arguments peuvent être eux-même des balises (cf FORMULAIRE_SIGNATURE)
591 * mais gare au bouclage (on peut s'aider de `$nom` pour le réperer au besoin)
593 * En revanche ils n'ont pas de filtres, donc on appelle `calculer_balise()` qui
594 * ne s'occupe pas de ce qu'il y a dans `$p` (mais qui va y ecrire le code)
596 * @uses calculer_balise()
597 * Pour obtenir le code d'éxécution de chaque argument.
600 * Liste des noms d'arguments (balises) à collecter (chaque argument
601 * de la balise dynamique est considéré comme étant un nom de balise)
603 * AST au niveau de la balise
607 * Liste des codes PHP d'éxecution des balises collectées
609 function collecter_balise_dynamique($l, &$p, $nom) {
612 $x = calculer_balise($c, $p);
621 * Récuperer le nom du serveur
623 * Mais pas si c'est un serveur spécifique dérogatoire
626 * AST positionné sur la balise
628 * Nom de la connexion
630 function trouver_nom_serveur_distant($p) {
631 $nom = $p->id_boucle
;
633 and isset($p->boucles
[$nom])
635 $s = $p->boucles
[$nom]->sql_serveur
;
637 and strlen($serveur = strtolower($s))
638 and !in_array($serveur, $GLOBALS['exception_des_connect'])
649 * Teste si une balise est appliquée sur une base distante
651 * La fonction loge une erreur si la balise est utilisée sur une
652 * base distante et retourne false dans ce cas.
655 * Il faudrait savoir traiter les formulaires en local
656 * tout en appelant le serveur SQL distant.
657 * En attendant, cette fonction permet de refuser une authentification
658 * sur quelque-chose qui n'a rien a voir.
661 * AST positionné sur la balise
664 * - true : La balise est autorisée
665 * - false : La balise est interdite car le serveur est distant
667 function balise_distante_interdite($p) {
668 $nom = $p->id_boucle
;
670 if ($nom and trouver_nom_serveur_distant($p)) {
671 spip_log($nom . ':' . $p->nom_champ
. ' ' . _T('zbug_distant_interdit'));
681 // Traitements standard de divers champs
682 // definis par $table_des_traitements, cf. ecrire/public/interfaces
684 // http://code.spip.net/@champs_traitements
685 function champs_traitements($p) {
687 if (isset($GLOBALS['table_des_traitements'][$p->nom_champ
])) {
688 $ps = $GLOBALS['table_des_traitements'][$p->nom_champ
];
690 // quand on utilise un traitement catch-all *
691 // celui-ci ne s'applique pas sur les balises calculees qui peuvent gerer
692 // leur propre securite
693 if (!$p->balise_calculee
) {
694 $ps = $GLOBALS['table_des_traitements']['*'];
701 // Recuperer le type de boucle (articles, DATA) et la table SQL sur laquelle elle porte
702 $idb = index_boucle($p);
703 // mais on peut aussi etre hors boucle. Se mefier.
704 $type_requete = isset($p->boucles
[$idb]->type_requete
) ?
$p->boucles
[$idb]->type_requete
: false;
705 $table_sql = isset($p->boucles
[$idb]->show
['table_sql']) ?
$p->boucles
[$idb]->show
['table_sql'] : false;
707 // le traitement peut n'etre defini que pour une table en particulier "spip_articles"
708 if ($table_sql and isset($ps[$table_sql])) {
709 $ps = $ps[$table_sql];
710 } // ou pour une boucle en particulier "DATA","articles"
711 elseif ($type_requete and isset($ps[$type_requete])) {
712 $ps = $ps[$type_requete];
713 } // ou pour indiferrement quelle que soit la boucle
714 elseif (isset($ps[0])) {
725 // Si une boucle DOCUMENTS{doublons} est presente dans le squelette,
726 // ou si in INCLURE contient {doublons}
727 // on insere une fonction de remplissage du tableau des doublons
728 // dans les filtres propre() ou typo()
729 // (qui traitent les raccourcis <docXX> referencant les docs)
731 if (isset($p->descr
['documents'])
733 $p->descr
['documents']
735 (strpos($ps, 'propre') !== false)
737 (strpos($ps, 'typo') !== false)
740 $ps = 'traiter_doublons_documents($doublons, ' . $ps . ')';
743 // La protection des champs par |safehtml est assuree par les extensions
744 // dans la declaration des traitements des champs sensibles
746 // Remplacer enfin le placeholder %s par le vrai code de la balise
747 return str_replace('%s', $p->code
, $ps);
752 // Appliquer les filtres a un champ [(#CHAMP|filtre1|filtre2)]
753 // retourne un code php compile exprimant ce champ filtre et securise
754 // - une etoile => pas de processeurs standards
755 // - deux etoiles => pas de securite non plus !
757 // http://code.spip.net/@applique_filtres
758 function applique_filtres($p) {
760 // Traitements standards (cf. supra)
761 if ($p->etoile
== '') {
762 $code = champs_traitements($p);
767 // Appliquer les filtres perso
769 $code = compose_filtres($p, $code);
772 // S'il y a un lien avec la session, ajouter un code qui levera
773 // un drapeau dans la structure d'invalidation $Cache
774 if (isset($p->descr
['session'])) {
775 $code = "invalideur_session(\$Cache, $code)";
778 $code = sandbox_composer_interdire_scripts($code, $p);
783 // Cf. function pipeline dans ecrire/inc_utils.php
784 // http://code.spip.net/@compose_filtres
785 function compose_filtres(&$p, $code) {
787 $image_miette = false;
788 foreach ($p->param
as $filtre) {
789 $fonc = array_shift($filtre);
792 } // normalement qu'au premier tour.
793 $is_filtre_image = ((substr($fonc, 0, 6) == 'image_') and $fonc != 'image_graver');
794 if ($image_miette and !$is_filtre_image) {
795 // il faut graver maintenant car apres le filtre en cours
796 // on est pas sur d'avoir encore le nom du fichier dans le pipe
797 $code = "filtrer('image_graver', $code)";
798 $image_miette = false;
800 // recuperer les arguments du filtre,
801 // a separer par "," ou ":" dans le cas du filtre "?{a,b}"
806 // |?{a,b} *doit* avoir exactement 2 arguments ; on les force
807 if (count($filtre) != 2) {
808 $filtre = array(isset($filtre[0]) ?
$filtre[0] : "", isset($filtre[1]) ?
$filtre[1] : "");
811 $arglist = compose_filtres_args($p, $filtre, $sep);
812 $logique = filtre_logique($fonc, $code, substr($arglist, 1));
816 $code = sandbox_composer_filtre($fonc, $code, $arglist, $p);
817 if ($is_filtre_image) {
818 $image_miette = true;
822 // ramasser les images intermediaires inutiles et graver l'image finale
824 $code = "filtrer('image_graver',$code)";
830 // Filtres et,ou,oui,non,sinon,xou,xor,and,or,not,yes
832 function filtre_logique($fonc, $code, $arg) {
835 case in_array($fonc, $GLOBALS['table_criteres_infixes']):
836 return "($code $fonc $arg)";
837 case ($fonc == 'and') or ($fonc == 'et'):
838 return "((($code) AND ($arg)) ?' ' :'')";
839 case ($fonc == 'or') or ($fonc == 'ou'):
840 return "((($code) OR ($arg)) ?' ' :'')";
841 case ($fonc == 'xor') or ($fonc == 'xou'):
842 return "((($code) XOR ($arg)) ?' ' :'')";
843 case ($fonc == 'sinon'):
844 return "(((\$a = $code) OR (is_string(\$a) AND strlen(\$a))) ? \$a : $arg)";
845 case ($fonc == 'not') or ($fonc == 'non'):
846 return "(($code) ?'' :' ')";
847 case ($fonc == 'yes') or ($fonc == 'oui'):
848 return "(($code) ?' ' :'')";
854 // http://code.spip.net/@compose_filtres_args
855 function compose_filtres_args($p, $args, $sep) {
857 foreach ($args as $arg) {
859 calculer_liste($arg, $p->descr
, $p->boucles
, $p->id_boucle
);
867 * Réserve les champs necessaires à la comparaison avec le contexte donné par
870 * Attention en recursif il faut les réserver chez soi-même ET chez sa maman
872 * @param string $idb Identifiant de la boucle
873 * @param string $nom_champ
874 * @param array $boucles AST du squelette
875 * @param null|string $defaut
878 function calculer_argument_precedent($idb, $nom_champ, &$boucles, $defaut = null) {
880 // si recursif, forcer l'extraction du champ SQL mais ignorer le code
881 if ($boucles[$idb]->externe
) {
882 index_pile($idb, $nom_champ, $boucles, '', $defaut);
883 // retourner $Pile[$SP] et pas $Pile[0] si recursion en 1ere boucle
884 // on ignore le defaut fourni dans ce cas
885 $defaut = "@\$Pile[\$SP]['$nom_champ']";
888 return index_pile($boucles[$idb]->id_parent
, $nom_champ, $boucles, '', $defaut);
892 // Rechercher dans la pile des boucles actives celle ayant un critere
893 // comportant un certain $motif, et construire alors une reference
894 // a l'environnement de cette boucle, qu'on indexe avec $champ.
895 // Sert a referencer une cellule non declaree dans la table et pourtant la.
896 // Par exemple pour la balise #POINTS on produit $Pile[$SP-n]['points']
897 // si la n-ieme boucle a un critere "recherche", car on sait qu'il a produit
898 // "SELECT XXXX AS points"
901 // http://code.spip.net/@rindex_pile
902 function rindex_pile($p, $champ, $motif) {
907 foreach ($p->boucles
[$b]->criteres
as $critere) {
908 if ($critere->op
== $motif) {
909 $p->code
= '$Pile[$SP' . (($n == 0) ?
"" : "-$n") .
916 $b = $p->boucles
[$b]->id_parent
;
919 // si on est hors d'une boucle de {recherche}, cette balise est vide
924 $p->interdire_scripts
= false;