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