source: spip-zone/_plugins_/iterateurs/public/references.php @ 82836

Last change on this file since 82836 was 82836, checked in by fil@…, 7 years ago

suivre http://core.spip.org/projects/spip/repository/revisions/21376 ($Pile en premier argument sur le filtre |set{})

File size: 17.6 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2014                                                *
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// fonctions de recherche et de reservation
14// dans l'arborescence des boucles
15
16if (!defined('_ECRIRE_INC_VERSION')) return;
17
18// index_pile retourne la position dans la pile du champ SQL $nom_champ
19// en prenant la boucle la plus proche du sommet de pile (indique par $idb).
20// Si on ne trouve rien, on considere que ca doit provenir du contexte
21// (par l'URL ou l'include) qui a ete recopie dans Pile[0]
22// (un essai d'affinage a debouche sur un bug vicieux)
23// Si ca reference un champ SQL, on le memorise dans la structure $boucles
24// afin de construire un requete SQL minimale (plutot qu'un brutal 'SELECT *')
25
26// http://doc.spip.org/@index_pile
27function index_pile($idb, $nom_champ, &$boucles, $explicite='') {
28
29        $i = 0;
30        if (strlen($explicite)) {
31        // Recherche d'un champ dans un etage superieur
32          while (($idb !== $explicite) && ($idb !=='')) {
33#               spip_log("Cherchexpl: $nom_champ '$explicite' '$idb' '$i'");
34                        $i++;
35                        $idb = $boucles[$idb]->id_parent;
36                }
37        }
38
39#       spip_log("Cherche: $nom_champ a partir de '$idb'");
40        $nom_champ = strtolower($nom_champ);
41        // attention: entre la boucle nommee 0, "" et le tableau vide,
42        // il y a incoherences qu'il vaut mieux eviter
43        while (isset($boucles[$idb])) {
44                list ($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles);
45
46                if ($t) {
47                  if (!in_array($t, $boucles[$idb]->select)) {
48                    $boucles[$idb]->select[] = $t;
49                  }
50                  return '$Pile[$SP' . ($i ? "-$i" : "") . '][\'' . $c . '\']';
51                }
52#               spip_log("On remonte vers $i");
53                // Sinon on remonte d'un cran
54                $idb = $boucles[$idb]->id_parent;
55                $i++;
56        }
57
58#       spip_log("Pas vu $nom_champ");
59        // esperons qu'il y sera
60        return('@$Pile[0][\''. strtolower($nom_champ) . '\']');
61}
62
63// http://doc.spip.org/@index_tables_en_pile
64function index_tables_en_pile($idb, $nom_champ, &$boucles) {
65        global $exceptions_des_tables;
66
67        $r = $boucles[$idb]->type_requete;
68
69        if ($r == 'boucle') return array();
70        if (!$r) {
71                # continuer pour chercher l'erreur suivante
72                return  array("'#" . $r . ':' . $nom_champ . "'",'');
73        }
74
75        $desc = $boucles[$idb]->show;
76        $excep = isset($exceptions_des_tables[$r]) ? $exceptions_des_tables[$r] : '';
77        if ($excep)
78                $excep = isset($excep[$nom_champ]) ? $excep[$nom_champ] : '';
79        if ($excep) {
80          return index_exception($boucles[$idb], $desc, $nom_champ, $excep);
81        } else {
82                if (isset($desc['field'][$nom_champ])) {
83                        $t = $boucles[$idb]->id_table;
84                        return array("$t.$nom_champ", $nom_champ);
85                } else if (isset($desc['field']['*'])) {
86                        $t = $boucles[$idb]->id_table;
87                        return array($nom_champ, $nom_champ);
88                } else {
89                  if ($boucles[$idb]->jointures_explicites) {
90                    $t = trouver_champ_exterieur($nom_champ, 
91                                                 $boucles[$idb]->jointures,
92                                                 $boucles[$idb]);
93                    if ($t) 
94                        return index_exception($boucles[$idb], 
95                                               $desc,
96                                               $nom_champ,
97                                               array($t[1]['id_table'], $nom_champ));
98                  }
99                  return array('','');
100                }
101        }
102}
103
104// Reference a une entite SPIP alias d'un champ SQL
105// Ca peut meme etre d'un champ dans une jointure
106// qu'il faut provoquer si ce n'est fait
107
108// http://doc.spip.org/@index_exception
109function index_exception(&$boucle, $desc, $nom_champ, $excep)
110{
111        static $trouver_table;
112        if (!$trouver_table)
113                $trouver_table = charger_fonction('trouver_table', 'base');
114
115        if (is_array($excep)) {
116                // permettre aux plugins de gerer eux meme des jointures derogatoire ingerables
117                $t = NULL;
118                if (count($excep)==3){
119                        $index_exception_derogatoire = array_pop($excep);
120                        $t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep);
121                }
122                if ($t == NULL) {
123                        list($e, $x) = $excep;  #PHP4 affecte de gauche a droite
124                        $excep = $x;            #PHP5 de droite a gauche !
125                        $j = $trouver_table($e, $boucle->sql_serveur);
126                        if (!$j) return array('','');
127                        $e = $j['table'];
128                        if (!$t = array_search($e, $boucle->from)) {
129                                $k = $j['key']['PRIMARY KEY'];
130                                if (strpos($k,',')) {
131                                        $l = (preg_split('/\s*,\s*/', $k));
132                                        $k = $desc['key']['PRIMARY KEY'];
133                                        if (!in_array($k, $l)) {
134                                                spip_log("jointure impossible $e " . join(',', $l));
135                                                return array('','');
136                                        }
137                                }
138                                $k = array($boucle->id_table, array($e), $k);
139                                fabrique_jointures($boucle, array($k));
140                                $t = array_search($e, $boucle->from);
141                        }
142                }
143        }
144        else $t = $boucle->id_table;
145        // demander a SQL de gerer le synonyme
146        // ca permet que excep soit dynamique (Cedric, 2/3/06)
147        if ($excep != $nom_champ) $excep .= ' AS '. $nom_champ;
148        return array("$t.$excep", $nom_champ);
149}
150
151
152// cette fonction sert d'API pour demander le champ '$champ' dans la pile
153// http://doc.spip.org/@champ_sql
154function champ_sql($champ, $p) {
155        return index_pile($p->id_boucle, $champ, $p->boucles, $p->nom_boucle);
156}
157
158// cette fonction sert d'API pour demander une balise Spip avec filtres
159
160// http://doc.spip.org/@calculer_champ
161function calculer_champ($p) {
162        $p = calculer_balise($p->nom_champ, $p);
163        return applique_filtres($p);
164}
165
166// Cette fonction sert d'API pour demander une balise SPIP sans filtres.
167// Pour une balise nommmee NOM, elle demande a charger_fonction de chercher
168// s'il existe une fonction balise_NOM ou balise_NOM_dist
169// eventuellement en chargeant le fichier balise/NOM.php.
170// Si la balise est de la forme PREFIXE_SUFFIXE (cf LOGO_* et URL_*)
171// elle fait de meme avec juste le PREFIXE.
172// Si pas de fonction, c'est une reference a une colonne de table SQL connue.
173// Les surcharges des colonnes SQL via charger_fonction sont donc possibles.
174
175// http://doc.spip.org/@calculer_balise
176function calculer_balise($nom, $p) {
177
178        // S'agit-t-il d'une balise_XXXX[_dist]() ?
179        if ($f = charger_fonction($nom, 'balise', true)) {
180                $res = $f($p);
181                if ($res !== NULL)
182                        return $res;
183        }
184
185        // Certaines des balises comportant un _ sont generiques
186        if ($f = strpos($nom, '_')
187        AND $f = charger_fonction(substr($nom,0,$f+1), 'balise', true)) {
188                $res = $f($p);
189                if ($res !== NULL)
190                        return $res;
191        }
192
193        $f = charger_fonction('DEFAUT', 'calculer_balise');
194
195        return $f($nom, $p);
196}
197
198function calculer_balise_DEFAUT_dist($nom, $p) {
199
200        // ca pourrait etre un champ SQL homonyme,
201        $p->code = index_pile($p->id_boucle, $nom, $p->boucles, $p->nom_boucle);
202
203        // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour
204        // il faut recracher {...} quand ce n'est finalement pas des args
205        if ($p->fonctions AND (!$p->fonctions[0][0]) AND $p->fonctions[0][1]) {
206                $code = addslashes($p->fonctions[0][1]);
207                $p->code .= " . '$code'";
208        }
209
210        // ne pas passer le filtre securite sur les id_xxx
211        if (strpos($nom, 'ID_') === 0)
212                $p->interdire_scripts = false;
213
214        // Compatibilite ascendante avec les couleurs html (#FEFEFE) :
215        // SI le champ SQL n'est pas trouve
216        // ET si la balise a une forme de couleur
217        // ET s'il n'y a ni filtre ni etoile
218        // ALORS retourner la couleur.
219        // Ca permet si l'on veut vraiment de recuperer [(#ACCEDE*)]
220        if (preg_match("/^[A-F]{1,6}$/i", $nom)
221        AND !$p->etoile
222        AND !$p->fonctions) {
223                $p->code = "'#$nom'";
224                $p->interdire_scripts = false;
225        }
226
227        return $p;
228}
229
230
231//
232// Traduction des balises dynamiques, notamment les "formulaire_*"
233// Inclusion du fichier associe a son nom, qui contient la fonction homonyme
234// donnant les arguments a chercher dans la pile, et qui sont donc compiles.
235// On leur adjoint les arguments explicites de la balise (cf #LOGIN{url})
236// et d'eventuelles valeurs transmises d'autorite par la balise.
237// (cf http://trac.rezo.net/trac/spip/ticket/1728)
238// La fonction nommee ci-dessous recevra a l'execution la valeur de tout ca.
239
240define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s',
241        array(%s%s),
242        array(%s%s))");
243
244// http://doc.spip.org/@calculer_balise_dynamique
245function calculer_balise_dynamique($p, $nom, $l, $supp=array()) {
246
247        if (!balise_distante_interdite($p)) {
248                $p->code = "''";
249                return $p;
250        }
251        // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour
252        // il faut recracher {...} quand ce n'est finalement pas des args
253        if ($p->fonctions AND (!$p->fonctions[0][0]) AND $p->fonctions[0][1]) {
254                $p->fonctions = null;
255        }
256
257        if ($p->param AND ($c = $p->param[0])) {
258                // liste d'arguments commence toujours par la chaine vide
259                array_shift($c);
260                // construire la liste d'arguments comme pour un filtre
261                $param = compose_filtres_args($p, $c, ',');
262        } else  $param = "";
263        $collecte = collecter_balise_dynamique($l, $p, $nom);
264
265        $p->code = sprintf(CODE_EXECUTER_BALISE, $nom,
266                join(',', $collecte),
267                ($collecte ? $param : substr($param,1)), # virer la virgule
268                memoriser_contexte_compil($p),
269                (!$supp ? '' : (', ' . join(',', $supp))));
270
271        $p->interdire_scripts = false;
272        return $p;
273}
274
275// Construction du tableau des arguments d'une balise dynamique.
276// Ces arguments peuvent etre eux-meme des balises (cf FORMULAIRE_SIGNATURE)
277// mais gare au bouclage (on peut s'aider de $nom pour le reperer au besoin)
278// En revanche ils n'ont pas de filtres, donc on appelle calculer_balise qui
279// ne s'occupe pas de ce qu'il y a dans $p (mais qui va y ecrire le code)
280
281// http://doc.spip.org/@collecter_balise_dynamique
282function collecter_balise_dynamique($l, &$p, $nom) {
283        $args = array();
284        foreach($l as $c) { $x = calculer_balise($c, $p); $args[] = $x->code;}
285        return $args;
286}
287
288
289// il faudrait savoir traiter les formulaires en local
290// tout en appelant le serveur SQL distant.
291// En attendant, cette fonction permet de refuser une authentification
292// sur qqch qui n'a rien a voir.
293
294// http://doc.spip.org/@balise_distante_interdite
295function balise_distante_interdite($p) {
296        $nom = $p->id_boucle;
297
298        if ($nom 
299                AND $p->boucles[$nom]->sql_serveur
300                AND !in_array($p->boucles[$nom]->sql_serveur,$GLOBALS['exception_des_connect'])) {
301                spip_log( $nom .':' . $p->nom_champ .' '._T('zbug_distant_interdit'));
302                return false;
303        }
304        return true;
305}
306
307
308//
309// Traitements standard de divers champs
310// definis par $table_des_traitements, cf. ecrire/public/interfaces
311//
312// http://doc.spip.org/@champs_traitements
313function champs_traitements ($p) {
314        global $table_des_traitements;
315
316        if (!isset($table_des_traitements[$p->nom_champ]))
317                return $p->code;
318        $ps = $table_des_traitements[$p->nom_champ];
319        if (is_array($ps)) {
320          // new style
321
322                if ($p->nom_boucle)
323                        $type = $p->boucles[$p->nom_boucle]->type_requete;
324                else
325                        $type = $p->type_requete;
326                // le traitement peut n'etre defini que pour une table en particulier
327                if (isset($ps[$type]))
328                        $ps = $ps[$type];
329                elseif(isset($ps[0]))
330                        $ps = $ps[0];
331                else $ps=false;
332        }
333
334        if (!$ps) return $p->code;
335
336        // Si une boucle DOCUMENTS{doublons} est presente dans le squelette,
337        // ou si in INCLURE contient {doublons}
338        // on insere une fonction de remplissage du tableau des doublons
339        // dans les filtres propre() ou typo()
340        // (qui traitent les raccourcis <docXX> referencant les docs)
341
342        if (isset($p->descr['documents']) 
343        AND 
344          $p->descr['documents']
345        AND (
346                (strpos($ps,'propre') !== false)
347                OR
348                (strpos($ps,'typo') !== false)
349        ))
350                $ps = 'traiter_doublons_documents($doublons, '.$ps.')';
351
352        // Passer |safehtml sur les boucles "sensibles"
353        // sauf sur les champs dont on est surs
354        // ces exceptions doivent etre ventilees dans les plugins fonctionnels concernes
355        // dans la globale table_des_traitements
356        switch ($p->type_requete) {
357                case 'signatures':
358                case 'syndic_articles':
359                        $champs_surs = array(
360                        'date', 'date_heure', 'statut', 'ip', 'url_article', 'maj', 'idx'
361                        );
362                        if (!in_array(strtolower($p->nom_champ), $champs_surs)
363                        AND !preg_match(',^ID_,', $p->nom_champ))
364                                $ps = 'safehtml('.$ps.')';
365                        break;
366                default:
367                        break;
368        }
369
370        // Remplacer enfin le placeholder %s par le vrai code de la balise
371        return str_replace('%s', $p->code, $ps);
372}
373
374
375//
376// Appliquer les filtres a un champ [(#CHAMP|filtre1|filtre2)]
377// retourne un code php compile exprimant ce champ filtre et securise
378//  - une etoile => pas de processeurs standards
379//  - deux etoiles => pas de securite non plus !
380//
381// http://doc.spip.org/@applique_filtres
382function applique_filtres($p) {
383
384        // Traitements standards (cf. supra)
385        if ($p->etoile == '')
386                $code = champs_traitements($p);
387        else
388                $code = $p->code;
389
390        // Appliquer les filtres perso
391        if ($p->param)
392                $code = compose_filtres($p, $code);
393
394        // S'il y a un lien avec la session, ajouter un code qui levera
395        // un drapeau dans la structure d'invalidation $Cache
396        if (isset($p->descr['session']))
397                $code = "invalideur_session(\$Cache, $code)";
398
399        // Securite
400        if ($p->interdire_scripts
401        AND $p->etoile != '**') {
402                if (!preg_match("/^sinon[(](.*),'([^']*)'[)]$/", $code, $r))
403                        $code = "interdire_scripts($code)";
404                else {
405                  $code = interdire_scripts($r[2]);
406                  $code = "sinon(interdire_scripts($r[1]),'$code')";
407                }
408        }
409        return $code;
410}
411
412// Cf. function pipeline dans ecrire/inc_utils.php
413// http://doc.spip.org/@compose_filtres
414function compose_filtres(&$p, $code) {
415
416        $image_miette = false;
417        foreach($p->param as $filtre) {
418                $fonc = array_shift($filtre);
419                if (!$fonc) continue; // normalement qu'au premier tour.
420                $is_filtre_image = ((substr($fonc,0,6)=='image_') AND $fonc!='image_graver');
421                if ($image_miette AND !$is_filtre_image){
422        // il faut graver maintenant car apres le filtre en cours
423        // on est pas sur d'avoir encore le nom du fichier dans le pipe
424                        $code = "filtrer('image_graver', $code)";
425                        $image_miette = false;
426                }
427                // recuperer les arguments du filtre,
428                // a separer par "," ou ":" dans le cas du filtre "?{a,b}"
429                if ($fonc !== '?') {
430                        $sep = ',';
431                } else {$sep = ':';
432                        // |?{a,b} *doit* avoir exactement 2 arguments ; on les force
433                        if (count($filtre) != 2)
434                                $filtre = array(isset($filtre[0])?$filtre[0]:"", isset($filtre[1])?$filtre[1]:"");
435                }
436                $arglist = compose_filtres_args($p, $filtre, $sep);
437                $logique = filtre_logique($fonc, $code, substr($arglist,1));
438                if ($logique)
439                        $code = $logique;
440                else {
441                        if (isset($GLOBALS['spip_matrice'][$fonc])) {
442                                $code = "filtrer('$fonc',$code$arglist)";
443                                if ($is_filtre_image) $image_miette = true;
444                        }
445
446                        // le filtre est defini sous forme de fonction ou de methode
447                        // par ex. dans inc_texte, inc_filtres ou mes_fonctions
448                        elseif ($f = chercher_filtre($fonc)) {
449                                // cas particulier : le filtre |set doit acceder a la $Pile
450                                // proto: filtre_set($val, &$Pile, $args...)
451                                if (in_array($fonc, array('set'))) {
452                                        $code = "$f(\$Pile,$code$arglist)";
453                                }
454                                else {
455                                        $code = "$f($code$arglist)";
456                                }
457                        }
458
459                        // le filtre n'existe pas,
460                        // on le notifie
461                        else erreur_squelette(array('zbug_erreur_filtre', array('filtre'=>  texte_script($fonc))), $p);
462                }
463        }
464        // ramasser les images intermediaires inutiles et graver l'image finale
465        if ($image_miette)
466                $code = "filtrer('image_graver',$code)";
467
468        return $code;
469}
470
471// Filtres et,ou,oui,non,sinon,xou,xor,and,or,not,yes
472// et comparateurs
473function filtre_logique($fonc, $code, $arg)
474{
475        global $table_criteres_infixes;
476        switch (true) {
477                case in_array($fonc, $table_criteres_infixes):
478                        return "($code $fonc $arg)";
479                case ($fonc == 'and') OR ($fonc == 'et'):
480                        return "((($code) AND ($arg)) ?' ' :'')";
481                case ($fonc == 'or') OR ($fonc == 'ou'):
482                        return "((($code) OR ($arg)) ?' ' :'')";
483                case ($fonc == 'xor') OR ($fonc == 'xou'):
484                        return "((($code) XOR ($arg)) ?' ' :'')";
485                case ($fonc == 'sinon'):
486                        return "(((\$a = $code) OR (!is_array(\$a) AND strlen(\$a))) ? \$a : $arg)";
487                case ($fonc == 'not') OR ($fonc == 'non'):
488                        return "(($code) ?'' :' ')";
489                case ($fonc == 'yes') OR ($fonc == 'oui'):
490                        return "(($code) ?' ' :'')";
491        }
492        return '';
493}
494
495// http://doc.spip.org/@compose_filtres_args
496function compose_filtres_args($p, $args, $sep)
497{
498        $arglist = "";
499        foreach ($args as $arg) {
500                $arglist .= $sep . 
501                  calculer_liste($arg, $p->descr, $p->boucles, $p->id_boucle);
502        }
503        return $arglist;
504}
505
506//
507// Reserve les champs necessaires a la comparaison avec le contexte donne par
508// la boucle parente ; attention en recursif il faut les reserver chez soi-meme
509// ET chez sa maman
510//
511// http://doc.spip.org/@calculer_argument_precedent
512function calculer_argument_precedent($idb, $nom_champ, &$boucles) {
513
514        // si recursif, forcer l'extraction du champ SQL mais ignorer le code
515        if ($boucles[$idb]->externe) {
516                index_pile ($idb, $nom_champ, $boucles); 
517                $zero = '$SP';
518        } else $zero = '0';
519        // retourner $Pile[$SP] et pas $Pile[0] si recursion en 1ere boucle
520        $prec = $boucles[$idb]->id_parent;
521        return (($prec === '')
522                ? ('$Pile[' . $zero . "]['$nom_champ']") 
523                : index_pile($prec, $nom_champ, $boucles));
524}
525
526//
527// Rechercher dans la pile des boucles actives celle ayant un critere
528// comportant un certain $motif, et construire alors une reference
529// a l'environnement de cette boucle, qu'on indexe avec $champ.
530// Sert a referencer une cellule non declaree dans la table et pourtant la.
531// Par exemple pour la balise #POINTS on produit $Pile[$SP-n]['points']
532// si la n-ieme boucle a un critere "recherche", car on sait qu'il a produit
533// "SELECT XXXX AS points"
534//
535
536// http://doc.spip.org/@rindex_pile
537function rindex_pile($p, $champ, $motif) 
538{
539        $n = 0;
540        $b = $p->id_boucle;
541        $p->code = '';
542        while ($b != '') {
543                foreach($p->boucles[$b]->criteres as $critere) {
544                        if ($critere->op == $motif) {
545                                $p->code = '$Pile[$SP' . (($n==0) ? "" : "-$n") .
546                                        "]['$champ']";
547                                $b = '';
548                                break 2;
549                        }
550                }
551                $n++;
552                $b = $p->boucles[$b]->id_parent;
553        }
554
555        // si on est hors d'une boucle de {recherche}, cette balise est vide
556        if (!$p->code)
557                $p->code = "''";
558
559        $p->interdire_scripts = false;
560        return $p;
561}
562
563?>
Note: See TracBrowser for help on using the repository browser.