c21b6f226b9a2afa5cff6af96435a99c8b00c387
[ptitvelo/web/www.git] / www / ecrire / public / compiler.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2012 *
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 /**
15 * Fichier principal du compilateur de squelettes
16 *
17 * @package SPIP\Compilateur\Compilation
18 **/
19
20 if (!defined('_ECRIRE_INC_VERSION')) return;
21
22 /** Repérer un code ne calculant rien, meme avec commentaire */
23 define('CODE_MONOTONE', ",^(\n//[^\n]*\n)?\(?'([^'])*'\)?$,");
24 /** Indique s'il faut commenter le code produit */
25 define('CODE_COMMENTE', true);
26
27 // definition des structures de donnees
28 include_spip('public/interfaces');
29
30 // Definition de la structure $p, et fonctions de recherche et de reservation
31 // dans l'arborescence des boucles
32 include_spip('public/references');
33
34 // production du code qui peut etre securisee
35 include_spip('public/sandbox');
36
37 // definition des boucles
38 include_spip('public/boucles');
39
40 // definition des criteres
41 include_spip('public/criteres');
42
43 // definition des balises
44 include_spip('public/balises');
45
46 // Gestion des jointures
47 include_spip('public/jointures');
48
49 // Les 2 ecritures INCLURE{A1,A2,A3...} et INCLURE(A1){A2}{A3}... sont admises
50 // Preferer la premiere.
51 // Les Ai sont de la forme Vi=Ei ou bien Vi qui veut alors dire Vi=Vi
52 // Le resultat est un tableau indexe par les Vi
53 // Toutefois, si le premier argument n'est pas de la forme Vi=Ei
54 // il est conventionnellement la valeur de l'index 1.
55 // pour la balise #INCLURE
56 // mais pas pour <INCLURE> dont le fond est defini explicitement.
57
58
59 // http://doc.spip.org/@argumenter_inclure
60 function argumenter_inclure($params, $rejet_filtres, $p, &$boucles, $id_boucle, $echap=true, $lang = '', $fond1=false){
61 $l = array();
62 $erreur_p_i_i = '';
63 if (!is_array($params)) return $l;
64 foreach($params as $k => $couple) {
65 // la liste d'arguments d'inclusion peut se terminer par un filtre
66 $filtre = array_shift($couple);
67 if ($filtre) break;
68 foreach($couple as $n => $val) {
69 $var = $val[0];
70 if ($var->type != 'texte') {
71 if ($n OR $k OR $fond1) {
72 $erreur_p_i_i = array('zbug_parametres_inclus_incorrects',
73 array('param' => $var->nom_champ));
74 erreur_squelette($erreur_p_i_i, $p);
75 } else $l[1] = calculer_liste($val, $p->descr, $boucles, $id_boucle);
76 break;
77 } else {
78 preg_match(",^([^=]*)(=?)(.*)$,", $var->texte,$m);
79 $var = $m[1];
80 $auto = false;;
81 if ($m[2]) {
82 $v = $m[3];
83 if (preg_match(',^[\'"](.*)[\'"]$,', $v, $m)) $v = $m[1];
84 $val[0] = new Texte;
85 $val[0]->texte = $v;
86 } elseif ($k OR $n OR $fond1) {
87 $auto = true;
88 } else $var = 1;
89
90 if ($var == 'lang') {
91 $lang = !$auto
92 ? calculer_liste($val, $p->descr, $boucles, $id_boucle)
93 : '$GLOBALS["spip_lang"]';
94 } else {
95 $val = $auto
96 ? index_pile($id_boucle, $var, $boucles)
97 : calculer_liste($val, $p->descr, $boucles, $id_boucle);
98 if ($var !== 1)
99 $val = ($echap?"\'$var\' => ' . argumenter_squelette(":"'$var' => ")
100 . $val . ($echap? ") . '":" ");
101 else $val = $echap ? "'.$val.'" : $val;
102 $l[$var] = $val;
103 }
104 }
105 }
106 }
107 if ($erreur_p_i_i) return false;
108 // Cas particulier de la langue : si {lang=xx} est definie, on
109 // la passe, sinon on passe la langue courante au moment du calcul
110 // sauf si on n'en veut pas
111 if ($lang === false) return $l;
112 if (!$lang) $lang = '$GLOBALS["spip_lang"]';
113 $l['lang'] = ($echap?"\'lang\' => ' . argumenter_squelette(":"'lang' => ") . $lang . ($echap?") . '":" ");
114
115 return $l;
116 }
117
118 /**
119 * Code d'appel à un <INCLURE()>
120 *
121 * Code PHP pour un squelette (aussi pour #INCLURE, #MODELE #LES_AUTEURS)
122 */
123 define('CODE_RECUPERER_FOND', 'recuperer_fond(%s, %s, array(%s), %s)');
124
125 /**
126 * Compile une inclusion <INCLURE> ou #INCLURE
127 *
128 * @param Inclure $p
129 * Description de l'inclusion (AST au niveau de l'inclure)
130 * @param array $boucles
131 * AST du squelette
132 * @param string $id_boucle
133 * Identifiant de la boucle contenant l'inclure
134 * @return string
135 * Code PHP appelant l'inclusion
136 **/
137 function calculer_inclure($p, &$boucles, $id_boucle) {
138
139 $_contexte = argumenter_inclure($p->param, false, $p, $boucles, $id_boucle, true, '', true);
140 if (is_string($p->texte)) {
141 $fichier = $p->texte;
142 $code = "\"$fichier\"";
143
144 } else {
145 $code = calculer_liste($p->texte, $p->descr, $boucles, $id_boucle);
146 if ($code AND preg_match("/^'([^']*)'/s", $code, $r))
147 $fichier = $r[1];
148 else $fichier = '';
149 }
150 if (!$code OR $code === '""') {
151 $erreur_p_i_i = array('zbug_parametres_inclus_incorrects',
152 array('param' => $code));
153 erreur_squelette($erreur_p_i_i, $p);
154 return false;
155 }
156 $compil = texte_script(memoriser_contexte_compil($p));
157
158 if (is_array($_contexte)) {
159 // Critere d'inclusion {env} (et {self} pour compatibilite ascendante)
160 if ($env = (isset($_contexte['env'])|| isset($_contexte['self']))) {
161 unset($_contexte['env']);
162 }
163
164 // noter les doublons dans l'appel a public.php
165 if (isset($_contexte['doublons'])) {
166 $_contexte['doublons'] = "\\'doublons\\' => '.var_export(\$doublons,true).'";
167 }
168
169 if ($ajax = isset($_contexte['ajax'])){
170 $ajax = preg_replace(",=>(.*)$,ims",'=> ($v=(\\1))?$v:true',$_contexte['ajax']);
171 unset($_contexte['ajax']);
172 }
173
174 $_contexte = join(",\n\t", $_contexte);
175 }
176 else
177 return false; // j'aurais voulu toucher le fond ...
178
179 $contexte = 'array(' . $_contexte .')';
180
181 if ($env) {
182 $contexte = "array_merge('.var_export(\$Pile[0],1).',$contexte)";
183 }
184
185 // s'il y a une extension .php, ce n'est pas un squelette
186 if (preg_match('/^.+[.]php$/s', $fichier)) {
187 $code = sandbox_composer_inclure_php($fichier, $p);
188 } else {
189 $_options[] = "\"compil\"=>array($compil)";
190 if ($ajax)
191 $_options[] = $ajax;
192 $code = " ' . argumenter_squelette($code) . '";
193 $code = "echo " . sprintf(CODE_RECUPERER_FOND, $code, $contexte, implode(',',$_options), "_request(\"connect\")") . ';';
194 }
195
196 return "\n'<'.'". "?php ". $code . "\n?'." . "'>'";
197 }
198
199
200 /**
201 * Gérer les statuts declarés pour cette table
202 *
203 * S'il existe des statuts sur cette table, déclarés dans la description
204 * d'un objet éditorial, applique leurs contraintes
205 *
206 * @param Boucle $boucle
207 * Descrition de la boucle
208 * @param bool $echapper
209 * true pour échapper le code créé
210 * @param bool $ignore_previsu
211 * true pour ne tester que le cas publie et ignorer l'eventuel var_mode=preview de la page
212 */
213 function instituer_boucle(&$boucle, $echapper=true, $ignore_previsu=false){
214 /*
215 $show['statut'][] = array(
216 'champ'=>'statut', // champ de la table sur lequel porte le filtrage par le statut
217 'publie'=>'publie', // valeur ou liste de valeurs, qui definissent l'objet comme publie.
218 'previsu'=>'publie,prop', // valeur ou liste de valeurs qui sont visibles en previsu
219 'post_date'=>'date', // un champ de date pour la prise en compte des post_dates, ou rien sinon
220 'exception'=>'statut', // liste des modificateurs qui annulent le filtrage par statut
221 // si plusieurs valeurs : array('statut','tout','lien')
222 );
223
224 Pour 'publier' ou 'previsu', si la chaine commence par un "!" on exclu au lieu de filtrer sur les valeurs donnees
225 si la chaine est vide, on ne garde rien si elle est seulement "!" on n'exclu rien
226
227 Si le statut repose sur une jointure, 'champ' est alors un tableau du format suivant :
228 'champ'=>array(
229 array(table1, cle1),
230 ...
231 array(tablen, clen),
232 champstatut
233 )
234
235 champstatut est alors le champ statut sur la tablen
236 dans les jointures, clen peut etre un tableau pour une jointure complexe : array('id_objet','id_article','objet','article')
237 */
238 $id_table = $boucle->id_table;
239 $show = $boucle->show;
240 if (isset($show['statut']) AND $show['statut']){
241 foreach($show['statut'] as $k=>$s){
242 // Restreindre aux elements publies si pas de {statut} ou autre dans les criteres
243 $filtrer = true;
244 if (isset($s['exception'])) {
245 foreach(is_array($s['exception'])?$s['exception']:array($s['exception']) as $m) {
246 if (isset($boucle->modificateur[$m]) OR isset($boucle->modificateur['criteres'][$m])) {
247 $filtrer = false;
248 break;
249 }
250 }
251 }
252
253 if ($filtrer) {
254 if (is_array($s['champ'])){
255 $statut = preg_replace(',\W,','',array_pop($s['champ'])); // securite
256 $jointures = array();
257 // indiquer la description de chaque table dans le tableau de jointures,
258 // ce qui permet d'eviter certains GROUP BY inutiles.
259 $trouver_table = charger_fonction('trouver_table', 'base');
260 foreach($s['champ'] as $j) {
261 $id = reset($j);
262 $def = $trouver_table($id);
263 $jointures[] = array('',array($id,$def),end($j));
264 }
265 $jointures[0][0] = $id_table;
266 if (!array_search($id, $boucle->from)){
267 include_spip('public/jointures');
268 fabrique_jointures($boucle, $jointures, true, $boucle->show, $id_table, '', $echapper);
269 }
270 // trouver l'alias de la table d'arrivee qui porte le statut
271 $id = array_search($id, $boucle->from);
272 }
273 else {
274 $id = $id_table;
275 $statut = preg_replace(',\W,','',$s['champ']); // securite
276 }
277 $mstatut = $id .'.'.$statut;
278
279 $arg_ignore_previsu=($ignore_previsu?",true":'');
280 include_spip('public/quete');
281 if (isset($s['post_date']) AND $s['post_date']
282 AND $GLOBALS['meta']["post_dates"] == 'non'){
283 $date = $id.'.'.preg_replace(',\W,','',$s['post_date']); // securite
284 array_unshift($boucle->where,
285 $echapper ?
286 "\nquete_condition_postdates('$date',"._q($boucle->sql_serveur)."$arg_ignore_previsu)"
287 :
288 quete_condition_postdates($date,$boucle->sql_serveur,$ignore_previsu)
289 );
290 }
291 array_unshift($boucle->where,
292 $echapper ?
293 "\nquete_condition_statut('$mstatut',"
294 . _q($s['previsu']).","
295 ._q($s['publie']).","
296 ._q($boucle->sql_serveur)."$arg_ignore_previsu)"
297 :
298 quete_condition_statut($mstatut,$s['previsu'],$s['publie'],$boucle->sql_serveur,$ignore_previsu)
299 );
300 }
301 }
302 }
303 }
304
305 /**
306 * Produit le corps PHP d'une boucle Spip.
307 *
308 * Ce corps remplit une variable $t0 retournée en valeur.
309 * Ici on distingue boucles recursives et boucle à requête SQL
310 * et on insère le code d'envoi au debusqueur du resultat de la fonction.
311 *
312 * @param string $id_boucle
313 * Identifiant de la boucle
314 * @param array $boucles
315 * AST du squelette
316 * @return string
317 * Code PHP compilé de la boucle
318 */
319 function calculer_boucle($id_boucle, &$boucles) {
320
321 $boucle = &$boucles[$id_boucle];
322 instituer_boucle($boucle);
323 $boucles[$id_boucle] = pipeline('post_boucle', $boucles[$id_boucle]);
324
325 // en mode debug memoriser les premiers passages dans la boucle,
326 // mais pas tous, sinon ca pete.
327 if (_request('var_mode_affiche') != 'resultat')
328 $trace = '';
329 else {
330 $trace = $boucles[$id_boucle]->descr['nom'] . $id_boucle;
331 $trace = "if (count(@\$GLOBALS['debug_objets']['resultat']['$trace'])<3)
332 \$GLOBALS['debug_objets']['resultat']['$trace'][] = \$t0;";
333 }
334 return ($boucles[$id_boucle]->type_requete == TYPE_RECURSIF)
335 ? calculer_boucle_rec($id_boucle, $boucles, $trace)
336 : calculer_boucle_nonrec($id_boucle, $boucles, $trace);
337 }
338
339
340 /**
341 * Compilation d'une boucle recursive.
342 *
343 * @internal
344 * Il suffit (ET IL FAUT) sauvegarder les valeurs des arguments passes par
345 * reference, car par definition un tel passage ne les sauvegarde pas
346 *
347 * @param string $id_boucle
348 * Identifiant de la boucle
349 * @param array $boucles
350 * AST du squelette
351 * @param string $trace
352 * Code PHP (en mode debug uniquement) servant à conserver une
353 * trace des premières valeurs de la boucle afin de pouvoir
354 * les afficher dans le débugueur ultérieurement
355 * @return string
356 * Code PHP compilé de la boucle récursive
357 **/
358 function calculer_boucle_rec($id_boucle, &$boucles, $trace) {
359 $nom = $boucles[$id_boucle]->param[0];
360 return
361 // Numrows[$nom] peut ne pas être encore defini
362 "\n\t\$save_numrows = (isset(\$Numrows['$nom']) ? \$Numrows['$nom'] : array());"
363 . "\n\t\$t0 = " . $boucles[$id_boucle]->return . ";"
364 . "\n\t\$Numrows['$nom'] = (\$save_numrows);"
365 . $trace
366 . "\n\treturn \$t0;";
367 }
368
369 /**
370 * Compilation d'une boucle non recursive.
371 *
372 * La constante donne le cadre systématique du code:
373 * %s1: initialisation des arguments de calculer_select
374 * %s2: appel de calculer_select en donnant un contexte pour les cas d'erreur
375 * %s3: initialisation du sous-tableau Numrows[id_boucle]
376 * %s4: sauvegarde de la langue et calcul des invariants de boucle sur elle
377 * %s5: boucle while sql_fetch ou str_repeat si corps monotone
378 * %s6: restauration de la langue
379 * %s7: liberation de la ressource, en tenant compte du serveur SQL
380 * %s8: code de trace eventuel avant le retour
381 **/
382 define('CODE_CORPS_BOUCLE', '%s
383 $t0 = "";
384 // REQUETE
385 $iter = IterFactory::create(
386 "%s",
387 %s,
388 array(%s)
389 );
390 if (!$iter->err()) {
391 %s%s$SP++;
392 // RESULTATS
393 %s
394 %s$iter->free();
395 }%s
396 return $t0;'
397 );
398
399 /**
400 * Compilation d'une boucle (non recursive).
401 *
402 * @param string $id_boucle
403 * Identifiant de la boucle
404 * @param array $boucles
405 * AST du squelette
406 * @param string $trace
407 * Code PHP (en mode debug uniquement) servant à conserver une
408 * trace des premières valeurs de la boucle afin de pouvoir
409 * les afficher dans le débugueur ultérieurement
410 * @return string
411 * Code PHP compilé de la boucle récursive
412 **/
413 function calculer_boucle_nonrec($id_boucle, &$boucles, $trace) {
414
415 $boucle = &$boucles[$id_boucle];
416 $return = $boucle->return;
417 $type_boucle = $boucle->type_requete;
418 $primary = $boucle->primary;
419 $constant = preg_match(CODE_MONOTONE, str_replace("\\'",'', $return));
420 $flag_cpt = $boucle->mode_partie ||$boucle->cptrows;
421 $corps = '';
422
423 // faudrait expanser le foreach a la compil, car y en a souvent qu'un
424 // et puis faire un [] plutot qu'un "','."
425 if ($boucle->doublons)
426 $corps .= "\n\t\t\tforeach(" . $boucle->doublons . ' as $k) $doublons[$k] .= "," . ' .
427 index_pile($id_boucle, $primary, $boucles)
428 . "; // doublons\n";
429
430 // La boucle doit-elle selectionner la langue ?
431 // - par defaut, les boucles suivantes le font
432 // (sauf si forcer_lang==true ou si le titre contient <multi>).
433 // - a moins d'une demande explicite via {!lang_select}
434 if (!$constant && $boucle->lang_select != 'non' &&
435 (($boucle->lang_select == 'oui') ||
436 in_array($type_boucle, array(
437 'articles', 'rubriques', 'hierarchie', 'breves'
438 )))
439 ) {
440 // Memoriser la langue avant la boucle et la restituer apres
441 // afin que le corps de boucle affecte la globale directement
442 $init_lang = "lang_select(\$GLOBALS['spip_lang']);\n\t";
443 $fin_lang = "lang_select();\n\t";
444
445 $corps .=
446 "\n\t\tlang_select_public("
447 . index_pile($id_boucle, 'lang', $boucles)
448 . ", '".$boucle->lang_select."'"
449 . (in_array($type_boucle, array(
450 'articles', 'rubriques', 'hierarchie', 'breves'
451 )) ? ', '.index_pile($id_boucle, 'titre', $boucles) : '')
452 . ');';
453 }
454 else {
455 $init_lang = '';
456 $fin_lang = '';
457 // sortir les appels au traducteur (invariants de boucle)
458 if (strpos($return, '?php') === false
459 AND preg_match_all("/\W(_T[(]'[^']*'[)])/", $return, $r)) {
460 $i = 1;
461 foreach($r[1] as $t) {
462 $init_lang .= "\n\t\$l$i = $t;";
463 $return = str_replace($t, "\$l$i", $return);
464 $i++;
465 }
466 }
467 }
468
469 // gestion optimale des separateurs et des boucles constantes
470 if (count($boucle->separateur))
471 $code_sep = ("'" . str_replace("'","\'",join('',$boucle->separateur)) . "'");
472
473 $corps .=
474 ((!$boucle->separateur) ?
475 (($constant && !$corps && !$flag_cpt) ? $return :
476 (($return==="''") ? '' :
477 ("\n\t\t" . '$t0 .= ' . $return . ";"))) :
478 ("\n\t\t\$t1 " .
479 ((strpos($return, '$t1.') === 0) ?
480 (".=" . substr($return,4)) :
481 ('= ' . $return)) .
482 ";\n\t\t" .
483 '$t0 .= ((strlen($t1) && strlen($t0)) ? ' . $code_sep . " : '') . \$t1;"));
484
485 // Calculer les invalideurs si c'est une boucle non constante et si on
486 // souhaite invalider ces elements
487 if (!$constant AND $primary) {
488 include_spip('inc/invalideur');
489 if (function_exists($i = 'calcul_invalideurs'))
490 $corps = $i($corps, $primary, $boucles, $id_boucle);
491 }
492
493 // gerer le compteur de boucle
494 // avec ou sans son utilisation par les criteres {1/3} {1,4} {n-2,1}...
495
496 if ($boucle->partie OR $boucle->cptrows)
497 $corps = "\n\t\t\$Numrows['$id_boucle']['compteur_boucle']++;"
498 . $boucle->partie
499 . $corps;
500
501 // si le corps est une constante, ne pas appeler le serveur N fois!
502
503 if (preg_match(CODE_MONOTONE,str_replace("\\'",'',$corps), $r)) {
504 if (!isset($r[2]) OR (!$r[2])) {
505 if (!$boucle->numrows)
506 return "\n\t\$t0 = '';";
507 else
508 $corps = "";
509 } else {
510 $boucle->numrows = true;
511 $corps = "\n\t\$t0 = str_repeat($corps, \$Numrows['$id_boucle']['total']);";
512 }
513 } else $corps = "while (\$Pile[\$SP]=\$iter->fetch()) {\n$corps\n }";
514
515 $count = '';
516 if (!$boucle->select) {
517 if (!$boucle->numrows OR $boucle->limit OR $boucle->mode_partie OR $boucle->group)
518 $count = '1';
519 else $count = 'count(*)';
520 $boucles[$id_boucle]->select[]= $count;
521 }
522
523 if ($flag_cpt)
524 $nums = "\n\t// COMPTEUR\n\t"
525 . "\$Numrows['$id_boucle']['compteur_boucle'] = 0;\n\t";
526 else $nums = '';
527
528 if ($boucle->numrows OR $boucle->mode_partie) {
529 $nums .= "\$Numrows['$id_boucle']['total'] = @intval(\$iter->count());"
530 . $boucle->mode_partie
531 . "\n\t";
532 }
533
534 // Ne calculer la requete que maintenant
535 // car ce qui precede appelle index_pile qui influe dessus
536
537 $init = (($init = $boucles[$id_boucle]->doublons)
538 ? ("\n\t$init = array();") : '')
539 . calculer_requete_sql($boucles[$id_boucle]);
540
541 $contexte = memoriser_contexte_compil($boucle);
542
543 $a = sprintf(CODE_CORPS_BOUCLE,
544 $init,
545 $boucle->iterateur,
546 "\$command",
547 $contexte,
548 $nums,
549 $init_lang,
550 $corps,
551 $fin_lang,
552 $trace
553 );
554
555 # var_dump($a);exit;
556 return $a;
557 }
558
559
560 /**
561 * Calcule le code PHP d'une boucle contenant les informations qui produiront une requête SQL
562 *
563 * Le code produit est un tableau associatif $command contenant les informations
564 * pour que la boucle produise ensuite sa requête, tel que $command['from'] = 'spip_articles';
565 *
566 * @param Boucle $boucle
567 * AST de la boucle
568 * @return string
569 * Code PHP compilé définissant les informations de requête
570 **/
571 function calculer_requete_sql($boucle)
572 {
573 $init = array();
574 $init[] = calculer_dec('table', "'" . $boucle->id_table ."'");
575 $init[] = calculer_dec('id', "'" . $boucle->id_boucle ."'");
576 # En absence de champ c'est un decompte :
577 $init[] = calculer_dec('from', calculer_from($boucle));
578 $init[] = calculer_dec('type', calculer_from_type($boucle));
579 $init[] = calculer_dec('groupby', 'array(' . (($g=join("\",\n\t\t\"",$boucle->group))?'"'.$g.'"':'') . ")");
580 $init[] = calculer_dec('select', 'array("' . join("\",\n\t\t\"", $boucle->select). "\")");
581 $init[] = calculer_dec('orderby', 'array(' . calculer_order($boucle) . ")");
582 $init[] = calculer_dec('where', calculer_dump_array($boucle->where));
583 $init[] = calculer_dec('join', calculer_dump_join($boucle->join));
584 $init[] = calculer_dec('limit',
585 (strpos($boucle->limit, 'intval') === false ?
586 "'".$boucle->limit."'"
587 :
588 $boucle->limit));
589 $init[] = calculer_dec('having', calculer_dump_array($boucle->having));
590 $s = $d = "";
591 foreach ($init as $i){
592 if (reset($i))
593 $s .= "\n\t\t".end($i);
594 else
595 $d .= "\n\t".end($i);
596 }
597
598 return ($boucle->hierarchie ? "\n\t$boucle->hierarchie" : '')
599 . $boucle->in
600 . $boucle->hash
601 . "\n\t".'if (!isset($command[\'table\'])) {'
602 . $s
603 . "\n\t}"
604 . $d;
605 }
606
607 /**
608 * Retourne une chaîne des informations du contexte de compilation
609 *
610 * Retourne la source, le nom, l'identifiant de boucle, la ligne, la langue
611 * de l'élément dans une chaîne.
612 *
613 * @see reconstruire_contexte_compil()
614 *
615 * @param Object $p
616 * Objet de l'AST dont on mémorise le contexte
617 * @return string
618 * Informations du contexte séparés par des virgules,
619 * qui peut être utilisé pour la production d'un tableau array()
620 **/
621 function memoriser_contexte_compil($p) {
622 return join(',', array(
623 _q($p->descr['sourcefile']),
624 _q($p->descr['nom']),
625 @_q($p->id_boucle),
626 intval($p->ligne),
627 '$GLOBALS[\'spip_lang\']'));
628 }
629
630 /**
631 * Reconstruit un contexte de compilation
632 *
633 * Pour un tableau d'information de contexte donné,
634 * retourne un objet Contexte (objet générique de l'AST)
635 * avec ces informations
636 *
637 * @see memoriser_contexte_compil()
638 *
639 * @param array $context_compil
640 * Tableau des informations du contexte
641 * @return Contexte
642 * Objet Contexte
643 **/
644 function reconstruire_contexte_compil($context_compil)
645 {
646 if (!is_array($context_compil)) return $context_compil;
647 $p = new Contexte;
648 $p->descr = array('sourcefile' => $context_compil[0],
649 'nom' => $context_compil[1]);
650 $p->id_boucle = $context_compil[2];
651 $p->ligne = $context_compil[3];
652 $p->lang = $context_compil[4];
653 return $p;
654 }
655
656 // http://doc.spip.org/@calculer_dec
657 function calculer_dec($nom, $val)
658 {
659 $static = 'if (!isset($command[\''.$nom.'\'])) ';
660 // si une variable apparait dans le calcul de la clause
661 // il faut la re-evaluer a chaque passage
662 if (
663 strpos($val, '$') !== false
664 /*
665 OR strpos($val, 'sql_') !== false
666 OR (
667 $test = str_replace(array("array(",'\"',"\'"),array("","",""),$val) // supprimer les array( et les echappements de guillemets
668 AND strpos($test,"(")!==FALSE // si pas de parenthese ouvrante, pas de fonction, on peut sortir
669 AND $test = preg_replace(",'[^']*',UimsS","",$test) // supprimer les chaines qui peuvent contenir des fonctions SQL qui ne genent pas
670 AND preg_match(",\w+\s*\(,UimsS",$test,$regs) // tester la presence de fonctions restantes
671 )*/
672 )
673 $static = "";
674
675 return array($static,'$command[\''.$nom.'\'] = ' . $val . ';');
676 }
677
678 // http://doc.spip.org/@calculer_dump_array
679 function calculer_dump_array($a)
680 {
681 if (!is_array($a)) return $a ;
682 $res = "";
683 if ($a AND $a[0] == "'?'")
684 return ("(" . calculer_dump_array($a[1]) .
685 " ? " . calculer_dump_array($a[2]) .
686 " : " . calculer_dump_array($a[3]) .
687 ")");
688 else {
689 foreach($a as $v) $res .= ", " . calculer_dump_array($v);
690 return "\n\t\t\tarray(" . substr($res,2) . ')';
691 }
692 }
693
694 // http://doc.spip.org/@calculer_dump_join
695 function calculer_dump_join($a)
696 {
697 $res = "";
698 foreach($a as $k => $v)
699 $res .= ", '$k' => array(".implode(',',$v).")";
700 return 'array(' . substr($res,2) . ')';
701 }
702
703 // http://doc.spip.org/@calculer_from
704 function calculer_from(&$boucle)
705 {
706 $res = "";
707 foreach($boucle->from as $k => $v) $res .= ",'$k' => '$v'";
708 return 'array(' . substr($res,1) . ')';
709 }
710
711 // http://doc.spip.org/@calculer_from_type
712 function calculer_from_type(&$boucle)
713 {
714 $res = "";
715 foreach($boucle->from_type as $k => $v) $res .= ",'$k' => '$v'";
716 return 'array(' . substr($res,1) . ')';
717 }
718
719 // http://doc.spip.org/@calculer_order
720 function calculer_order(&$boucle)
721 {
722 if (!$order = $boucle->order
723 AND !$order = $boucle->default_order)
724 $order = array();
725
726 /*if (isset($boucle->modificateur['collate'])){
727 $col = "." . $boucle->modificateur['collate'];
728 foreach($order as $k=>$o)
729 if (strpos($order[$k],'COLLATE')===false)
730 $order[$k].= $col;
731 }*/
732 return join(', ', $order);
733 }
734
735 // Production du code PHP a partir de la sequence livree par le phraseur
736 // $boucles est passe par reference pour affectation par index_pile.
737 // Retourne une expression PHP,
738 // (qui sera argument d'un Return ou la partie droite d'une affectation).
739
740 // http://doc.spip.org/@calculer_liste
741 function calculer_liste($tableau, $descr, &$boucles, $id_boucle='') {
742 if (!$tableau) return "''";
743 if (!isset($descr['niv'])) $descr['niv'] = 0;
744 $codes = compile_cas($tableau, $descr, $boucles, $id_boucle);
745 if ($codes === false) return false;
746 $n = count($codes);
747 if (!$n) return "''";
748 $tab = str_repeat("\t", $descr['niv']);
749 if (_request('var_mode_affiche') != 'validation') {
750 if ($n==1)
751 return $codes[0];
752 else {
753 $res = '';
754 foreach($codes as $code) {
755 if (!preg_match("/^'[^']*'$/", $code)
756 OR substr($res,-1,1)!=="'")
757 $res .= " .\n$tab$code";
758 else {
759 $res = substr($res,0,-1) . substr($code,1);
760 }
761 }
762 return '(' . substr($res,2+$descr['niv']) . ')';
763 }
764 } else {
765 $nom = $descr['nom'] . $id_boucle . ($descr['niv']?$descr['niv']:'');
766 return "join('', array_map('array_shift', \$GLOBALS['debug_objets']['sequence']['$nom'] = array(" . join(" ,\n$tab", $codes) . ")))";
767 }
768 }
769
770 define('_REGEXP_COND_VIDE_NONVIDE',"/^[(](.*)[?]\s*''\s*:\s*('[^']+')\s*[)]$/");
771 define('_REGEXP_COND_NONVIDE_VIDE',"/^[(](.*)[?]\s*('[^']+')\s*:\s*''\s*[)]$/");
772 define('_REGEXP_CONCAT_NON_VIDE', "/^(.*)[.]\s*'[^']+'\s*$/");
773
774 // http://doc.spip.org/@compile_cas
775 function compile_cas($tableau, $descr, &$boucles, $id_boucle) {
776
777 $codes = array();
778 // cas de la boucle recursive
779 if (is_array($id_boucle))
780 $id_boucle = $id_boucle[0];
781 $type = !$id_boucle ? '' : $boucles[$id_boucle]->type_requete;
782 $tab = str_repeat("\t", ++$descr['niv']);
783 $mode = _request('var_mode_affiche');
784 $err_e_c = '';
785 // chaque commentaire introduit dans le code doit commencer
786 // par un caractere distinguant le cas, pour exploitation par debug.
787 foreach ($tableau as $p) {
788
789 switch($p->type) {
790 // texte seul
791 case 'texte':
792 $code = sandbox_composer_texte($p->texte, $p);
793 $commentaire= strlen($p->texte) . " signes";
794 $avant='';
795 $apres='';
796 $altern = "''";
797 break;
798
799 case 'polyglotte':
800 $code = "";
801 foreach($p->traductions as $k => $v) {
802 $code .= ",'" .
803 str_replace(array("\\","'"),array("\\\\","\\'"), $k) .
804 "' => '" .
805 str_replace(array("\\","'"),array("\\\\","\\'"), $v) .
806 "'";
807 }
808 $code = "choisir_traduction(array(" .
809 substr($code,1) .
810 "))";
811 $commentaire= '&';
812 $avant='';
813 $apres='';
814 $altern = "''";
815 break;
816
817 // inclure
818 case 'include':
819 $p->descr = $descr;
820 $code = calculer_inclure($p, $boucles, $id_boucle);
821 if ($code === false) {
822 $err_e_c = true;
823 $code = "''";
824 } else {
825 $commentaire = '<INCLURE ' . addslashes(str_replace("\n", ' ', $code)) . '>';
826 $avant='';
827 $apres='';
828 $altern = "''";
829 }
830 break;
831
832 // boucle
833 case TYPE_RECURSIF:
834 $nom = $p->id_boucle;
835 $newdescr = $descr;
836 $newdescr['id_mere'] = $nom;
837 $newdescr['niv']++;
838 $avant = calculer_liste($p->avant,
839 $newdescr, $boucles, $id_boucle);
840 $apres = calculer_liste($p->apres,
841 $newdescr, $boucles, $id_boucle);
842 $newdescr['niv']--;
843 $altern = calculer_liste($p->altern,
844 $newdescr, $boucles, $id_boucle);
845 if (($avant === false) OR ($apres === false) OR ($altern === false)) {
846 $err_e_c = true;
847 $code = "''";
848 } else {
849 $code = 'BOUCLE' .
850 str_replace("-","_", $nom) . $descr['nom'] .
851 '($Cache, $Pile, $doublons, $Numrows, $SP)';
852 $commentaire= "?$nom";
853 if (!$boucles[$nom]->milieu
854 AND $boucles[$nom]->type_requete <> TYPE_RECURSIF) {
855 if ($altern != "''") $code .= "\n. $altern";
856 if ($avant<>"''" OR $apres<>"''")
857 spip_log("boucle $nom toujours vide, code superflu dans $id");
858 $avant = $apres = $altern = "''";
859 } else if ($altern != "''") $altern = "($altern)";
860 }
861 break;
862
863 case 'idiome':
864 $l = array();
865 foreach ($p->arg as $k => $v) {
866 if ($k) $l[]= _q($k).' => '.calculer_liste($v,$p->descr,$boucles,$id_boucle);
867 }
868 $l = !$l ? '' : (", array(".implode(",\n",$l).")");
869 $code = "_T('" . $p->module . ":" .$p->nom_champ . "'$l)";
870 if ($p->param) {
871 $p->id_boucle = $id_boucle;
872 $p->boucles = &$boucles;
873 $code = compose_filtres($p, $code);
874 }
875 $commentaire = ":";
876 $avant='';
877 $apres='';
878 $altern = "''";
879 break;
880
881 case 'champ':
882
883 // cette structure pourrait etre completee des le phrase' (a faire)
884 $p->id_boucle = $id_boucle;
885 $p->boucles = &$boucles;
886 $p->descr = $descr;
887 #$p->interdire_scripts = true;
888 $p->type_requete = $type;
889
890 $code = calculer_champ($p);
891 $commentaire = '#' . $p->nom_champ . $p->etoile;
892 $avant = calculer_liste($p->avant,
893 $descr, $boucles, $id_boucle);
894 $apres = calculer_liste($p->apres,
895 $descr, $boucles, $id_boucle);
896 $altern = "''";
897 // Si la valeur est destinee a une comparaison a ''
898 // forcer la conversion en une chaine par strval
899 // si ca peut etre autre chose qu'une chaine
900 if (($avant != "''" OR $apres != "''")
901 AND $code[0]!= "'"
902 # AND (strpos($code,'interdire_scripts') !== 0)
903 AND !preg_match(_REGEXP_COND_VIDE_NONVIDE, $code)
904 AND !preg_match(_REGEXP_COND_NONVIDE_VIDE, $code)
905 AND !preg_match(_REGEXP_CONCAT_NON_VIDE, $code))
906 $code = "strval($code)";
907 break;
908
909 default:
910 // Erreur de construction de l'arbre de syntaxe abstraite
911 $code = "''";
912 $p->descr = $descr;
913 $err_e_c = _T('zbug_erreur_compilation');
914 erreur_squelette($err_e_c, $p);
915 } // switch
916
917 if ($code != "''") {
918 $code = compile_retour($code, $avant, $apres, $altern, $tab, $descr['niv']);
919 $codes[]= (($mode == 'validation') ?
920 "array($code, '$commentaire', " . $p->ligne . ")"
921 : (($mode == 'code') ?
922 "\n// $commentaire\n$code" :
923 $code));
924 }
925 } // foreach
926
927 return $err_e_c ? false : $codes;
928 }
929
930 // production d'une expression conditionnelle ((v=EXP) ? (p . v .s) : a)
931 // mais si EXP est de la forme (t ? 'C' : '') on produit (t ? (p . C . s) : a)
932 // de meme si EXP est de la forme (t ? '' : 'C')
933
934 // http://doc.spip.org/@compile_retour
935 function compile_retour($code, $avant, $apres, $altern, $tab, $n)
936 {
937 if ($avant == "''") $avant = '';
938 if ($apres == "''") $apres = '';
939 if (!$avant AND !$apres AND ($altern==="''")) return $code;
940
941 if (preg_match(_REGEXP_CONCAT_NON_VIDE, $code)) {
942 $t = $code;
943 $cond = '';
944 } elseif (preg_match(_REGEXP_COND_VIDE_NONVIDE,$code, $r)) {
945 $t = $r[2];
946 $cond = '!' . $r[1];
947 } else if (preg_match(_REGEXP_COND_NONVIDE_VIDE,$code, $r)) {
948 $t = $r[2];
949 $cond = $r[1];
950 } else {
951 $t = '$t' . $n;
952 $cond = "($t = $code)!==''";
953 }
954
955 $res = (!$avant ? "" : "$avant . ") .
956 $t .
957 (!$apres ? "" : " . $apres");
958
959 if ($res !== $t) $res = "($res)";
960 return !$cond ? $res : "($cond ?\n\t$tab$res :\n\t$tab$altern)";
961 }
962
963
964 function compile_inclure_doublons($lexemes)
965 {
966 foreach($lexemes as $v)
967 if($v->type === 'include' AND $v->param)
968 foreach($v->param as $r)
969 if (trim($r[0]) === 'doublons')
970 return true;
971 return false;
972 }
973
974 // Prend en argument le texte d'un squelette, le nom de son fichier d'origine,
975 // sa grammaire et un nom. Retourne False en cas d'erreur,
976 // sinon retourne un tableau de fonctions PHP compilees a evaluer,
977 // notamment une fonction portant ce nom et calculant une page.
978 // Pour appeler la fonction produite, lui fournir 2 tableaux de 1 e'le'ment:
979 // - 1er: element 'cache' => nom (du fichier ou` mettre la page)
980 // - 2e: element 0 contenant un environnement ('id_article => $id_article, etc)
981 // Elle retournera alors un tableau de 5 e'le'ments:
982 // - 'texte' => page HTML, application du squelette a` l'environnement;
983 // - 'squelette' => le nom du squelette
984 // - 'process_ins' => 'html' ou 'php' selon la pre'sence de PHP dynamique
985 // - 'invalideurs' => de'pendances de cette page, pour invalider son cache.
986 // - 'entetes' => tableau des entetes http
987 // En cas d'erreur, elle retournera un tableau des 2 premiers elements seulement
988
989 // http://doc.spip.org/@public_compiler_dist
990 function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect=''){
991 // Pre-traitement : reperer le charset du squelette, et le convertir
992 // Bonus : supprime le BOM
993 include_spip('inc/charsets');
994 $squelette = transcoder_page($squelette);
995
996 // rendre inertes les echappements de #[](){}<>
997 $i = 0;
998 while(false !== strpos($squelette, $inerte = '-INERTE'.$i)) $i++;
999 $squelette = preg_replace_callback(',\\\\([#[()\]{}<>]),',
1000 create_function('$a', "return '$inerte-'.ord(\$a[1]).'-';"), $squelette, -1, $esc);
1001
1002 $descr = array('nom' => $nom,
1003 'gram' => $gram,
1004 'sourcefile' => $sourcefile,
1005 'squelette' => $squelette);
1006
1007 // Phraser le squelette, selon sa grammaire
1008
1009 $boucles = array();
1010 $f = charger_fonction('phraser_' . $gram, 'public');
1011
1012 $squelette = $f($squelette, '', $boucles, $descr);
1013 $boucles = compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect);
1014
1015 // restituer les echappements
1016 if ($esc)
1017 foreach($boucles as $i=>$boucle) {
1018 $boucles[$i]->return = preg_replace_callback(",$inerte-(\d+)-,", create_function('$a', 'return chr($a[1]);'),
1019 $boucle->return);
1020 $boucles[$i]->descr['squelette'] = preg_replace_callback(",$inerte-(\d+)-,", create_function('$a', 'return "\\\\".chr($a[1]);'),
1021 $boucle->descr['squelette']);
1022 }
1023
1024 $debug = ($boucles AND defined('_VAR_MODE') AND _VAR_MODE == 'debug');
1025 if ($debug) {
1026 include_spip('public/decompiler');
1027 foreach($boucles as $id => $boucle) {
1028 if ($id)
1029 $decomp = "\n/* BOUCLE " .
1030 $boucle->type_requete .
1031 " " .
1032 str_replace('*/', '* /', public_decompiler($boucle, $gram, 0, 'criteres')) .
1033 " */\n";
1034 else $decomp = ("\n/*\n" .
1035 str_replace('*/', '* /', public_decompiler($squelette, $gram))
1036 . "\n*/");
1037 $boucles[$id]->return = $decomp .$boucle->return;
1038 $GLOBALS['debug_objets']['code'][$nom.$id] = $boucle->return;
1039 }
1040 }
1041
1042 return $boucles;
1043 }
1044
1045 // Point d'entree pour arbre de syntaxe abstraite fourni en premier argument
1046 // Autres specifications comme ci-dessus
1047
1048 function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect=''){
1049 static $trouver_table;
1050 spip_timer('calcul_skel');
1051
1052 if (defined('_VAR_MODE') AND _VAR_MODE == 'debug') {
1053 $GLOBALS['debug_objets']['squelette'][$nom] = $descr['squelette'];
1054 $GLOBALS['debug_objets']['sourcefile'][$nom] = $sourcefile;
1055
1056 if (!isset($GLOBALS['debug_objets']['principal']))
1057 $GLOBALS['debug_objets']['principal'] = $nom;
1058 }
1059 foreach ($boucles as $id => $boucle) {
1060 $GLOBALS['debug_objets']['boucle'][$nom.$id] = $boucle;
1061 }
1062 $descr['documents'] = compile_inclure_doublons($squelette);
1063
1064 // Demander la description des tables une fois pour toutes
1065 // et reperer si les doublons sont demandes
1066 // pour un inclure ou une boucle document
1067 // c'est utile a la fonction champs_traitements
1068 if (!$trouver_table)
1069 $trouver_table = charger_fonction('trouver_table', 'base');
1070
1071 foreach($boucles as $id => $boucle) {
1072 if (!($type = $boucle->type_requete)) continue;
1073 if (!$descr['documents'] AND (
1074 (($type == 'documents') AND $boucle->doublons) OR
1075 compile_inclure_doublons($boucle->avant) OR
1076 compile_inclure_doublons($boucle->apres) OR
1077 compile_inclure_doublons($boucle->milieu) OR
1078 compile_inclure_doublons($boucle->altern)))
1079 $descr['documents'] = true;
1080 if ($type != TYPE_RECURSIF) {
1081 if (!$boucles[$id]->sql_serveur AND $connect)
1082 $boucles[$id]->sql_serveur = $connect;
1083
1084 // chercher dans les iterateurs du repertoire iterateur/
1085 if ($g = charger_fonction(
1086 preg_replace('/\W/', '_', $boucle->type_requete), 'iterateur', true)) {
1087 $boucles[$id] = $g($boucle);
1088
1089 // sinon, en cas de requeteur d'un type predefini,
1090 // utiliser les informations donnees par le requeteur
1091 // cas "php:xx" et "data:xx".
1092 } else if ($boucle->sql_serveur AND $requeteur = charger_fonction($boucle->sql_serveur, 'requeteur', true)) {
1093 $requeteur($boucles, $boucle, $id);
1094
1095 // utiliser la description des champs transmis
1096 } else {
1097 $show = $trouver_table($type, $boucles[$id]->sql_serveur);
1098 // si la table n'existe pas avec le connecteur par defaut,
1099 // c'est peut etre une table qui necessite son connecteur dedie fourni
1100 // permet une ecriture allegee (GEO) -> (geo:GEO)
1101 if (!$show
1102 AND $show=$trouver_table($type, strtolower($type))) {
1103 $boucles[$id]->sql_serveur = strtolower($type);
1104 }
1105 if ($show) {
1106 $boucles[$id]->show = $show;
1107 // recopie les infos les plus importantes
1108 $boucles[$id]->primary = $show['key']["PRIMARY KEY"];
1109 $boucles[$id]->id_table = $x = preg_replace(",^spip_,","",$show['id_table']);
1110 $boucles[$id]->from[$x] = $nom_table = $show['table'];
1111 $boucles[$id]->iterateur = 'SQL';
1112
1113 $boucles[$id]->descr = &$descr;
1114 if ((!$boucles[$id]->jointures)
1115 AND is_array($show['tables_jointures'])
1116 AND count($x = $show['tables_jointures']))
1117 $boucles[$id]->jointures = $x;
1118 if ($boucles[$id]->jointures_explicites){
1119 $jointures = preg_split("/\s+/",$boucles[$id]->jointures_explicites);
1120 while ($j=array_pop($jointures))
1121 array_unshift($boucles[$id]->jointures,$j);
1122 }
1123 } else {
1124 // Pas une erreur si la table est optionnelle
1125 if ($boucles[$id]->table_optionnelle)
1126 $boucles[$id]->type_requete = '';
1127 else {
1128 $boucles[$id]->type_requete = false;
1129 $boucle = $boucles[$id];
1130 $x = (!$boucle->sql_serveur ? '' :
1131 ($boucle->sql_serveur . ":")) .
1132 $type;
1133 $msg = array('zbug_table_inconnue',
1134 array('table' => $x));
1135 erreur_squelette($msg, $boucle);
1136 }
1137 }
1138 }
1139 }
1140 }
1141
1142 // Commencer par reperer les boucles appelees explicitement
1143 // car elles indexent les arguments de maniere derogatoire
1144 foreach($boucles as $id => $boucle) {
1145 if ($boucle->type_requete == TYPE_RECURSIF AND $boucle->param) {
1146 $boucles[$id]->descr = &$descr;
1147 $rec = &$boucles[$boucle->param[0]];
1148 if (!$rec) {
1149 $msg = array('zbug_boucle_recursive_undef',
1150 array('nom' => $boucle->param[0]));
1151 erreur_squelette($msg, $boucle);
1152 $boucles[$id]->type_requete = false;
1153 } else {
1154 $rec->externe = $id;
1155 $descr['id_mere'] = $id;
1156 $boucles[$id]->return =
1157 calculer_liste(array($rec),
1158 $descr,
1159 $boucles,
1160 $boucle->param);
1161 }
1162 }
1163 }
1164 foreach($boucles as $id => $boucle) {
1165 $id = strval($id); // attention au type dans index_pile
1166 $type = $boucle->type_requete;
1167 if ($type AND $type != TYPE_RECURSIF) {
1168 $res = '';
1169 if ($boucle->param) {
1170 // retourne un tableau en cas d'erreur
1171 $res = calculer_criteres($id, $boucles);
1172 }
1173 $descr['id_mere'] = $id;
1174 $boucles[$id]->return =
1175 calculer_liste($boucle->milieu,
1176 $descr,
1177 $boucles,
1178 $id);
1179 // Si les criteres se sont mal compiles
1180 // ne pas tenter d'assembler le code final
1181 // (mais compiler le corps pour detection d'erreurs)
1182 if (is_array($res)) {
1183 $boucles[$id]->type_requete = false;
1184 }
1185 }
1186 }
1187
1188 // idem pour la racine
1189 $descr['id_mere'] = '';
1190 $corps = calculer_liste($squelette, $descr, $boucles);
1191
1192
1193
1194 // Calcul du corps de toutes les fonctions PHP,
1195 // en particulier les requetes SQL et TOTAL_BOUCLE
1196 // de'terminables seulement maintenant
1197
1198 foreach($boucles as $id => $boucle) {
1199 $boucle = $boucles[$id] = pipeline('pre_boucle', $boucle);
1200 if ($boucle->return === false) {$corps = false; continue;}
1201 // appeler la fonction de definition de la boucle
1202
1203 if ($req = $boucle->type_requete) {
1204 // boucle personnalisée ?
1205 $table = strtoupper($boucle->type_requete);
1206 $serveur = strtolower($boucle->sql_serveur);
1207 if (
1208 // fonction de boucle avec serveur & table
1209 (!$serveur OR
1210 ((!function_exists($f = "boucle_".$serveur."_".$table))
1211 AND (!function_exists($f = $f."_dist"))
1212 )
1213 )
1214 // fonction de boucle avec table
1215 AND (!function_exists($f = "boucle_".$table))
1216 AND (!function_exists($f = $f."_dist"))
1217 ){
1218 // fonction de boucle standard
1219 if (!function_exists($f = 'boucle_DEFAUT')) {
1220 $f = 'boucle_DEFAUT_dist';
1221 }
1222 }
1223
1224 $req = "\n\n\tstatic \$command = array();\n\t" .
1225 "static \$connect;\n\t" .
1226 "\$command['connect'] = \$connect = " .
1227 _q($boucle->sql_serveur) .
1228 ";" .
1229 $f($id, $boucles);
1230 } else $req = ("\n\treturn '';");
1231
1232 $boucles[$id]->return =
1233 "\n\nfunction BOUCLE" . strtr($id,"-","_") . $nom .
1234 '(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' .
1235 $req .
1236 "\n}\n";
1237 }
1238
1239 // Au final, si le corps ou un critere au moins s'est mal compile
1240 // retourner False, sinon inserer leur decompilation
1241 if (is_bool($corps)) return false;
1242
1243 $principal = "\nfunction " . $nom . '($Cache, $Pile, $doublons=array(), $Numrows=array(), $SP=0) {
1244 '
1245 // reporter de maniere securisee les doublons inclus
1246 .'
1247 if (isset($Pile[0]["doublons"]) AND is_array($Pile[0]["doublons"]))
1248 $doublons = nettoyer_env_doublons($Pile[0]["doublons"]);
1249
1250 $connect = ' .
1251 _q($connect) . ';
1252 $page = ' .
1253 // ATTENTION, le calcul de l'expression $corps affectera $Cache
1254 // c'est pourquoi on l'affecte a la variable auxiliaire $page.
1255 // avant de referencer $Cache
1256 $corps . ";
1257
1258 return analyse_resultat_skel(".var_export($nom,true)
1259 .", \$Cache, \$page, ".var_export($sourcefile,true).");
1260 }";
1261
1262 $secondes = spip_timer('calcul_skel');
1263 spip_log("COMPIL ($secondes) [$sourcefile] $nom.php");
1264 // $connect n'est pas sûr : on nettoie
1265 $connect = preg_replace(',[^\w],', '', $connect);
1266
1267 // Assimiler la fct principale a une boucle anonyme, pour retourner un resultat simple
1268 $code = new Boucle;
1269 $code->descr = $descr;
1270 $code->return = '
1271 //
1272 // Fonction principale du squelette ' .
1273 $sourcefile .
1274 ($connect ? " pour $connect" : '') .
1275 (!CODE_COMMENTE ? '' : "\n// Temps de compilation total: $secondes") .
1276 "\n//\n" .
1277 $principal;
1278
1279 $boucles[''] = $code;
1280 return $boucles;
1281 }
1282
1283
1284 /**
1285 * Requeteur pour les boucles (php:nom_iterateur)
1286 *
1287 * Analyse si le nom d'iterateur correspond bien a une classe PHP existante
1288 * et dans ce cas charge la boucle avec cet iterateur.
1289 * Affichera une erreur dans le cas contraire.
1290 *
1291 * @param $boucles Liste des boucles
1292 * @param $boucle La boucle parcourue
1293 * @param $id L'identifiant de la boucle parcourue
1294 *
1295 **/
1296 function requeteur_php_dist(&$boucles, &$boucle, &$id) {
1297 if (class_exists($boucle->type_requete)) {
1298 $g = charger_fonction('php', 'iterateur');
1299 $boucles[$id] = $g($boucle, $boucle->type_requete);
1300 } else {
1301 $x = $boucle->type_requete;
1302 $boucle->type_requete = false;
1303 $msg = array('zbug_iterateur_inconnu',
1304 array('iterateur' => $x));
1305 erreur_squelette($msg, $boucle);
1306 }
1307 }
1308
1309
1310 /**
1311 * Requeteur pour les boucles (data:type de donnee)
1312 * note: (DATA) tout court ne passe pas par ici.
1313 *
1314 * Analyse si le type de donnee peut etre traite
1315 * et dans ce cas charge la boucle avec cet iterateur.
1316 * Affichera une erreur dans le cas contraire.
1317 *
1318 * @param $boucles Liste des boucles
1319 * @param $boucle La boucle parcourue
1320 * @param $id L'identifiant de la boucle parcourue
1321 *
1322 **/
1323 function requeteur_data_dist(&$boucles, &$boucle, &$id) {
1324 include_spip('iterateur/data');
1325 if ($h = charger_fonction($boucle->type_requete . '_to_array' , 'inc', true)) {
1326 $g = charger_fonction('data', 'iterateur');
1327 $boucles[$id] = $g($boucle);
1328 // from[0] stocke le type de data (rss, yql, ...)
1329 $boucles[$id]->from[] = $boucle->type_requete;
1330
1331 } else {
1332 $x = $boucle->type_requete;
1333 $boucle->type_requete = false;
1334 $msg = array('zbug_requeteur_inconnu',
1335 array(
1336 'requeteur' => 'data',
1337 'type' => $x
1338 ));
1339 erreur_squelette($msg, $boucle);
1340 }
1341 }
1342
1343 ?>