a4250bbf71f216b6808e524093fb278f2a81d332
[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 $idb_origine = $idb;
102 $nom_champ_origine = $nom_champ;
103
104 $i = 0;
105 if (strlen($explicite)) {
106 // Recherche d'un champ dans un etage superieur
107 while (($idb !== $explicite) && ($idb !== '')) {
108 # spip_log("Cherchexpl: $nom_champ '$explicite' '$idb' '$i'");
109 $i++;
110 $idb = $boucles[$idb]->id_parent;
111 }
112 }
113
114 # spip_log("Cherche: $nom_champ a partir de '$idb'");
115 $nom_champ = strtolower($nom_champ);
116 $conditionnel = array();
117 // attention: entre la boucle nommee 0, "" et le tableau vide,
118 // il y a incoherences qu'il vaut mieux eviter
119 while (isset($boucles[$idb])) {
120 $joker = true;
121 // modifie $joker si tous les champs sont autorisés.
122 // $t = le select pour le champ, si on l'a trouvé (ou si joker)
123 // $c = le nom du champ demandé
124 list($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles, $joker);
125 if ($t) {
126 if ($select and !in_array($t, $boucles[$idb]->select)) {
127 $boucles[$idb]->select[] = $t;
128 }
129 // renseigner la boucle source de ce champ pour les traitements
130 $boucles[$idb_origine]->index_champ[$nom_champ_origine] = $idb;
131 $champ = '$Pile[$SP' . ($i ? "-$i" : "") . '][\'' . $c . '\']';
132 if (!$joker) {
133 return index_compose($conditionnel, $champ);
134 }
135
136 // tant que l'on trouve des tables avec joker, on continue
137 // avec la boucle parente et on conditionne à l'exécution
138 // la présence du champ. Si le champ existe à l'exécution
139 // dans une boucle, il est pris, sinon on le cherche dans le parent...
140 $conditionnel[] = "isset($champ)?$champ";
141 }
142
143 if ($remonte_pile) {
144 # spip_log("On remonte vers $i");
145 // Sinon on remonte d'un cran
146 $idb = $boucles[$idb]->id_parent;
147 $i++;
148 } else {
149 $idb = null;
150 }
151 }
152
153 # spip_log("Pas vu $nom_champ");
154 // esperons qu'il y sera
155 // ou qu'on a fourni une valeur par "defaut" plus pertinent
156 return index_compose($conditionnel, $defaut);
157 }
158
159 /**
160 * Reconstuire la cascade de condition de recherche d'un champ
161 *
162 * On ajoute la valeur finale par défaut pour les balises dont on ne saura
163 * qu'à l'exécution si elles sont definies ou non (boucle DATA)
164 *
165 * @param array $conditionnel Liste de codes PHP pour retrouver un champ
166 * @param string $defaut Valeur par défaut si aucun des moyens ne l'a trouvé
167 * @return string Code PHP complet de recherche d'un champ
168 */
169 function index_compose($conditionnel, $defaut) {
170 while ($c = array_pop($conditionnel)) {
171 // si on passe defaut = '', ne pas générer d'erreur de compilation.
172 $defaut = "($c:(" . ($defaut ? $defaut : "''") . "))";
173 }
174
175 return $defaut;
176 }
177
178 /**
179 * Cherche un champ dans une boucle
180 *
181 * Le champ peut être :
182 *
183 * - un alias d'un autre : il faut alors le calculer, éventuellement en
184 * construisant une jointure.
185 * - présent dans la table : on l'utilise
186 * - absent, mais le type de boucle l'autorise (joker des itérateurs DATA) :
187 * on l'utilise et lève le drapeau joker
188 * - absent, on cherche une jointure et on l'utilise si on en trouve.
189 *
190 * @todo
191 * Ici la recherche de jointure sur l'absence d'un champ ne cherche
192 * une jointure que si des jointures explicites sont demandées,
193 * et non comme à d'autres endroits sur toutes les jointures possibles.
194 * Il faut homogénéiser cela.
195 *
196 *
197 * @param string $idb Identifiant de la boucle
198 * @param string $nom_champ Nom du champ SQL cherché
199 * @param Boucle $boucles AST du squelette
200 * @param bool $joker
201 * Le champ peut-il être inconnu à la compilation ?
202 * Ce drapeau sera levé si c'est le cas.
203 * @return array
204 * Liste (Nom du champ véritable, nom du champ demandé).
205 * Le nom du champ véritable est une expression pour le SELECT de
206 * la boucle tel que "rubriques.titre" ou "mots.titre AS titre_mot".
207 * Les éléments de la liste sont vides si on ne trouve rien.
208 **/
209 function index_tables_en_pile($idb, $nom_champ, &$boucles, &$joker) {
210
211 $r = $boucles[$idb]->type_requete;
212 // boucle recursive, c'est foutu...
213 if ($r == TYPE_RECURSIF) {
214 return array();
215 }
216 if (!$r) {
217 $joker = false; // indiquer a l'appelant
218 # continuer pour chercher l'erreur suivante
219 return array("'#" . $r . ':' . $nom_champ . "'", '');
220 }
221
222 $desc = $boucles[$idb]->show;
223 // le nom du champ est il une exception de la table ? un alias ?
224 $excep = isset($GLOBALS['exceptions_des_tables'][$r]) ? $GLOBALS['exceptions_des_tables'][$r] : '';
225 if ($excep) {
226 $excep = isset($excep[$nom_champ]) ? $excep[$nom_champ] : '';
227 }
228 if ($excep) {
229 $joker = false; // indiquer a l'appelant
230 return index_exception($boucles[$idb], $desc, $nom_champ, $excep);
231 } // pas d'alias. Le champ existe t'il ?
232 else {
233 // le champ est réellement présent, on le prend.
234 if (isset($desc['field'][$nom_champ])) {
235 $t = $boucles[$idb]->id_table;
236 $joker = false; // indiquer a l'appelant
237 return array("$t.$nom_champ", $nom_champ);
238 }
239 // Tous les champs sont-ils acceptés ?
240 // Si oui, on retourne le champ, et on lève le flag joker
241 // C'est le cas des itérateurs DATA qui acceptent tout
242 // et testent la présence du champ à l'exécution et non à la compilation
243 // car ils ne connaissent pas ici leurs contenus.
244 elseif (/*$joker AND */
245 isset($desc['field']['*'])
246 ) {
247 $joker = true; // indiquer a l'appelant
248 return array($nom_champ, $nom_champ);
249 }
250 // pas d'alias, pas de champ, pas de joker...
251 // tenter via une jointure...
252 else {
253 $joker = false; // indiquer a l'appelant
254 // regarder si le champ est deja dans une jointure existante
255 // sinon, si il y a des joitures explicites, la construire
256 if (!$t = trouver_champ_exterieur($nom_champ, $boucles[$idb]->from, $boucles[$idb])) {
257 if ($boucles[$idb]->jointures_explicites) {
258 // [todo] Ne pas lancer que lorsque il y a des jointures explicites !!!!
259 // fonctionnel, il suffit d'utiliser $boucles[$idb]->jointures au lieu de jointures_explicites
260 // mais est-ce ce qu'on veut ?
261 $jointures = preg_split("/\s+/", $boucles[$idb]->jointures_explicites);
262 if ($cle = trouver_jointure_champ($nom_champ, $boucles[$idb], $jointures)) {
263 $t = trouver_champ_exterieur($nom_champ, $boucles[$idb]->from, $boucles[$idb]);
264 }
265 }
266 }
267 if ($t) {
268 // si on a trouvé une jointure possible, on fait comme
269 // si c'était une exception pour le champ demandé
270 return index_exception($boucles[$idb],
271 $desc,
272 $nom_champ,
273 array($t[1]['id_table'], reset($t[2])));
274 }
275
276 return array('', '');
277 }
278 }
279 }
280
281
282 /**
283 * Retrouve un alias d'un champ dans une boucle
284 *
285 * Référence à une entite SPIP alias d'un champ SQL.
286 * Ça peut même être d'un champ dans une jointure qu'il faut provoquer
287 * si ce n'est fait
288 *
289 * @param Boucle $boucle Boucle dont on prend un alias de champ
290 * @param array $desc Description de la table SQL de la boucle
291 * @param string $nom_champ Nom du champ original demandé
292 * @param array $excep
293 * Description de l'exception pour ce champ. Peut être :
294 *
295 * - string : nom du champ véritable dans la table
296 * - array :
297 * - liste (table, champ) indique que le véritable champ
298 * est dans une autre table et construit la jointure dessus
299 * - liste (table, champ, fonction) idem, mais en passant un
300 * nom de fonction qui s'occupera de créer la jointure.
301 * @return array
302 * Liste (nom du champ alias, nom du champ). Le nom du champ alias
303 * est une expression pour le SELECT de la boucle du style "mots.titre AS titre_mot"
304 **/
305 function index_exception(&$boucle, $desc, $nom_champ, $excep) {
306 static $trouver_table;
307 if (!$trouver_table) {
308 $trouver_table = charger_fonction('trouver_table', 'base');
309 }
310
311 if (is_array($excep)) {
312 // permettre aux plugins de gerer eux meme des jointures derogatoire ingerables
313 $t = null;
314 if (count($excep) == 3) {
315 $index_exception_derogatoire = array_pop($excep);
316 $t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep);
317 }
318 if ($t == null) {
319 list($e, $x) = $excep; #PHP4 affecte de gauche a droite
320 $excep = $x; #PHP5 de droite a gauche !
321 $j = $trouver_table($e, $boucle->sql_serveur);
322 if (!$j) {
323 return array('', '');
324 }
325 $e = $j['table'];
326 if (!$t = array_search($e, $boucle->from)) {
327 $k = $j['key']['PRIMARY KEY'];
328 if (strpos($k, ',')) {
329 $l = (preg_split('/\s*,\s*/', $k));
330 $k = $desc['key']['PRIMARY KEY'];
331 if (!in_array($k, $l)) {
332 spip_log("jointure impossible $e " . join(',', $l));
333
334 return array('', '');
335 }
336 }
337 $k = array($boucle->id_table, array($e), $k);
338 fabrique_jointures($boucle, array($k));
339 $t = array_search($e, $boucle->from);
340 }
341 }
342 } else {
343 $t = $boucle->id_table;
344 }
345 // demander a SQL de gerer le synonyme
346 // ca permet que excep soit dynamique (Cedric, 2/3/06)
347 if ($excep != $nom_champ) {
348 $excep .= ' AS ' . $nom_champ;
349 }
350
351 return array("$t.$excep", $nom_champ);
352 }
353
354 /**
355 * Demande le champ '$champ' dans la pile
356 *
357 * Le champ est cherché dans l'empilement de boucles, sinon dans la valeur
358 * par défaut (qui est l'environnement du squelette si on ne la précise pas).
359 *
360 * @api
361 * @param string $champ
362 * Champ recherché
363 * @param Champ $p
364 * AST au niveau de la balise
365 * @param null|string $defaut
366 * Code de la valeur par défaut si on ne trouve pas le champ dans une
367 * des boucles parentes. Sans précision, il sera pris dans l'environnement
368 * du squelette.
369 * Passer $defaut = '' pour ne pas prendre l'environnement.
370 * @param bool $remonte_pile
371 * Permettre de remonter dans la pile des boucles pour trouver le champ
372 * @return string
373 * Code PHP pour retrouver le champ
374 */
375 function champ_sql($champ, $p, $defaut = null, $remonte_pile = true) {
376 return index_pile($p->id_boucle, $champ, $p->boucles, $p->nom_boucle, $defaut, $remonte_pile);
377 }
378
379
380 /**
381 * Calcule et retourne le code PHP d'exécution d'une balise SPIP et des ses filtres
382 *
383 * Cette fonction qui sert d'API au compilateur demande à calculer
384 * le code PHP d'une balise, puis lui applique les filtres (automatiques
385 * et décrits dans le squelette)
386 *
387 * @uses calculer_balise()
388 * @uses applique_filtres()
389 *
390 * @param Champ $p
391 * AST au niveau de la balise
392 * @return string
393 * Code PHP pour d'exécution de la balise et de ses filtres
394 **/
395 function calculer_champ($p) {
396 $p = calculer_balise($p->nom_champ, $p);
397
398 return applique_filtres($p);
399 }
400
401
402 /**
403 * Calcule et retourne le code PHP d'exécution d'une balise SPIP
404 *
405 * Cette fonction qui sert d'API au compilateur demande à calculer
406 * le code PHP d'une balise (cette fonction ne calcule pas les éventuels
407 * filtres de la balise).
408 *
409 * Pour une balise nommmée `NOM`, elle demande à `charger_fonction()` de chercher
410 * s'il existe une fonction `balise_NOM` ou `balise_NOM_dist`
411 * éventuellement en chargeant le fichier `balise/NOM.php.`
412 *
413 * Si la balise est de la forme `PREFIXE_SUFFIXE` (cf `LOGO_*` et `URL_*`)
414 * elle fait de même avec juste le `PREFIXE`.
415 *
416 * S'il n'y a pas de fonction trouvée, on considère la balise comme une référence
417 * à une colonne de table SQL connue, sinon à l'environnement (cf. `calculer_balise_DEFAUT_dist()`).
418 *
419 * Les surcharges des colonnes SQL via charger_fonction sont donc possibles.
420 *
421 * @uses calculer_balise_DEFAUT_dist()
422 * Lorsqu'aucune fonction spécifique n'est trouvée.
423 * @see charger_fonction()
424 * Pour la recherche des fonctions de balises
425 *
426 * @param string $nom
427 * Nom de la balise
428 * @param Champ $p
429 * AST au niveau de la balise
430 * @return Champ
431 * Pile complétée par le code PHP pour l'exécution de la balise et de ses filtres
432 **/
433 function calculer_balise($nom, $p) {
434
435 // S'agit-t-il d'une balise_XXXX[_dist]() ?
436 if ($f = charger_fonction($nom, 'balise', true)) {
437 $p->balise_calculee = true;
438 $res = $f($p);
439 if ($res !== null and is_object($res)) {
440 return $res;
441 }
442 }
443
444 // Certaines des balises comportant un _ sont generiques
445 if ($f = strpos($nom, '_')
446 and $f = charger_fonction(substr($nom, 0, $f + 1), 'balise', true)
447 ) {
448 $res = $f($p);
449 if ($res !== null and is_object($res)) {
450 return $res;
451 }
452 }
453
454 $f = charger_fonction('DEFAUT', 'calculer_balise');
455
456 return $f($nom, $p);
457 }
458
459
460 /**
461 * Calcule et retourne le code PHP d'exécution d'une balise SPIP non déclarée
462 *
463 * Cette fonction demande à calculer le code PHP d'une balise qui
464 * n'a pas de fonction spécifique.
465 *
466 * On considère la balise comme une référence à une colonne de table SQL
467 * connue, sinon à l'environnement.
468 *
469 * @uses index_pile()
470 * Pour la recherche de la balise comme colonne SQL ou comme environnement
471 * @note
472 * Le texte de la balise est retourné si il ressemble à une couleur
473 * et qu'aucun champ correspondant n'a été trouvé, comme `#CCAABB`
474 *
475 * @param string $nom
476 * Nom de la balise
477 * @param Champ $p
478 * AST au niveau de la balise
479 * @return string
480 * Code PHP pour d'exécution de la balise et de ses filtres
481 **/
482 function calculer_balise_DEFAUT_dist($nom, $p) {
483
484 // ca pourrait etre un champ SQL homonyme,
485 $p->code = index_pile($p->id_boucle, $nom, $p->boucles, $p->nom_boucle);
486
487 // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour
488 // il faut recracher {...} quand ce n'est finalement pas des args
489 if ($p->fonctions and (!$p->fonctions[0][0]) and $p->fonctions[0][1]) {
490 $code = addslashes($p->fonctions[0][1]);
491 $p->code .= " . '$code'";
492 }
493
494 // ne pas passer le filtre securite sur les id_xxx
495 if (strpos($nom, 'ID_') === 0) {
496 $p->interdire_scripts = false;
497 }
498
499 // Compatibilite ascendante avec les couleurs html (#FEFEFE) :
500 // SI le champ SQL n'est pas trouve
501 // ET si la balise a une forme de couleur
502 // ET s'il n'y a ni filtre ni etoile
503 // ALORS retourner la couleur.
504 // Ca permet si l'on veut vraiment de recuperer [(#ACCEDE*)]
505 if (preg_match("/^[A-F]{1,6}$/i", $nom)
506 and !$p->etoile
507 and !$p->fonctions
508 ) {
509 $p->code = "'#$nom'";
510 $p->interdire_scripts = false;
511 }
512
513 return $p;
514 }
515
516
517 /** Code PHP d'exécution d'une balise dynamique */
518 define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s',
519 array(%s%s),
520 array(%s%s))");
521
522
523 /**
524 * Calcule le code PHP d'exécution d'une balise SPIP dynamique
525 *
526 * Calcule les balises dynamiques, notamment les `formulaire_*`.
527 *
528 * Inclut le fichier associé à son nom, qui contient la fonction homonyme
529 * donnant les arguments à chercher dans la pile, et qui sont donc compilés.
530 *
531 * On leur adjoint les arguments explicites de la balise (cf `#LOGIN{url}`)
532 * et d'éventuelles valeurs transmises d'autorité par la balise.
533 * (cf http://core.spip.net/issues/1728)
534 *
535 * La fonction `executer_balise_dynamique()` définie par la
536 * constante `CODE_EXECUTER_BALISE` recevra à l'exécution la valeur de tout ca.
537 *
538 * @uses collecter_balise_dynamique()
539 * Qui calcule le code d'exécution de chaque argument de la balise
540 * @see executer_balise_dynamique()
541 * Code PHP produit qui chargera les fonctions de la balise dynamique à l'exécution,
542 * appelée avec les arguments calculés.
543 * @param Champ $p
544 * AST au niveau de la balise
545 * @param string $nom
546 * Nom de la balise dynamique
547 * @param array $l
548 * Liste des noms d'arguments (balises) à collecter
549 * @param array $supp
550 * Liste de données supplémentaires à transmettre au code d'exécution.
551 * @return Champ
552 * Balise complétée de son code d'exécution
553 **/
554 function calculer_balise_dynamique($p, $nom, $l, $supp = array()) {
555
556 if (!balise_distante_interdite($p)) {
557 $p->code = "''";
558
559 return $p;
560 }
561 // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour
562 // il faut recracher {...} quand ce n'est finalement pas des args
563 if ($p->fonctions and (!$p->fonctions[0][0]) and $p->fonctions[0][1]) {
564 $p->fonctions = null;
565 }
566
567 if ($p->param and ($c = $p->param[0])) {
568 // liste d'arguments commence toujours par la chaine vide
569 array_shift($c);
570 // construire la liste d'arguments comme pour un filtre
571 $param = compose_filtres_args($p, $c, ',');
572 } else {
573 $param = "";
574 }
575 $collecte = collecter_balise_dynamique($l, $p, $nom);
576
577 $p->code = sprintf(CODE_EXECUTER_BALISE, $nom,
578 join(',', $collecte),
579 ($collecte ? $param : substr($param, 1)), # virer la virgule
580 memoriser_contexte_compil($p),
581 (!$supp ? '' : (', ' . join(',', $supp))));
582
583 $p->interdire_scripts = false;
584
585 return $p;
586 }
587
588
589 /**
590 * Construction du tableau des arguments d'une balise dynamique.
591 *
592 * Pour chaque argument (un nom de balise), crée le code PHP qui le calculera.
593 *
594 * @note
595 * Ces arguments peuvent être eux-même des balises (cf FORMULAIRE_SIGNATURE)
596 * mais gare au bouclage (on peut s'aider de `$nom` pour le réperer au besoin)
597 *
598 * En revanche ils n'ont pas de filtres, donc on appelle `calculer_balise()` qui
599 * ne s'occupe pas de ce qu'il y a dans `$p` (mais qui va y ecrire le code)
600 *
601 * @uses calculer_balise()
602 * Pour obtenir le code d'éxécution de chaque argument.
603 *
604 * @param array $l
605 * Liste des noms d'arguments (balises) à collecter (chaque argument
606 * de la balise dynamique est considéré comme étant un nom de balise)
607 * @param Champ $p
608 * AST au niveau de la balise
609 * @param string $nom
610 * Nom de la balise
611 * @return array
612 * Liste des codes PHP d'éxecution des balises collectées
613 **/
614 function collecter_balise_dynamique($l, &$p, $nom) {
615 $args = array();
616 foreach ($l as $c) {
617 $x = calculer_balise($c, $p);
618 $args[] = $x->code;
619 }
620
621 return $args;
622 }
623
624
625 /**
626 * Récuperer le nom du serveur
627 *
628 * Mais pas si c'est un serveur spécifique dérogatoire
629 *
630 * @param Champ $p
631 * AST positionné sur la balise
632 * @return string
633 * Nom de la connexion
634 **/
635 function trouver_nom_serveur_distant($p) {
636 $nom = $p->id_boucle;
637 if ($nom
638 and isset($p->boucles[$nom])
639 ) {
640 $s = $p->boucles[$nom]->sql_serveur;
641 if (strlen($s)
642 and strlen($serveur = strtolower($s))
643 and !in_array($serveur, $GLOBALS['exception_des_connect'])
644 ) {
645 return $serveur;
646 }
647 }
648
649 return "";
650 }
651
652
653 /**
654 * Teste si une balise est appliquée sur une base distante
655 *
656 * La fonction loge une erreur si la balise est utilisée sur une
657 * base distante et retourne false dans ce cas.
658 *
659 * @note
660 * Il faudrait savoir traiter les formulaires en local
661 * tout en appelant le serveur SQL distant.
662 * En attendant, cette fonction permet de refuser une authentification
663 * sur quelque-chose qui n'a rien a voir.
664 *
665 * @param Champ $p
666 * AST positionné sur la balise
667 * @return bool
668 *
669 * - true : La balise est autorisée
670 * - false : La balise est interdite car le serveur est distant
671 **/
672 function balise_distante_interdite($p) {
673 $nom = $p->id_boucle;
674
675 if ($nom and trouver_nom_serveur_distant($p)) {
676 spip_log($nom . ':' . $p->nom_champ . ' ' . _T('zbug_distant_interdit'));
677
678 return false;
679 }
680
681 return true;
682 }
683
684
685 //
686 // Traitements standard de divers champs
687 // definis par $table_des_traitements, cf. ecrire/public/interfaces
688 //
689 // http://code.spip.net/@champs_traitements
690 function champs_traitements($p) {
691
692 if (isset($GLOBALS['table_des_traitements'][$p->nom_champ])) {
693 $ps = $GLOBALS['table_des_traitements'][$p->nom_champ];
694 } else {
695 // quand on utilise un traitement catch-all *
696 // celui-ci ne s'applique pas sur les balises calculees qui peuvent gerer
697 // leur propre securite
698 if (!$p->balise_calculee) {
699 $ps = $GLOBALS['table_des_traitements']['*'];
700 } else {
701 $ps = false;
702 }
703 }
704
705 if (is_array($ps)) {
706 // Recuperer le type de boucle (articles, DATA) et la table SQL sur laquelle elle porte
707 $idb = index_boucle($p);
708 // si le champ a ete trouve dans une boucle parente sa source est renseignee ici
709 if (!empty($p->boucles[$idb]->index_champ[$p->nom_champ])) {
710 $idb = $p->boucles[$idb]->index_champ[$p->nom_champ];
711 }
712
713 // mais on peut aussi etre hors boucle. Se mefier.
714 $type_requete = isset($p->boucles[$idb]->type_requete) ? $p->boucles[$idb]->type_requete : false;
715 $table_sql = isset($p->boucles[$idb]->show['table_sql']) ? $p->boucles[$idb]->show['table_sql'] : false;
716
717 // bien prendre en compte les alias de boucles (hierarchie => rubrique, syndication => syncdic, etc.)
718 if ($type_requete and isset($GLOBALS['table_des_tables'][$type_requete])) {
719 $type_alias = $type_requete;
720 $type_requete = $GLOBALS['table_des_tables'][$type_requete];
721 } else {
722 $type_alias = false;
723 }
724
725 // le traitement peut n'etre defini que pour une table en particulier "spip_articles"
726 if ($table_sql and isset($ps[$table_sql])) {
727 $ps = $ps[$table_sql];
728 } // ou pour une boucle en particulier "DATA","articles"
729 elseif ($type_requete and isset($ps[$type_requete])) {
730 $ps = $ps[$type_requete];
731 } // ou pour une boucle utilisant un alias ("hierarchie")
732 elseif ($type_alias and isset($ps[$type_alias])) {
733 $ps = $ps[$type_alias];
734 } // ou pour indifféremment quelle que soit la boucle
735 elseif (isset($ps[0])) {
736 $ps = $ps[0];
737 } else {
738 $ps = false;
739 }
740 }
741
742 if (!$ps) {
743 return $p->code;
744 }
745
746 // Si une boucle DOCUMENTS{doublons} est presente dans le squelette,
747 // ou si in INCLURE contient {doublons}
748 // on insere une fonction de remplissage du tableau des doublons
749 // dans les filtres propre() ou typo()
750 // (qui traitent les raccourcis <docXX> referencant les docs)
751
752 if (isset($p->descr['documents'])
753 and
754 $p->descr['documents']
755 and (
756 (strpos($ps, 'propre') !== false)
757 or
758 (strpos($ps, 'typo') !== false)
759 )
760 ) {
761 $ps = 'traiter_doublons_documents($doublons, ' . $ps . ')';
762 }
763
764 // La protection des champs par |safehtml est assuree par les extensions
765 // dans la declaration des traitements des champs sensibles
766
767 // Remplacer enfin le placeholder %s par le vrai code de la balise
768 return str_replace('%s', $p->code, $ps);
769 }
770
771
772 //
773 // Appliquer les filtres a un champ [(#CHAMP|filtre1|filtre2)]
774 // retourne un code php compile exprimant ce champ filtre et securise
775 // - une etoile => pas de processeurs standards
776 // - deux etoiles => pas de securite non plus !
777 //
778 // http://code.spip.net/@applique_filtres
779 function applique_filtres($p) {
780
781 // Traitements standards (cf. supra)
782 if ($p->etoile == '') {
783 $code = champs_traitements($p);
784 } else {
785 $code = $p->code;
786 }
787
788 // Appliquer les filtres perso
789 if ($p->param) {
790 $code = compose_filtres($p, $code);
791 }
792
793 // S'il y a un lien avec la session, ajouter un code qui levera
794 // un drapeau dans la structure d'invalidation $Cache
795 if (isset($p->descr['session'])) {
796 $code = "invalideur_session(\$Cache, $code)";
797 }
798
799 $code = sandbox_composer_interdire_scripts($code, $p);
800
801 return $code;
802 }
803
804 // Cf. function pipeline dans ecrire/inc_utils.php
805 // http://code.spip.net/@compose_filtres
806 function compose_filtres(&$p, $code) {
807
808 $image_miette = false;
809 foreach ($p->param as $filtre) {
810 $fonc = array_shift($filtre);
811 if (!$fonc) {
812 continue;
813 } // normalement qu'au premier tour.
814 $is_filtre_image = ((substr($fonc, 0, 6) == 'image_') and $fonc != 'image_graver');
815 if ($image_miette and !$is_filtre_image) {
816 // il faut graver maintenant car apres le filtre en cours
817 // on est pas sur d'avoir encore le nom du fichier dans le pipe
818 $code = "filtrer('image_graver', $code)";
819 $image_miette = false;
820 }
821 // recuperer les arguments du filtre,
822 // a separer par "," ou ":" dans le cas du filtre "?{a,b}"
823 if ($fonc !== '?') {
824 $sep = ',';
825 } else {
826 $sep = ':';
827 // |?{a,b} *doit* avoir exactement 2 arguments ; on les force
828 if (count($filtre) != 2) {
829 $filtre = array(isset($filtre[0]) ? $filtre[0] : "", isset($filtre[1]) ? $filtre[1] : "");
830 }
831 }
832 $arglist = compose_filtres_args($p, $filtre, $sep);
833 $logique = filtre_logique($fonc, $code, substr($arglist, 1));
834 if ($logique) {
835 $code = $logique;
836 } else {
837 $code = sandbox_composer_filtre($fonc, $code, $arglist, $p);
838 if ($is_filtre_image) {
839 $image_miette = true;
840 }
841 }
842 }
843 // ramasser les images intermediaires inutiles et graver l'image finale
844 if ($image_miette) {
845 $code = "filtrer('image_graver',$code)";
846 }
847
848 return $code;
849 }
850
851 // Filtres et,ou,oui,non,sinon,xou,xor,and,or,not,yes
852 // et comparateurs
853 function filtre_logique($fonc, $code, $arg) {
854
855 switch (true) {
856 case in_array($fonc, $GLOBALS['table_criteres_infixes']):
857 return "($code $fonc $arg)";
858 case ($fonc == 'and') or ($fonc == 'et'):
859 return "((($code) AND ($arg)) ?' ' :'')";
860 case ($fonc == 'or') or ($fonc == 'ou'):
861 return "((($code) OR ($arg)) ?' ' :'')";
862 case ($fonc == 'xor') or ($fonc == 'xou'):
863 return "((($code) XOR ($arg)) ?' ' :'')";
864 case ($fonc == 'sinon'):
865 return "(((\$a = $code) OR (is_string(\$a) AND strlen(\$a))) ? \$a : $arg)";
866 case ($fonc == 'not') or ($fonc == 'non'):
867 return "(($code) ?'' :' ')";
868 case ($fonc == 'yes') or ($fonc == 'oui'):
869 return "(($code) ?' ' :'')";
870 }
871
872 return '';
873 }
874
875 // http://code.spip.net/@compose_filtres_args
876 function compose_filtres_args($p, $args, $sep) {
877 $arglist = "";
878 foreach ($args as $arg) {
879 $arglist .= $sep .
880 calculer_liste($arg, $p->descr, $p->boucles, $p->id_boucle);
881 }
882
883 return $arglist;
884 }
885
886
887 /**
888 * Réserve les champs necessaires à la comparaison avec le contexte donné par
889 * la boucle parente.
890 *
891 * Attention en recursif il faut les réserver chez soi-même ET chez sa maman
892 *
893 * @param string $idb Identifiant de la boucle
894 * @param string $nom_champ
895 * @param array $boucles AST du squelette
896 * @param null|string $defaut
897 * @return
898 **/
899 function calculer_argument_precedent($idb, $nom_champ, &$boucles, $defaut = null) {
900
901 // si recursif, forcer l'extraction du champ SQL mais ignorer le code
902 if ($boucles[$idb]->externe) {
903 index_pile($idb, $nom_champ, $boucles, '', $defaut);
904 // retourner $Pile[$SP] et pas $Pile[0] si recursion en 1ere boucle
905 // on ignore le defaut fourni dans ce cas
906 $defaut = "@\$Pile[\$SP]['$nom_champ']";
907 }
908
909 return index_pile($boucles[$idb]->id_parent, $nom_champ, $boucles, '', $defaut);
910 }
911
912 //
913 // Rechercher dans la pile des boucles actives celle ayant un critere
914 // comportant un certain $motif, et construire alors une reference
915 // a l'environnement de cette boucle, qu'on indexe avec $champ.
916 // Sert a referencer une cellule non declaree dans la table et pourtant la.
917 // Par exemple pour la balise #POINTS on produit $Pile[$SP-n]['points']
918 // si la n-ieme boucle a un critere "recherche", car on sait qu'il a produit
919 // "SELECT XXXX AS points"
920 //
921
922 // http://code.spip.net/@rindex_pile
923 function rindex_pile($p, $champ, $motif) {
924 $n = 0;
925 $b = $p->id_boucle;
926 $p->code = '';
927 while ($b != '') {
928 foreach ($p->boucles[$b]->criteres as $critere) {
929 if ($critere->op == $motif) {
930 $p->code = '$Pile[$SP' . (($n == 0) ? "" : "-$n") .
931 "]['$champ']";
932 $b = '';
933 break 2;
934 }
935 }
936 $n++;
937 $b = $p->boucles[$b]->id_parent;
938 }
939
940 // si on est hors d'une boucle de {recherche}, cette balise est vide
941 if (!$p->code) {
942 $p->code = "''";
943 }
944
945 $p->interdire_scripts = false;
946
947 return $p;
948 }