PageRenderTime 64ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/spip/ecrire/public/compiler.php

https://github.com/eyeswebcrea/espace-couture-sittler.fr
PHP | 1012 lines | 742 code | 112 blank | 158 comment | 145 complexity | e62fe09d58d146de6a4ddcfd3a9b70fa MD5 | raw file
Possible License(s): LGPL-2.1, GPL-3.0
  1. <?php
  2. /***************************************************************************\
  3. * SPIP, Systeme de publication pour l'internet *
  4. * *
  5. * Copyright (c) 2001-2011 *
  6. * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
  7. * *
  8. * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
  9. * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
  10. \***************************************************************************/
  11. //
  12. // Fichier principal du compilateur de squelettes
  13. //
  14. if (!defined('_ECRIRE_INC_VERSION')) return;
  15. // reperer un code ne calculant rien, meme avec commentaire
  16. define('CODE_MONOTONE', ",^(\n//[^\n]*\n)?\(?'([^'])*'\)?$,");
  17. // s'il faut commenter le code produit
  18. define('CODE_COMMENTE', true);
  19. // definition des structures de donnees
  20. include_spip('public/interfaces');
  21. // Definition de la structure $p, et fonctions de recherche et de reservation
  22. // dans l'arborescence des boucles
  23. include_spip('public/references');
  24. // definition des boucles
  25. include_spip('public/boucles');
  26. // definition des criteres
  27. include_spip('public/criteres');
  28. // definition des balises
  29. include_spip('public/balises');
  30. // Gestion des jointures
  31. include_spip('public/jointures');
  32. // Les 2 ecritures INCLURE{A1,A2,A3...} et INCLURE(A1){A2}{A3}... sont admises
  33. // Preferer la premiere.
  34. // Les Ai sont de la forme Vi=Ei ou bien Vi qui veut alors dire Vi=Vi
  35. // Le resultat est un tableau indexe par les Vi
  36. // Toutefois, si le premier argument n'est pas de la forme Vi=Ei
  37. // il est conventionnellement la valeur de l'index 1.
  38. // pour la balise #INCLURE
  39. // mais pas pour <INCLURE> dont le fond est defini explicitement.
  40. // http://doc.spip.org/@argumenter_inclure
  41. function argumenter_inclure($params, $rejet_filtres, $p, &$boucles, $id_boucle, $echap=true, $lang = '', $fond1=false){
  42. $l = array();
  43. $erreur_p_i_i = '';
  44. if (!is_array($params)) return $l;
  45. foreach($params as $k => $couple) {
  46. // la liste d'arguments d'inclusion peut se terminer par un filtre
  47. $filtre = array_shift($couple);
  48. if ($filtre) break;
  49. foreach($couple as $n => $val) {
  50. $var = $val[0];
  51. if ($var->type != 'texte') {
  52. if ($n OR $k OR $fond1) {
  53. $erreur_p_i_i = array('zbug_parametres_inclus_incorrects',
  54. array('param' => $var->nom_champ));
  55. erreur_squelette($erreur_p_i_i, $p);
  56. } else $l[1] = calculer_liste($val, $p->descr, $boucles, $id_boucle);
  57. break;
  58. } else {
  59. preg_match(",^([^=]*)(=?)(.*)$,", $var->texte,$m);
  60. $var = $m[1];
  61. $auto = false;;
  62. if ($m[2]) {
  63. $v = $m[3];
  64. if (preg_match(',^[\'"](.*)[\'"]$,', $v, $m)) $v = $m[1];
  65. $val[0] = new Texte;
  66. $val[0]->texte = $v;
  67. } elseif ($k OR $n OR $fond1) {
  68. $auto = true;
  69. } else $var = 1;
  70. if ($var == 'lang') {
  71. $lang = !$auto
  72. ? calculer_liste($val, $p->descr, $boucles, $id_boucle)
  73. : '$GLOBALS["spip_lang"]';
  74. } else {
  75. $val = $auto
  76. ? index_pile($id_boucle, $var, $boucles)
  77. : calculer_liste($val, $p->descr, $boucles, $id_boucle);
  78. if ($var !== 1)
  79. $val = ($echap?"\'$var\' => ' . argumenter_squelette(":"'$var' => ")
  80. . $val . ($echap? ") . '":" ");
  81. else $val = $echap ? "'.$val.'" : $val;
  82. $l[$var] = $val;
  83. }
  84. }
  85. }
  86. }
  87. if ($erreur_p_i_i) return false;
  88. // Cas particulier de la langue : si {lang=xx} est definie, on
  89. // la passe, sinon on passe la langue courante au moment du calcul
  90. // sauf si on n'en veut pas
  91. if ($lang === false) return $l;
  92. if (!$lang) $lang = '$GLOBALS["spip_lang"]';
  93. $l['lang'] = ($echap?"\'lang\' => ' . argumenter_squelette(":"'lang' => ") . $lang . ($echap?") . '":" ");
  94. return $l;
  95. }
  96. //
  97. // Calculer un <INCLURE()>
  98. // La constante ci-dessous donne le code general quand il s'agit d'un script.
  99. define('CODE_INCLURE_SCRIPT', 'if (($path = %s) AND is_readable($path))
  100. include $path;
  101. else erreur_squelette(array("fichier_introuvable", array("fichier" => "%s")), array(%s));'
  102. );
  103. // // et celle-ci pour un squelette (aussi pour #INCLURE, #MODELE #LES_AUTEURS)
  104. define('CODE_RECUPERER_FOND', 'recuperer_fond(%s, %s, array(%s), %s)');
  105. // http://doc.spip.org/@calculer_inclure
  106. function calculer_inclure($p, &$boucles, $id_boucle) {
  107. $_contexte = argumenter_inclure($p->param, false, $p, $boucles, $id_boucle, true, '', true);
  108. if (is_string($p->texte)) {
  109. $fichier = $p->texte;
  110. $code = "\"$fichier\"";
  111. } else {
  112. $code = calculer_liste($p->texte, $p->descr, $boucles, $id_boucle);
  113. if ($code AND preg_match("/^'([^']*)'/s", $code, $r))
  114. $fichier = $r[1];
  115. else $fichier = '';
  116. }
  117. if (!$code OR $code === '""') {
  118. $erreur_p_i_i = array('zbug_parametres_inclus_incorrects',
  119. array('param' => $code));
  120. erreur_squelette($erreur_p_i_i, $p);
  121. return false;
  122. }
  123. $compil = texte_script(memoriser_contexte_compil($p));
  124. if (is_array($_contexte)) {
  125. // Critere d'inclusion {env} (et {self} pour compatibilite ascendante)
  126. if ($env = (isset($_contexte['env'])|| isset($_contexte['self']))) {
  127. unset($_contexte['env']);
  128. }
  129. // noter les doublons dans l'appel a public.php
  130. if (isset($_contexte['doublons'])) {
  131. $_contexte['doublons'] = "\\'doublons\\' => '.var_export(\$doublons,true).'";
  132. }
  133. if ($ajax = isset($_contexte['ajax']))
  134. unset($_contexte['ajax']);
  135. $_contexte = join(",\n\t", $_contexte);
  136. }
  137. else
  138. return false; // j'aurais voulu toucher le fond ...
  139. $contexte = 'array(' . $_contexte .')';
  140. if ($env) {
  141. $contexte = "array_merge('.var_export(\$Pile[0],1).',$contexte)";
  142. }
  143. // s'il y a une extension .php, ce n'est pas un squelette
  144. if (preg_match('/^.+[.]php$/s', $fichier)) {
  145. // si inexistant, on essaiera a l'execution
  146. if ($path = find_in_path($fichier))
  147. $path = "\"$path\"";
  148. else $path = "find_in_path(\"$fichier\")";
  149. $code = sprintf(CODE_INCLURE_SCRIPT, $path, $fichier, $compil);
  150. } else {
  151. $_options[] = "\"compil\"=>array($compil)";
  152. if ($ajax)
  153. $_options[] = "\"ajax\"=>true";
  154. $code = " ' . argumenter_squelette($code) . '";
  155. $code = "echo " . sprintf(CODE_RECUPERER_FOND, $code, $contexte, implode(',',$_options), "_request(\"connect\")") . ';';
  156. }
  157. return "\n'<'.'". "?php ". $code . "\n?'." . "'>'";
  158. }
  159. //
  160. // calculer_boucle() produit le corps PHP d'une boucle Spip.
  161. // ce corps remplit une variable $t0 retournee en valeur.
  162. // Ici on distingue boucles recursives et boucle a requete SQL
  163. // et on insere le code d'envoi au debusqueur du resultat de la fonction.
  164. // http://doc.spip.org/@calculer_boucle
  165. function calculer_boucle($id_boucle, &$boucles) {
  166. $boucles[$id_boucle] = pipeline('post_boucle', $boucles[$id_boucle]);
  167. // en mode debug memoriser les premiers passages dans la boucle,
  168. // mais pas tous, sinon ca pete.
  169. if (_request('var_mode_affiche') != 'resultat')
  170. $trace = '';
  171. else {
  172. $trace = $boucles[$id_boucle]->descr['nom'] . $id_boucle;
  173. $trace = "if (count(@\$GLOBALS['debug_objets']['resultat']['$trace'])<3)
  174. \$GLOBALS['debug_objets']['resultat']['$trace'][] = \$t0;";
  175. }
  176. return ($boucles[$id_boucle]->type_requete == 'boucle')
  177. ? calculer_boucle_rec($id_boucle, $boucles, $trace)
  178. : calculer_boucle_nonrec($id_boucle, $boucles, $trace);
  179. }
  180. // compil d'une boucle recursive.
  181. // il suffit (ET IL FAUT) sauvegarder les valeurs des arguments passes par
  182. // reference, car par definition un tel passage ne les sauvegarde pas
  183. // http://doc.spip.org/@calculer_boucle_rec
  184. function calculer_boucle_rec($id_boucle, &$boucles, $trace) {
  185. $nom = $boucles[$id_boucle]->param[0];
  186. return "\n\t\$save_numrows = (\$Numrows['$nom']);"
  187. . "\n\t\$t0 = " . $boucles[$id_boucle]->return . ";"
  188. . "\n\t\$Numrows['$nom'] = (\$save_numrows);"
  189. . $trace
  190. . "\n\treturn \$t0;";
  191. }
  192. // Compilation d'une boucle non recursive.
  193. // Ci-dessous la constante donnant le cadre systematique du code:
  194. // %s1: initialisation des arguments de calculer_select
  195. // %s2: appel de calculer_select en donnant un contexte pour les cas d'erreur
  196. // %s3: initialisation du sous-tableau Numrows[id_boucle]
  197. // %s4: sauvegarde de la langue et calcul des invariants de boucle sur elle
  198. // %s5: boucle while sql_fetch ou str_repeat si corps monotone
  199. // %s6: restauration de la langue
  200. // %s7: liberation de la ressource, en tenant compte du serveur SQL
  201. // %s8: code de trace eventuel avant le retour
  202. define('CODE_CORPS_BOUCLE', '%s
  203. $t0 = "";
  204. // REQUETE
  205. $result = calculer_select($select, $from, $type, $where, $join, $groupby, $orderby, $limit, $having, $table, $id, $connect,
  206. array(%s));
  207. if ($result) {
  208. %s%s$SP++;
  209. // RESULTATS
  210. %s
  211. %s@sql_free($result%s);
  212. }%s
  213. return $t0;'
  214. );
  215. // http://doc.spip.org/@calculer_boucle_nonrec
  216. function calculer_boucle_nonrec($id_boucle, &$boucles, $trace) {
  217. $boucle = &$boucles[$id_boucle];
  218. $return = $boucle->return;
  219. $type_boucle = $boucle->type_requete;
  220. $primary = $boucle->primary;
  221. $constant = preg_match(CODE_MONOTONE, str_replace("\\'",'', $return));
  222. $flag_cpt = $boucle->mode_partie ||$boucle->cptrows;
  223. $corps = '';
  224. // faudrait expanser le foreach a la compil, car y en a souvent qu'un
  225. // et puis faire un [] plutot qu'un "','."
  226. if ($boucle->doublons)
  227. $corps .= "\n\t\t\tforeach(" . $boucle->doublons . ' as $k) $doublons[$k] .= "," . ' .
  228. index_pile($id_boucle, $primary, $boucles)
  229. . "; // doublons\n";
  230. // La boucle doit-elle selectionner la langue ?
  231. // -. par defaut, les boucles suivantes le font
  232. // (sauf si forcer_lang==true ou si le titre contient <multi>).
  233. // - . a moins d'une demande explicite via {!lang_select}
  234. if (!$constant && $boucle->lang_select != 'non' &&
  235. (($boucle->lang_select == 'oui') ||
  236. in_array($type_boucle, array(
  237. 'articles', 'rubriques', 'hierarchie', 'breves'
  238. )))
  239. ) {
  240. // Memoriser la langue avant la boucle et la restituer apres
  241. // afin que le corps de boucle affecte la globale directement
  242. $init_lang = "lang_select(\$GLOBALS['spip_lang']);\n\t";
  243. $fin_lang = "lang_select();\n\t";
  244. $corps .=
  245. "\n\t\tlang_select_public("
  246. . index_pile($id_boucle, 'lang', $boucles)
  247. . ", '".$boucle->lang_select."'"
  248. . (in_array($type_boucle, array(
  249. 'articles', 'rubriques', 'hierarchie', 'breves'
  250. )) ? ', '.index_pile($id_boucle, 'titre', $boucles) : '')
  251. . ');';
  252. }
  253. else {
  254. $init_lang = '';
  255. $fin_lang = '';
  256. // sortir les appels au traducteur (invariants de boucle)
  257. if (strpos($return, '?php') === false
  258. AND preg_match_all("/\W(_T[(]'[^']*'[)])/", $return, $r)) {
  259. $i = 1;
  260. foreach($r[1] as $t) {
  261. $init_lang .= "\n\t\$l$i = $t;";
  262. $return = str_replace($t, "\$l$i", $return);
  263. $i++;
  264. }
  265. }
  266. }
  267. // gestion optimale des separateurs et des boucles constantes
  268. if (count($boucle->separateur))
  269. $code_sep = ("'" . str_replace("'","\'",join('',$boucle->separateur)) . "'");
  270. $corps .=
  271. ((!$boucle->separateur) ?
  272. (($constant && !$corps && !$flag_cpt) ? $return :
  273. (($return==="''") ? '' :
  274. ("\n\t\t" . '$t0 .= ' . $return . ";"))) :
  275. ("\n\t\t\$t1 " .
  276. ((strpos($return, '$t1.') === 0) ?
  277. (".=" . substr($return,4)) :
  278. ('= ' . $return)) .
  279. ";\n\t\t" .
  280. '$t0 .= (($t1 && $t0) ? ' . $code_sep . " : '') . \$t1;"));
  281. // Calculer les invalideurs si c'est une boucle non constante et si on
  282. // souhaite invalider ces elements
  283. if (!$constant AND $primary) {
  284. include_spip('inc/invalideur');
  285. if (function_exists($i = 'calcul_invalideurs'))
  286. $corps = $i($corps, $primary, $boucles, $id_boucle);
  287. }
  288. // gerer le compteur de boucle
  289. // avec ou sans son utilisation par les criteres {1/3} {1,4} {n-2,1}...
  290. if ($boucle->partie OR $boucle->cptrows)
  291. $corps = "\n\t\t\$Numrows['$id_boucle']['compteur_boucle']++;"
  292. . $boucle->partie
  293. . $corps;
  294. $serveur = !$boucle->sql_serveur ? ''
  295. : (', ' . _q($boucle->sql_serveur));
  296. // si le corps est une constante, ne pas appeler le serveur N fois!
  297. if (preg_match(CODE_MONOTONE,str_replace("\\'",'',$corps), $r)) {
  298. if (!isset($r[2]) OR (!$r[2])) {
  299. if (!$boucle->numrows)
  300. return "\n\t\$t0 = '';";
  301. else
  302. $corps = "";
  303. } else {
  304. $boucle->numrows = true;
  305. $corps = "\n\t\$t0 = str_repeat($corps, \$Numrows['$id_boucle']['total']);";
  306. }
  307. } else $corps = "while (\$Pile[\$SP] = @sql_fetch(\$result$serveur)) {\n$corps\n }";
  308. $count = '';
  309. if (!$boucle->select) {
  310. if (!$boucle->numrows OR $boucle->limit OR $boucle_mode_partie OR $boucle->group)
  311. $count = '1';
  312. else $count = 'count(*)';
  313. $boucles[$id_boucle]->select[]= $count;
  314. }
  315. if ($flag_cpt)
  316. $nums = "\n\t// COMPTEUR\n\t"
  317. . "\$Numrows['$id_boucle']['compteur_boucle'] = 0;\n\t";
  318. else $nums = '';
  319. if ($boucle->numrows OR $boucle->mode_partie) {
  320. if ($count == 'count(*)')
  321. $count = "array_shift(sql_fetch(\$result$serveur))";
  322. else $count = "sql_count(\$result$serveur)";
  323. $nums .= "\$Numrows['$id_boucle']['total'] = @intval($count);"
  324. . $boucle->mode_partie
  325. . "\n\t";
  326. }
  327. // Ne calculer la requete que maintenant
  328. // car ce qui precede appelle index_pile qui influe dessus
  329. $init = (($init = $boucles[$id_boucle]->doublons)
  330. ? ("\n\t$init = array();") : '')
  331. . calculer_requete_sql($boucles[$id_boucle]);
  332. $contexte = memoriser_contexte_compil($boucle);
  333. return sprintf(CODE_CORPS_BOUCLE, $init, $contexte, $nums, $init_lang, $corps, $fin_lang, $serveur, $trace);
  334. }
  335. // http://doc.spip.org/@calculer_requete_sql
  336. function calculer_requete_sql($boucle)
  337. {
  338. return ($boucle->hierarchie ? "\n\t$boucle->hierarchie" : '')
  339. . $boucle->in
  340. . $boucle->hash
  341. . calculer_dec('$table', "'" . $boucle->id_table ."'")
  342. . calculer_dec('$id', "'" . $boucle->id_boucle ."'")
  343. # En absence de champ c'est un decompte :
  344. . calculer_dec('$from', calculer_from($boucle))
  345. . calculer_dec('$type', calculer_from_type($boucle))
  346. . calculer_dec('$groupby', 'array(' . (($g=join("\",\n\t\t\"",$boucle->group))?'"'.$g.'"':'') . ")")
  347. . calculer_dec('$select', 'array("' . join("\",\n\t\t\"", $boucle->select). "\")")
  348. . calculer_dec('$orderby', 'array(' . calculer_order($boucle) . ")")
  349. . calculer_dec('$where', calculer_dump_array($boucle->where))
  350. . calculer_dec('$join', calculer_dump_join($boucle->join))
  351. . calculer_dec('$limit', (strpos($boucle->limit, 'intval') === false ?
  352. "'".$boucle->limit."'" :
  353. $boucle->limit))
  354. . calculer_dec('$having', calculer_dump_array($boucle->having));
  355. }
  356. function memoriser_contexte_compil($p) {
  357. return join(',', array(
  358. _q($p->descr['sourcefile']),
  359. _q($p->descr['nom']),
  360. @_q($p->id_boucle),
  361. intval($p->ligne),
  362. '$GLOBALS[\'spip_lang\']'));
  363. }
  364. function reconstruire_contexte_compil($context_compil)
  365. {
  366. if (!is_array($context_compil)) return $context_compil;
  367. include_spip('public/interfaces');
  368. $p = new Contexte;
  369. $p->descr = array('sourcefile' => $context_compil[0],
  370. 'nom' => $context_compil[1]);
  371. $p->id_boucle = $context_compil[2];
  372. $p->ligne = $context_compil[3];
  373. $p->lang = $context_compil[4];
  374. return $p;
  375. }
  376. // http://doc.spip.org/@calculer_dec
  377. function calculer_dec($nom, $val)
  378. {
  379. $static = "static ";
  380. if (
  381. strpos($val, '$') !== false
  382. OR strpos($val, 'sql_') !== false
  383. OR (
  384. $test = str_replace(array("array(",'\"',"\'"),array("","",""),$val) // supprimer les array( et les echappements de guillemets
  385. AND strpos($test,"(")!==FALSE // si pas de parenthese ouvrante, pas de fonction, on peut sortir
  386. AND $test = preg_replace(",'[^']*',UimsS","",$test) // supprimer les chaines qui peuvent contenir des fonctions SQL qui ne genent pas
  387. AND preg_match(",\w+\s*\(,UimsS",$test,$regs) // tester la presence de fonctions restantes
  388. )
  389. ){
  390. $static = "";
  391. }
  392. return "\n\t" . $static . $nom . ' = ' . $val . ';';
  393. }
  394. // http://doc.spip.org/@calculer_dump_array
  395. function calculer_dump_array($a)
  396. {
  397. if (!is_array($a)) return $a ;
  398. $res = "";
  399. if ($a AND $a[0] == "'?'")
  400. return ("(" . calculer_dump_array($a[1]) .
  401. " ? " . calculer_dump_array($a[2]) .
  402. " : " . calculer_dump_array($a[3]) .
  403. ")");
  404. else {
  405. foreach($a as $v) $res .= ", " . calculer_dump_array($v);
  406. return "\n\t\t\tarray(" . substr($res,2) . ')';
  407. }
  408. }
  409. // http://doc.spip.org/@calculer_dump_join
  410. function calculer_dump_join($a)
  411. {
  412. $res = "";
  413. foreach($a as $k => $v)
  414. $res .= ", '$k' => array(".implode(',',$v).")";
  415. return 'array(' . substr($res,2) . ')';
  416. }
  417. // http://doc.spip.org/@calculer_from
  418. function calculer_from(&$boucle)
  419. {
  420. $res = "";
  421. foreach($boucle->from as $k => $v) $res .= ",'$k' => '$v'";
  422. return 'array(' . substr($res,1) . ')';
  423. }
  424. // http://doc.spip.org/@calculer_from_type
  425. function calculer_from_type(&$boucle)
  426. {
  427. $res = "";
  428. foreach($boucle->from_type as $k => $v) $res .= ",'$k' => '$v'";
  429. return 'array(' . substr($res,1) . ')';
  430. }
  431. // http://doc.spip.org/@calculer_order
  432. function calculer_order(&$boucle)
  433. {
  434. if (!$order = $boucle->order
  435. AND !$order = $boucle->default_order)
  436. $order = array();
  437. /*if (isset($boucle->modificateur['collate'])){
  438. $col = "." . $boucle->modificateur['collate'];
  439. foreach($order as $k=>$o)
  440. if (strpos($order[$k],'COLLATE')===false)
  441. $order[$k].= $col;
  442. }*/
  443. return join(', ', $order);
  444. }
  445. // Production du code PHP a partir de la sequence livree par le phraseur
  446. // $boucles est passe par reference pour affectation par index_pile.
  447. // Retourne une expression PHP,
  448. // (qui sera argument d'un Return ou la partie droite d'une affectation).
  449. // http://doc.spip.org/@calculer_liste
  450. function calculer_liste($tableau, $descr, &$boucles, $id_boucle='') {
  451. if (!$tableau) return "''";
  452. if (!isset($descr['niv'])) $descr['niv'] = 0;
  453. $codes = compile_cas($tableau, $descr, $boucles, $id_boucle);
  454. if ($codes === false) return false;
  455. $n = count($codes);
  456. if (!$n) return "''";
  457. $tab = str_repeat("\t", $descr['niv']);
  458. if (_request('var_mode_affiche') != 'validation') {
  459. if ($n==1)
  460. return $codes[0];
  461. else {
  462. $res = '';
  463. foreach($codes as $code) {
  464. if (!preg_match("/^'[^']*'$/", $code)
  465. OR substr($res,-1,1)!=="'")
  466. $res .= " .\n$tab$code";
  467. else {
  468. $res = substr($res,0,-1) . substr($code,1);
  469. }
  470. }
  471. return '(' . substr($res,2+$descr['niv']) . ')';
  472. }
  473. } else {
  474. $nom = $descr['nom'] . $id_boucle . ($descr['niv']?$descr['niv']:'');
  475. return "join('', array_map('array_shift', \$GLOBALS['debug_objets']['sequence']['$nom'] = array(" . join(" ,\n$tab", $codes) . ")))";
  476. }
  477. }
  478. define('_REGEXP_COND_VIDE_NONVIDE',"/^[(](.*)[?]\s*''\s*:\s*('[^']+')\s*[)]$/");
  479. define('_REGEXP_COND_NONVIDE_VIDE',"/^[(](.*)[?]\s*('[^']+')\s*:\s*''\s*[)]$/");
  480. define('_REGEXP_CONCAT_NON_VIDE', "/^(.*)[.]\s*'[^']+'\s*$/");
  481. // http://doc.spip.org/@compile_cas
  482. function compile_cas($tableau, $descr, &$boucles, $id_boucle) {
  483. $codes = array();
  484. // cas de la boucle recursive
  485. if (is_array($id_boucle))
  486. $id_boucle = $id_boucle[0];
  487. $type = !$id_boucle ? '' : $boucles[$id_boucle]->type_requete;
  488. $tab = str_repeat("\t", ++$descr['niv']);
  489. $mode = _request('var_mode_affiche');
  490. $err_e_c = '';
  491. // chaque commentaire introduit dans le code doit commencer
  492. // par un caractere distinguant le cas, pour exploitation par debug.
  493. foreach ($tableau as $p) {
  494. switch($p->type) {
  495. // texte seul
  496. case 'texte':
  497. $code = "'".str_replace(array("\\","'"),array("\\\\","\\'"), $p->texte)."'";
  498. $commentaire= strlen($p->texte) . " signes";
  499. $avant='';
  500. $apres='';
  501. $altern = "''";
  502. break;
  503. case 'polyglotte':
  504. $code = "";
  505. foreach($p->traductions as $k => $v) {
  506. $code .= ",'" .
  507. str_replace(array("\\","'"),array("\\\\","\\'"), $k) .
  508. "' => '" .
  509. str_replace(array("\\","'"),array("\\\\","\\'"), $v) .
  510. "'";
  511. }
  512. $code = "choisir_traduction(array(" .
  513. substr($code,1) .
  514. "))";
  515. $commentaire= '&';
  516. $avant='';
  517. $apres='';
  518. $altern = "''";
  519. break;
  520. // inclure
  521. case 'include':
  522. $p->descr = $descr;
  523. $code = calculer_inclure($p, $boucles, $id_boucle);
  524. if ($code === false) {
  525. $err_e_c = true;
  526. $code = "''";
  527. } else {
  528. $commentaire = '<INCLURE ' . addslashes(str_replace("\n", ' ', $code)) . '>';
  529. $avant='';
  530. $apres='';
  531. $altern = "''";
  532. }
  533. break;
  534. // boucle
  535. case 'boucle':
  536. $nom = $p->id_boucle;
  537. $newdescr = $descr;
  538. $newdescr['id_mere'] = $nom;
  539. $newdescr['niv']++;
  540. $avant = calculer_liste($p->avant,
  541. $newdescr, $boucles, $id_boucle);
  542. $apres = calculer_liste($p->apres,
  543. $newdescr, $boucles, $id_boucle);
  544. $newdescr['niv']--;
  545. $altern = calculer_liste($p->altern,
  546. $newdescr, $boucles, $id_boucle);
  547. if (($avant === false) OR ($apres === false) OR ($altern === false)) {
  548. $err_e_c = true;
  549. $code = "''";
  550. } else {
  551. $code = 'BOUCLE' .
  552. str_replace("-","_", $nom) . $descr['nom'] .
  553. '($Cache, $Pile, $doublons, $Numrows, $SP)';
  554. $commentaire= "?$nom";
  555. if (!$boucles[$nom]->milieu
  556. AND $boucles[$nom]->type_requete <> 'boucle') {
  557. if ($altern != "''") $code .= "\n. $altern";
  558. if ($avant<>"''" OR $apres<>"''")
  559. spip_log("boucle $nom toujours vide, code superflu dans $id");
  560. $avant = $apres = $altern = "''";
  561. } else if ($altern != "''") $altern = "($altern)";
  562. }
  563. break;
  564. case 'idiome':
  565. $l = array();
  566. foreach ($p->arg as $k => $v) {
  567. if ($k) $l[]= _q($k).' => '.calculer_liste($v,$descr,$boucles,$id_boucle);
  568. }
  569. $l = !$l ? '' : (", array(".implode(",\n",$l).")");
  570. $code = "_T('" . $p->module . ":" .$p->nom_champ . "'$l)";
  571. if ($p->param) {
  572. $p->id_boucle = $id_boucle;
  573. $p->boucles = &$boucles;
  574. $code = compose_filtres($p, $code);
  575. }
  576. $commentaire = ":";
  577. $avant='';
  578. $apres='';
  579. $altern = "''";
  580. break;
  581. case 'champ':
  582. // cette structure pourrait etre completee des le phrase' (a faire)
  583. $p->id_boucle = $id_boucle;
  584. $p->boucles = &$boucles;
  585. $p->descr = $descr;
  586. #$p->interdire_scripts = true;
  587. $p->type_requete = $type;
  588. $code = calculer_champ($p);
  589. $commentaire = '#' . $p->nom_champ . $p->etoile;
  590. $avant = calculer_liste($p->avant,
  591. $descr, $boucles, $id_boucle);
  592. $apres = calculer_liste($p->apres,
  593. $descr, $boucles, $id_boucle);
  594. $altern = "''";
  595. // Si la valeur est destinee a une comparaison a ''
  596. // forcer la conversion en une chaine par strval
  597. // si ca peut etre autre chose qu'une chaine
  598. if (($avant != "''" OR $apres != "''")
  599. AND $code[0]!= "'"
  600. # AND (strpos($code,'interdire_scripts') !== 0)
  601. AND !preg_match(_REGEXP_COND_VIDE_NONVIDE, $code)
  602. AND !preg_match(_REGEXP_COND_NONVIDE_VIDE, $code)
  603. AND !preg_match(_REGEXP_CONCAT_NON_VIDE, $code))
  604. $code = "strval($code)";
  605. break;
  606. default:
  607. // Erreur de construction de l'arbre de syntaxe abstraite
  608. $code = "''";
  609. $p->descr = $descr;
  610. $err_e_c = array('zbug_erreur_compilation');
  611. erreur_squelette($err_e_c, $p);
  612. } // switch
  613. if ($code != "''") {
  614. $code = compile_retour($code, $avant, $apres, $altern, $tab, $descr['niv']);
  615. $codes[]= (($mode == 'validation') ?
  616. "array($code, '$commentaire', " . $p->ligne . ")"
  617. : (($mode == 'code') ?
  618. "\n// $commentaire\n$code" :
  619. $code));
  620. }
  621. } // foreach
  622. return $err_e_c ? false : $codes;
  623. }
  624. // production d'une expression conditionnelle ((v=EXP) ? (p . v .s) : a)
  625. // mais si EXP est de la forme (t ? 'C' : '') on produit (t ? (p . C . s) : a)
  626. // de meme si EXP est de la forme (t ? '' : 'C')
  627. // http://doc.spip.org/@compile_retour
  628. function compile_retour($code, $avant, $apres, $altern, $tab, $n)
  629. {
  630. if ($avant == "''") $avant = '';
  631. if ($apres == "''") $apres = '';
  632. if (!$avant AND !$apres AND ($altern==="''")) return $code;
  633. if (preg_match(_REGEXP_CONCAT_NON_VIDE, $code)) {
  634. $t = $code;
  635. $cond = '';
  636. } elseif (preg_match(_REGEXP_COND_VIDE_NONVIDE,$code, $r)) {
  637. $t = $r[2];
  638. $cond = '!' . $r[1];
  639. } else if (preg_match(_REGEXP_COND_NONVIDE_VIDE,$code, $r)) {
  640. $t = $r[2];
  641. $cond = $r[1];
  642. } else {
  643. $t = '$t' . $n;
  644. $cond = "($t = $code)!==''";
  645. }
  646. $res = (!$avant ? "" : "$avant . ") .
  647. $t .
  648. (!$apres ? "" : " . $apres");
  649. if ($res !== $t) $res = "($res)";
  650. return !$cond ? $res : "($cond ?\n\t$tab$res :\n\t$tab$altern)";
  651. }
  652. function compile_inclure_doublons($lexemes)
  653. {
  654. foreach($lexemes as $v)
  655. if($v->type === 'include' AND $v->param)
  656. foreach($v->param as $r)
  657. if (trim($r[0]) === 'doublons')
  658. return true;
  659. return false;
  660. }
  661. // Prend en argument le texte d'un squelette, le nom de son fichier d'origine,
  662. // sa grammaire et un nom. Retourne False en cas d'erreur,
  663. // sinon retourne un tableau de fonctions PHP compilees a evaluer,
  664. // notamment une fonction portant ce nom et calculant une page.
  665. // Pour appeler la fonction produite, lui fournir 2 tableaux de 1 e'le'ment:
  666. // - 1er: element 'cache' => nom (du fichier ou` mettre la page)
  667. // - 2e: element 0 contenant un environnement ('id_article => $id_article, etc)
  668. // Elle retournera alors un tableau de 5 e'le'ments:
  669. // - 'texte' => page HTML, application du squelette a` l'environnement;
  670. // - 'squelette' => le nom du squelette
  671. // - 'process_ins' => 'html' ou 'php' selon la pre'sence de PHP dynamique
  672. // - 'invalideurs' => de'pendances de cette page, pour invalider son cache.
  673. // - 'entetes' => tableau des entetes http
  674. // En cas d'erreur, elle retournera un tableau des 2 premiers elements seulement
  675. // http://doc.spip.org/@public_compiler_dist
  676. function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect=''){
  677. // Pre-traitement : reperer le charset du squelette, et le convertir
  678. // Bonus : supprime le BOM
  679. include_spip('inc/charsets');
  680. $squelette = transcoder_page($squelette);
  681. $descr = array('nom' => $nom,
  682. 'gram' => $gram,
  683. 'sourcefile' => $sourcefile,
  684. 'squelette' => $squelette);
  685. // Phraser le squelette, selon sa grammaire
  686. $boucles = array();
  687. $f = charger_fonction('phraser_' . $gram, 'public');
  688. $squelette = $f($squelette, '', $boucles, $descr);
  689. return compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect);
  690. }
  691. // Point d'entree pour arbre de syntaxe abstraite fourni en premier argument
  692. // Autres specifications comme ci-dessus
  693. function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect=''){
  694. global $tables_jointures;
  695. static $trouver_table;
  696. spip_timer('calcul_skel');
  697. if (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode'] == 'debug') {
  698. $GLOBALS['debug_objets']['squelette'][$nom] = $descr['squelette'];
  699. $GLOBALS['debug_objets']['sourcefile'][$nom] = $sourcefile;
  700. if (!isset($GLOBALS['debug_objets']['principal']))
  701. $GLOBALS['debug_objets']['principal'] = $nom;
  702. }
  703. foreach ($boucles as $id => $boucle) {
  704. $GLOBALS['debug_objets']['boucle'][$nom.$id] = $boucle;
  705. }
  706. $descr['documents'] = compile_inclure_doublons($squelette);
  707. // Demander la description des tables une fois pour toutes
  708. // et reperer si les doublons sont demandes
  709. // pour un inclure ou une boucle document
  710. // c'est utile a la fonction champs_traitements
  711. if (!$trouver_table)
  712. $trouver_table = charger_fonction('trouver_table', 'base');
  713. foreach($boucles as $id => $boucle) {
  714. if (!($type = $boucle->type_requete)) continue;
  715. if (!$descr['documents'] AND (
  716. (($type == 'documents') AND $boucle->doublons) OR
  717. compile_inclure_doublons($boucle->avant) OR
  718. compile_inclure_doublons($boucle->apres) OR
  719. compile_inclure_doublons($boucle->milieu) OR
  720. compile_inclure_doublons($boucle->altern)))
  721. $descr['documents'] = true;
  722. if ($type != 'boucle') {
  723. if (!$boucles[$id]->sql_serveur AND $connect)
  724. $boucles[$id]->sql_serveur = $connect;
  725. $show = $trouver_table($type, $boucles[$id]->sql_serveur);
  726. // si la table n'existe pas avec le connecteur par defaut,
  727. // c'est peut etre une table qui necessite son connecteur dedie fourni
  728. // permet une ecriture allegee (GEO) -> (geo:GEO)
  729. if (!$show AND $show=$trouver_table($type, strtolower($type)))
  730. $boucles[$id]->sql_serveur = strtolower($type);
  731. if ($show) {
  732. $boucles[$id]->show = $show;
  733. // recopie les infos les plus importantes
  734. $boucles[$id]->primary = $show['key']["PRIMARY KEY"];
  735. $boucles[$id]->id_table = $x = $show['id_table'];
  736. $boucles[$id]->from[$x] = $nom_table = $show['table'];
  737. $boucles[$id]->descr = &$descr;
  738. if ((!$boucles[$id]->jointures)
  739. AND (isset($tables_jointures[$nom_table]))
  740. AND is_array($x = $tables_jointures[$nom_table]))
  741. $boucles[$id]->jointures = $x;
  742. if ($boucles[$id]->jointures_explicites){
  743. $jointures = preg_split("/\s+/",$boucles[$id]->jointures_explicites);
  744. while ($j=array_pop($jointures))
  745. array_unshift($boucles[$id]->jointures,$j);
  746. }
  747. } else {
  748. // Pas une erreur si la table est optionnelle
  749. if ($boucles[$id]->table_optionnelle)
  750. $boucles[$id]->type_requete = '';
  751. else {
  752. $boucles[$id]->type_requete = false;
  753. $boucle = $boucles[$id];
  754. $x = (!$boucle->sql_serveur ? '' :
  755. ($boucle->sql_serveur . ":")) .
  756. $type;
  757. $msg = array('zbug_table_inconnue',
  758. array('table' => $x));
  759. erreur_squelette($msg, $boucle);
  760. }
  761. }
  762. }
  763. }
  764. // Commencer par reperer les boucles appelees explicitement
  765. // car elles indexent les arguments de maniere derogatoire
  766. foreach($boucles as $id => $boucle) {
  767. if ($boucle->type_requete == 'boucle' AND $boucle->param) {
  768. $boucles[$id]->descr = &$descr;
  769. $rec = &$boucles[$boucle->param[0]];
  770. if (!$rec) {
  771. $msg = array('zbug_boucle_recursive_undef',
  772. array('nom' => $boucle->param[0]));
  773. erreur_squelette($msg, $boucle);
  774. $boucles[$id]->type_requete = false;
  775. } else {
  776. $rec->externe = $id;
  777. $descr['id_mere'] = $id;
  778. $boucles[$id]->return =
  779. calculer_liste(array($rec),
  780. $descr,
  781. $boucles,
  782. $boucle->param);
  783. }
  784. }
  785. }
  786. foreach($boucles as $id => $boucle) {
  787. $id = strval($id); // attention au type dans index_pile
  788. $type = $boucle->type_requete;
  789. if ($type AND $type != 'boucle') {
  790. $crit = !$boucle->param ? true : calculer_criteres($id, $boucles);
  791. $descr['id_mere'] = $id;
  792. $boucles[$id]->return =
  793. calculer_liste($boucle->milieu,
  794. $descr,
  795. $boucles,
  796. $id);
  797. // Si les criteres se sont mal compiles
  798. // ne pas tenter d'assembler le code final
  799. // (mais compiler le corps pour detection d'erreurs)
  800. if (is_array($crit))
  801. $boucles[$id]->type_requete = false;
  802. }
  803. }
  804. // idem pour la racine
  805. $descr['id_mere'] = '';
  806. $corps = calculer_liste($squelette, $descr, $boucles);
  807. $debug = (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode']=='debug');
  808. if ($debug) {
  809. include_spip('public/decompiler');
  810. include_spip('public/format_' . _EXTENSION_SQUELETTES);
  811. }
  812. // Calcul du corps de toutes les fonctions PHP,
  813. // en particulier les requetes SQL et TOTAL_BOUCLE
  814. // de'terminables seulement maintenant
  815. foreach($boucles as $id => $boucle) {
  816. $boucle = $boucles[$id] = pipeline('pre_boucle', $boucle);
  817. if ($boucle->return === false) continue;
  818. // appeler la fonction de definition de la boucle
  819. if ($req = $boucle->type_requete) {
  820. $f = 'boucle_'.strtoupper($req);
  821. // si pas de definition perso, definition spip
  822. if (!function_exists($f)) $f = $f.'_dist';
  823. // laquelle a une definition par defaut
  824. if (!function_exists($f)) $f = 'boucle_DEFAUT';
  825. if (!function_exists($f)) $f = 'boucle_DEFAUT_dist';
  826. $req = "\n\n\tstatic \$connect = " .
  827. _q($boucle->sql_serveur) .
  828. ";" .
  829. $f($id, $boucles);
  830. } else $req = ("\n\treturn '';");
  831. $boucles[$id]->return =
  832. "function BOUCLE" . strtr($id,"-","_") . $nom .
  833. '(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' .
  834. $req .
  835. "\n}\n\n";
  836. if ($debug)
  837. $GLOBALS['debug_objets']['code'][$nom.$id] = $boucles[$id]->return;
  838. }
  839. // Au final, si le corps ou un critere au moins s'est mal compile
  840. // retourner False, sinon inserer leur decompilation
  841. if (is_bool($corps)) return false;
  842. foreach($boucles as $id => $boucle) {
  843. if ($boucle->return === false) return false;
  844. $boucle->return = "\n\n/* BOUCLE " .
  845. $boucle->type_requete .
  846. " " .
  847. (!$debug ? '' :
  848. str_replace('*/', '* /',
  849. decompiler_criteres($boucle->param,
  850. $boucle->criteres))) .
  851. " */\n\n " .
  852. $boucle->return;
  853. }
  854. $secondes = spip_timer('calcul_skel');
  855. spip_log("COMPIL ($secondes) [$sourcefile] $nom.php");
  856. // Assimiler la fct principale a une boucle anonyme, c'est plus simple
  857. $code = new Boucle;
  858. $code->descr = $descr;
  859. $code->return = '
  860. //
  861. // Fonction principale du squelette ' .
  862. $sourcefile .
  863. ($connect ? " pour $connect" : '') .
  864. (!CODE_COMMENTE ? '' : "\n// Temps de compilation total: $secondes") .
  865. "\n//" .
  866. (!$debug ? '' : ("\n/*\n" .
  867. str_replace('*/', '* /', public_decompiler($squelette))
  868. . "\n*/")) . "
  869. function " . $nom . '($Cache, $Pile, $doublons=array(), $Numrows=array(), $SP=0) {
  870. '
  871. // reporter de maniere securisee les doublons inclus
  872. .'
  873. if (isset($Pile[0]["doublons"]) AND is_array($Pile[0]["doublons"]))
  874. $doublons = nettoyer_env_doublons($Pile[0]["doublons"]);
  875. $connect = ' .
  876. _q($connect) . ';
  877. $page = ' .
  878. // ATTENTION, le calcul de l'expression $corps affectera $Cache
  879. // c'est pourquoi on l'affecte a la variable auxiliaire $page.
  880. // avant de referencer $Cache
  881. $corps . ";
  882. return analyse_resultat_skel(".var_export($nom,true)
  883. .", \$Cache, \$page, ".var_export($sourcefile,true).");
  884. }";
  885. $boucles[''] = $code;
  886. return $boucles;
  887. }
  888. ?>