[SPIP] ~maj v3.2.9-->v3.2.11
[lhc/web/www.git] / www / ecrire / public / references.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2019 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
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 \***************************************************************************/
12
13 /**
14 * Fonctions de recherche et de reservation dans l'arborescence des boucles
15 *
16 * @package SPIP\Core\Compilateur\References
17 **/
18 if (!defined('_ECRIRE_INC_VERSION')) {
19 return;
20 }
21
22 /**
23 * Retrouver l'index de la boucle d'une balise
24 *
25 * Retrouve à quelle boucle appartient une balise, utile dans le cas
26 * où une référence explicite est demandée
27 *
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
30 *
31 * @example
32 * Dans une balise dynamique ou calculée :
33 * ```
34 * $idb = index_boucle($p);
35 * ```
36 *
37 * @param Champ $p AST au niveau de la balise
38 * @return string
39 *
40 * - Identifiant de la boucle possédant ce champ.
41 * - '' si une référence explicite incorrecte est envoyée
42 */
43 function index_boucle($p) {
44
45 $idb = $p->id_boucle;
46 $explicite = $p->nom_boucle;
47
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;
52 }
53 }
54
55 return $idb;
56 }
57
58 /**
59 * Retourne la position dans la pile d'un champ SQL
60 *
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
63 * (indiqué par $idb).
64 *
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)
68 *
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 *')
71 *
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)
83 * @param bool $select
84 * Pour ajouter au select de la boucle, par defaut true
85 * @return string
86 * Code PHP pour obtenir le champ SQL
87 */
88 function index_pile(
89 $idb,
90 $nom_champ,
91 &$boucles,
92 $explicite = '',
93 $defaut = null,
94 $remonte_pile = true,
95 $select = true
96 ) {
97 if (!is_string($defaut)) {
98 $defaut = '@$Pile[0][\'' . strtolower($nom_champ) . '\']';
99 }
100
101 $i = 0;
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'");
106 $i++;
107 $idb = $boucles[$idb]->id_parent;
108 }
109 }
110
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])) {
117 $joker = true;
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);
122 if ($t) {
123 if ($select and !in_array($t, $boucles[$idb]->select)) {
124 $boucles[$idb]->select[] = $t;
125 }
126 $champ = '$Pile[$SP' . ($i ? "-$i" : "") . '][\'' . $c . '\']';
127 if (!$joker) {
128 return index_compose($conditionnel, $champ);
129 }
130
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";
136 }
137
138 if ($remonte_pile) {
139 # spip_log("On remonte vers $i");
140 // Sinon on remonte d'un cran
141 $idb = $boucles[$idb]->id_parent;
142 $i++;
143 } else {
144 $idb = null;
145 }
146 }
147
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);
152 }
153
154 /**
155 * Reconstuire la cascade de condition de recherche d'un champ
156 *
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)
159 *
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
163 */
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 : "''") . "))";
168 }
169
170 return $defaut;
171 }
172
173 /**
174 * Cherche un champ dans une boucle
175 *
176 * Le champ peut être :
177 *
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.
184 *
185 * @todo
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.
190 *
191 *
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
195 * @param bool $joker
196 * Le champ peut-il être inconnu à la compilation ?
197 * Ce drapeau sera levé si c'est le cas.
198 * @return array
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.
203 **/
204 function index_tables_en_pile($idb, $nom_champ, &$boucles, &$joker) {
205
206 $r = $boucles[$idb]->type_requete;
207 // boucle recursive, c'est foutu...
208 if ($r == TYPE_RECURSIF) {
209 return array();
210 }
211 if (!$r) {
212 $joker = false; // indiquer a l'appelant
213 # continuer pour chercher l'erreur suivante
214 return array("'#" . $r . ':' . $nom_champ . "'", '');
215 }
216
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] : '';
220 if ($excep) {
221 $excep = isset($excep[$nom_champ]) ? $excep[$nom_champ] : '';
222 }
223 if ($excep) {
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 ?
227 else {
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);
233 }
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']['*'])
241 ) {
242 $joker = true; // indiquer a l'appelant
243 return array($nom_champ, $nom_champ);
244 }
245 // pas d'alias, pas de champ, pas de joker...
246 // tenter via une jointure...
247 else {
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]);
259 }
260 }
261 }
262 if ($t) {
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],
266 $desc,
267 $nom_champ,
268 array($t[1]['id_table'], reset($t[2])));
269 }
270
271 return array('', '');
272 }
273 }
274 }
275
276
277 /**
278 * Retrouve un alias d'un champ dans une boucle
279 *
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
282 * si ce n'est fait
283 *
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 :
289 *
290 * - string : nom du champ véritable dans la table
291 * - array :
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.
296 * @return array
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"
299 **/
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');
304 }
305
306 if (is_array($excep)) {
307 // permettre aux plugins de gerer eux meme des jointures derogatoire ingerables
308 $t = null;
309 if (count($excep) == 3) {
310 $index_exception_derogatoire = array_pop($excep);
311 $t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep);
312 }
313 if ($t == null) {
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);
317 if (!$j) {
318 return array('', '');
319 }
320 $e = $j['table'];
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));
328
329 return array('', '');
330 }
331 }
332 $k = array($boucle->id_table, array($e), $k);
333 fabrique_jointures($boucle, array($k));
334 $t = array_search($e, $boucle->from);
335 }
336 }
337 } else {
338 $t = $boucle->id_table;
339 }
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;
344 }
345
346 return array("$t.$excep", $nom_champ);
347 }
348
349 /**
350 * Demande le champ '$champ' dans la pile
351 *
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).
354 *
355 * @api
356 * @param string $champ
357 * Champ recherché
358 * @param Champ $p
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
363 * du squelette.
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
367 * @return string
368 * Code PHP pour retrouver le champ
369 */
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);
372 }
373
374
375 /**
376 * Calcule et retourne le code PHP d'exécution d'une balise SPIP et des ses filtres
377 *
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)
381 *
382 * @uses calculer_balise()
383 * @uses applique_filtres()
384 *
385 * @param Champ $p
386 * AST au niveau de la balise
387 * @return string
388 * Code PHP pour d'exécution de la balise et de ses filtres
389 **/
390 function calculer_champ($p) {
391 $p = calculer_balise($p->nom_champ, $p);
392
393 return applique_filtres($p);
394 }
395
396
397 /**
398 * Calcule et retourne le code PHP d'exécution d'une balise SPIP
399 *
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).
403 *
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.`
407 *
408 * Si la balise est de la forme `PREFIXE_SUFFIXE` (cf `LOGO_*` et `URL_*`)
409 * elle fait de même avec juste le `PREFIXE`.
410 *
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()`).
413 *
414 * Les surcharges des colonnes SQL via charger_fonction sont donc possibles.
415 *
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
420 *
421 * @param string $nom
422 * Nom de la balise
423 * @param Champ $p
424 * AST au niveau de la balise
425 * @return Champ
426 * Pile complétée par le code PHP pour l'exécution de la balise et de ses filtres
427 **/
428 function calculer_balise($nom, $p) {
429
430 // S'agit-t-il d'une balise_XXXX[_dist]() ?
431 if ($f = charger_fonction($nom, 'balise', true)) {
432 $p->balise_calculee = true;
433 $res = $f($p);
434 if ($res !== null and is_object($res)) {
435 return $res;
436 }
437 }
438
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)
442 ) {
443 $res = $f($p);
444 if ($res !== null and is_object($res)) {
445 return $res;
446 }
447 }
448
449 $f = charger_fonction('DEFAUT', 'calculer_balise');
450
451 return $f($nom, $p);
452 }
453
454
455 /**
456 * Calcule et retourne le code PHP d'exécution d'une balise SPIP non déclarée
457 *
458 * Cette fonction demande à calculer le code PHP d'une balise qui
459 * n'a pas de fonction spécifique.
460 *
461 * On considère la balise comme une référence à une colonne de table SQL
462 * connue, sinon à l'environnement.
463 *
464 * @uses index_pile()
465 * Pour la recherche de la balise comme colonne SQL ou comme environnement
466 * @note
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`
469 *
470 * @param string $nom
471 * Nom de la balise
472 * @param Champ $p
473 * AST au niveau de la balise
474 * @return string
475 * Code PHP pour d'exécution de la balise et de ses filtres
476 **/
477 function calculer_balise_DEFAUT_dist($nom, $p) {
478
479 // ca pourrait etre un champ SQL homonyme,
480 $p->code = index_pile($p->id_boucle, $nom, $p->boucles, $p->nom_boucle);
481
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'";
487 }
488
489 // ne pas passer le filtre securite sur les id_xxx
490 if (strpos($nom, 'ID_') === 0) {
491 $p->interdire_scripts = false;
492 }
493
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)
501 and !$p->etoile
502 and !$p->fonctions
503 ) {
504 $p->code = "'#$nom'";
505 $p->interdire_scripts = false;
506 }
507
508 return $p;
509 }
510
511
512 /** Code PHP d'exécution d'une balise dynamique */
513 define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s',
514 array(%s%s),
515 array(%s%s))");
516
517
518 /**
519 * Calcule le code PHP d'exécution d'une balise SPIP dynamique
520 *
521 * Calcule les balises dynamiques, notamment les `formulaire_*`.
522 *
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.
525 *
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)
529 *
530 * La fonction `executer_balise_dynamique()` définie par la
531 * constante `CODE_EXECUTER_BALISE` recevra à l'exécution la valeur de tout ca.
532 *
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.
538 * @param Champ $p
539 * AST au niveau de la balise
540 * @param string $nom
541 * Nom de la balise dynamique
542 * @param array $l
543 * Liste des noms d'arguments (balises) à collecter
544 * @param array $supp
545 * Liste de données supplémentaires à transmettre au code d'exécution.
546 * @return Champ
547 * Balise complétée de son code d'exécution
548 **/
549 function calculer_balise_dynamique($p, $nom, $l, $supp = array()) {
550
551 if (!balise_distante_interdite($p)) {
552 $p->code = "''";
553
554 return $p;
555 }
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;
560 }
561
562 if ($p->param and ($c = $p->param[0])) {
563 // liste d'arguments commence toujours par la chaine vide
564 array_shift($c);
565 // construire la liste d'arguments comme pour un filtre
566 $param = compose_filtres_args($p, $c, ',');
567 } else {
568 $param = "";
569 }
570 $collecte = collecter_balise_dynamique($l, $p, $nom);
571
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))));
577
578 $p->interdire_scripts = false;
579
580 return $p;
581 }
582
583
584 /**
585 * Construction du tableau des arguments d'une balise dynamique.
586 *
587 * Pour chaque argument (un nom de balise), crée le code PHP qui le calculera.
588 *
589 * @note
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)
592 *
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)
595 *
596 * @uses calculer_balise()
597 * Pour obtenir le code d'éxécution de chaque argument.
598 *
599 * @param array $l
600 * Liste des noms d'arguments (balises) à collecter (chaque argument
601 * de la balise dynamique est considéré comme étant un nom de balise)
602 * @param Champ $p
603 * AST au niveau de la balise
604 * @param string $nom
605 * Nom de la balise
606 * @return array
607 * Liste des codes PHP d'éxecution des balises collectées
608 **/
609 function collecter_balise_dynamique($l, &$p, $nom) {
610 $args = array();
611 foreach ($l as $c) {
612 $x = calculer_balise($c, $p);
613 $args[] = $x->code;
614 }
615
616 return $args;
617 }
618
619
620 /**
621 * Récuperer le nom du serveur
622 *
623 * Mais pas si c'est un serveur spécifique dérogatoire
624 *
625 * @param Champ $p
626 * AST positionné sur la balise
627 * @return string
628 * Nom de la connexion
629 **/
630 function trouver_nom_serveur_distant($p) {
631 $nom = $p->id_boucle;
632 if ($nom
633 and isset($p->boucles[$nom])
634 ) {
635 $s = $p->boucles[$nom]->sql_serveur;
636 if (strlen($s)
637 and strlen($serveur = strtolower($s))
638 and !in_array($serveur, $GLOBALS['exception_des_connect'])
639 ) {
640 return $serveur;
641 }
642 }
643
644 return "";
645 }
646
647
648 /**
649 * Teste si une balise est appliquée sur une base distante
650 *
651 * La fonction loge une erreur si la balise est utilisée sur une
652 * base distante et retourne false dans ce cas.
653 *
654 * @note
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.
659 *
660 * @param Champ $p
661 * AST positionné sur la balise
662 * @return bool
663 *
664 * - true : La balise est autorisée
665 * - false : La balise est interdite car le serveur est distant
666 **/
667 function balise_distante_interdite($p) {
668 $nom = $p->id_boucle;
669
670 if ($nom and trouver_nom_serveur_distant($p)) {
671 spip_log($nom . ':' . $p->nom_champ . ' ' . _T('zbug_distant_interdit'));
672
673 return false;
674 }
675
676 return true;
677 }
678
679
680 //
681 // Traitements standard de divers champs
682 // definis par $table_des_traitements, cf. ecrire/public/interfaces
683 //
684 // http://code.spip.net/@champs_traitements
685 function champs_traitements($p) {
686
687 if (isset($GLOBALS['table_des_traitements'][$p->nom_champ])) {
688 $ps = $GLOBALS['table_des_traitements'][$p->nom_champ];
689 } else {
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']['*'];
695 } else {
696 $ps = false;
697 }
698 }
699
700 if (is_array($ps)) {
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;
706
707 // bien prendre en compte les alias de boucles (hierarchie => rubrique, syndication => syncdic, etc.)
708 if ($type_requete and isset($GLOBALS['table_des_tables'][$type_requete])) {
709 $type_requete = $GLOBALS['table_des_tables'][$type_requete];
710 }
711
712 // le traitement peut n'etre defini que pour une table en particulier "spip_articles"
713 if ($table_sql and isset($ps[$table_sql])) {
714 $ps = $ps[$table_sql];
715 } // ou pour une boucle en particulier "DATA","articles"
716 elseif ($type_requete and isset($ps[$type_requete])) {
717 $ps = $ps[$type_requete];
718 } // ou pour indifféremment quelle que soit la boucle
719 elseif (isset($ps[0])) {
720 $ps = $ps[0];
721 } else {
722 $ps = false;
723 }
724 }
725
726 if (!$ps) {
727 return $p->code;
728 }
729
730 // Si une boucle DOCUMENTS{doublons} est presente dans le squelette,
731 // ou si in INCLURE contient {doublons}
732 // on insere une fonction de remplissage du tableau des doublons
733 // dans les filtres propre() ou typo()
734 // (qui traitent les raccourcis <docXX> referencant les docs)
735
736 if (isset($p->descr['documents'])
737 and
738 $p->descr['documents']
739 and (
740 (strpos($ps, 'propre') !== false)
741 or
742 (strpos($ps, 'typo') !== false)
743 )
744 ) {
745 $ps = 'traiter_doublons_documents($doublons, ' . $ps . ')';
746 }
747
748 // La protection des champs par |safehtml est assuree par les extensions
749 // dans la declaration des traitements des champs sensibles
750
751 // Remplacer enfin le placeholder %s par le vrai code de la balise
752 return str_replace('%s', $p->code, $ps);
753 }
754
755
756 //
757 // Appliquer les filtres a un champ [(#CHAMP|filtre1|filtre2)]
758 // retourne un code php compile exprimant ce champ filtre et securise
759 // - une etoile => pas de processeurs standards
760 // - deux etoiles => pas de securite non plus !
761 //
762 // http://code.spip.net/@applique_filtres
763 function applique_filtres($p) {
764
765 // Traitements standards (cf. supra)
766 if ($p->etoile == '') {
767 $code = champs_traitements($p);
768 } else {
769 $code = $p->code;
770 }
771
772 // Appliquer les filtres perso
773 if ($p->param) {
774 $code = compose_filtres($p, $code);
775 }
776
777 // S'il y a un lien avec la session, ajouter un code qui levera
778 // un drapeau dans la structure d'invalidation $Cache
779 if (isset($p->descr['session'])) {
780 $code = "invalideur_session(\$Cache, $code)";
781 }
782
783 $code = sandbox_composer_interdire_scripts($code, $p);
784
785 return $code;
786 }
787
788 // Cf. function pipeline dans ecrire/inc_utils.php
789 // http://code.spip.net/@compose_filtres
790 function compose_filtres(&$p, $code) {
791
792 $image_miette = false;
793 foreach ($p->param as $filtre) {
794 $fonc = array_shift($filtre);
795 if (!$fonc) {
796 continue;
797 } // normalement qu'au premier tour.
798 $is_filtre_image = ((substr($fonc, 0, 6) == 'image_') and $fonc != 'image_graver');
799 if ($image_miette and !$is_filtre_image) {
800 // il faut graver maintenant car apres le filtre en cours
801 // on est pas sur d'avoir encore le nom du fichier dans le pipe
802 $code = "filtrer('image_graver', $code)";
803 $image_miette = false;
804 }
805 // recuperer les arguments du filtre,
806 // a separer par "," ou ":" dans le cas du filtre "?{a,b}"
807 if ($fonc !== '?') {
808 $sep = ',';
809 } else {
810 $sep = ':';
811 // |?{a,b} *doit* avoir exactement 2 arguments ; on les force
812 if (count($filtre) != 2) {
813 $filtre = array(isset($filtre[0]) ? $filtre[0] : "", isset($filtre[1]) ? $filtre[1] : "");
814 }
815 }
816 $arglist = compose_filtres_args($p, $filtre, $sep);
817 $logique = filtre_logique($fonc, $code, substr($arglist, 1));
818 if ($logique) {
819 $code = $logique;
820 } else {
821 $code = sandbox_composer_filtre($fonc, $code, $arglist, $p);
822 if ($is_filtre_image) {
823 $image_miette = true;
824 }
825 }
826 }
827 // ramasser les images intermediaires inutiles et graver l'image finale
828 if ($image_miette) {
829 $code = "filtrer('image_graver',$code)";
830 }
831
832 return $code;
833 }
834
835 // Filtres et,ou,oui,non,sinon,xou,xor,and,or,not,yes
836 // et comparateurs
837 function filtre_logique($fonc, $code, $arg) {
838
839 switch (true) {
840 case in_array($fonc, $GLOBALS['table_criteres_infixes']):
841 return "($code $fonc $arg)";
842 case ($fonc == 'and') or ($fonc == 'et'):
843 return "((($code) AND ($arg)) ?' ' :'')";
844 case ($fonc == 'or') or ($fonc == 'ou'):
845 return "((($code) OR ($arg)) ?' ' :'')";
846 case ($fonc == 'xor') or ($fonc == 'xou'):
847 return "((($code) XOR ($arg)) ?' ' :'')";
848 case ($fonc == 'sinon'):
849 return "(((\$a = $code) OR (is_string(\$a) AND strlen(\$a))) ? \$a : $arg)";
850 case ($fonc == 'not') or ($fonc == 'non'):
851 return "(($code) ?'' :' ')";
852 case ($fonc == 'yes') or ($fonc == 'oui'):
853 return "(($code) ?' ' :'')";
854 }
855
856 return '';
857 }
858
859 // http://code.spip.net/@compose_filtres_args
860 function compose_filtres_args($p, $args, $sep) {
861 $arglist = "";
862 foreach ($args as $arg) {
863 $arglist .= $sep .
864 calculer_liste($arg, $p->descr, $p->boucles, $p->id_boucle);
865 }
866
867 return $arglist;
868 }
869
870
871 /**
872 * Réserve les champs necessaires à la comparaison avec le contexte donné par
873 * la boucle parente.
874 *
875 * Attention en recursif il faut les réserver chez soi-même ET chez sa maman
876 *
877 * @param string $idb Identifiant de la boucle
878 * @param string $nom_champ
879 * @param array $boucles AST du squelette
880 * @param null|string $defaut
881 * @return
882 **/
883 function calculer_argument_precedent($idb, $nom_champ, &$boucles, $defaut = null) {
884
885 // si recursif, forcer l'extraction du champ SQL mais ignorer le code
886 if ($boucles[$idb]->externe) {
887 index_pile($idb, $nom_champ, $boucles, '', $defaut);
888 // retourner $Pile[$SP] et pas $Pile[0] si recursion en 1ere boucle
889 // on ignore le defaut fourni dans ce cas
890 $defaut = "@\$Pile[\$SP]['$nom_champ']";
891 }
892
893 return index_pile($boucles[$idb]->id_parent, $nom_champ, $boucles, '', $defaut);
894 }
895
896 //
897 // Rechercher dans la pile des boucles actives celle ayant un critere
898 // comportant un certain $motif, et construire alors une reference
899 // a l'environnement de cette boucle, qu'on indexe avec $champ.
900 // Sert a referencer une cellule non declaree dans la table et pourtant la.
901 // Par exemple pour la balise #POINTS on produit $Pile[$SP-n]['points']
902 // si la n-ieme boucle a un critere "recherche", car on sait qu'il a produit
903 // "SELECT XXXX AS points"
904 //
905
906 // http://code.spip.net/@rindex_pile
907 function rindex_pile($p, $champ, $motif) {
908 $n = 0;
909 $b = $p->id_boucle;
910 $p->code = '';
911 while ($b != '') {
912 foreach ($p->boucles[$b]->criteres as $critere) {
913 if ($critere->op == $motif) {
914 $p->code = '$Pile[$SP' . (($n == 0) ? "" : "-$n") .
915 "]['$champ']";
916 $b = '';
917 break 2;
918 }
919 }
920 $n++;
921 $b = $p->boucles[$b]->id_parent;
922 }
923
924 // si on est hors d'une boucle de {recherche}, cette balise est vide
925 if (!$p->code) {
926 $p->code = "''";
927 }
928
929 $p->interdire_scripts = false;
930
931 return $p;
932 }