X-Git-Url: http://git.cyclocoop.org/?p=velocampus%2Fweb%2Fwww.git;a=blobdiff_plain;f=www%2Fecrire%2Fpublic%2Fcompiler.php;fp=www%2Fecrire%2Fpublic%2Fcompiler.php;h=da13d9faf3d87a5c3d2d784a031ae06377b28066;hp=0000000000000000000000000000000000000000;hb=80b4d3e85f78d402ed2e73f8f5d1bf4c19962eed;hpb=aaf970bf4cdaf76689ecc10609048e18d073820c diff --git a/www/ecrire/public/compiler.php b/www/ecrire/public/compiler.php new file mode 100644 index 0000000..da13d9f --- /dev/null +++ b/www/ecrire/public/compiler.php @@ -0,0 +1,1012 @@ + dont le fond est defini explicitement. + + +// http://doc.spip.org/@argumenter_inclure +function argumenter_inclure($params, $rejet_filtres, $p, &$boucles, $id_boucle, $echap=true, $lang = '', $fond1=false){ + $l = array(); + $erreur_p_i_i = ''; + if (!is_array($params)) return $l; + foreach($params as $k => $couple) { + // la liste d'arguments d'inclusion peut se terminer par un filtre + $filtre = array_shift($couple); + if ($filtre) break; + foreach($couple as $n => $val) { + $var = $val[0]; + if ($var->type != 'texte') { + if ($n OR $k OR $fond1) { + $erreur_p_i_i = array('zbug_parametres_inclus_incorrects', + array('param' => $var->nom_champ)); + erreur_squelette($erreur_p_i_i, $p); + } else $l[1] = calculer_liste($val, $p->descr, $boucles, $id_boucle); + break; + } else { + preg_match(",^([^=]*)(=?)(.*)$,", $var->texte,$m); + $var = $m[1]; + $auto = false;; + if ($m[2]) { + $v = $m[3]; + if (preg_match(',^[\'"](.*)[\'"]$,', $v, $m)) $v = $m[1]; + $val[0] = new Texte; + $val[0]->texte = $v; + } elseif ($k OR $n OR $fond1) { + $auto = true; + } else $var = 1; + + if ($var == 'lang') { + $lang = !$auto + ? calculer_liste($val, $p->descr, $boucles, $id_boucle) + : '$GLOBALS["spip_lang"]'; + } else { + $val = $auto + ? index_pile($id_boucle, $var, $boucles) + : calculer_liste($val, $p->descr, $boucles, $id_boucle); + if ($var !== 1) + $val = ($echap?"\'$var\' => ' . argumenter_squelette(":"'$var' => ") + . $val . ($echap? ") . '":" "); + else $val = $echap ? "'.$val.'" : $val; + $l[$var] = $val; + } + } + } + } + if ($erreur_p_i_i) return false; + // Cas particulier de la langue : si {lang=xx} est definie, on + // la passe, sinon on passe la langue courante au moment du calcul + // sauf si on n'en veut pas + if ($lang === false) return $l; + if (!$lang) $lang = '$GLOBALS["spip_lang"]'; + $l['lang'] = ($echap?"\'lang\' => ' . argumenter_squelette(":"'lang' => ") . $lang . ($echap?") . '":" "); + + return $l; +} + +// +// Calculer un +// La constante ci-dessous donne le code general quand il s'agit d'un script. + +define('CODE_INCLURE_SCRIPT', 'if (($path = %s) AND is_readable($path)) + include $path; +else erreur_squelette(array("fichier_introuvable", array("fichier" => "%s")), array(%s));' +); + +// // et celle-ci pour un squelette (aussi pour #INCLURE, #MODELE #LES_AUTEURS) + +define('CODE_RECUPERER_FOND', 'recuperer_fond(%s, %s, array(%s), %s)'); + +// http://doc.spip.org/@calculer_inclure +function calculer_inclure($p, &$boucles, $id_boucle) { + + $_contexte = argumenter_inclure($p->param, false, $p, $boucles, $id_boucle, true, '', true); + if (is_string($p->texte)) { + $fichier = $p->texte; + $code = "\"$fichier\""; + + } else { + $code = calculer_liste($p->texte, $p->descr, $boucles, $id_boucle); + if ($code AND preg_match("/^'([^']*)'/s", $code, $r)) + $fichier = $r[1]; + else $fichier = ''; + } + if (!$code OR $code === '""') { + $erreur_p_i_i = array('zbug_parametres_inclus_incorrects', + array('param' => $code)); + erreur_squelette($erreur_p_i_i, $p); + return false; + } + $compil = texte_script(memoriser_contexte_compil($p)); + + if (is_array($_contexte)) { + // Critere d'inclusion {env} (et {self} pour compatibilite ascendante) + if ($env = (isset($_contexte['env'])|| isset($_contexte['self']))) { + unset($_contexte['env']); + } + + // noter les doublons dans l'appel a public.php + if (isset($_contexte['doublons'])) { + $_contexte['doublons'] = "\\'doublons\\' => '.var_export(\$doublons,true).'"; + } + + if ($ajax = isset($_contexte['ajax'])) + unset($_contexte['ajax']); + + $_contexte = join(",\n\t", $_contexte); + } + else + return false; // j'aurais voulu toucher le fond ... + + $contexte = 'array(' . $_contexte .')'; + + if ($env) { + $contexte = "array_merge('.var_export(\$Pile[0],1).',$contexte)"; + } + + // s'il y a une extension .php, ce n'est pas un squelette + if (preg_match('/^.+[.]php$/s', $fichier)) { + // si inexistant, on essaiera a l'execution + if ($path = find_in_path($fichier)) + $path = "\"$path\""; + else $path = "find_in_path(\"$fichier\")"; + + $code = sprintf(CODE_INCLURE_SCRIPT, $path, $fichier, $compil); + } else { + $_options[] = "\"compil\"=>array($compil)"; + if ($ajax) + $_options[] = "\"ajax\"=>true"; + $code = " ' . argumenter_squelette($code) . '"; + $code = "echo " . sprintf(CODE_RECUPERER_FOND, $code, $contexte, implode(',',$_options), "_request(\"connect\")") . ';'; + } + + return "\n'<'.'". "?php ". $code . "\n?'." . "'>'"; +} + +// +// calculer_boucle() produit le corps PHP d'une boucle Spip. +// ce corps remplit une variable $t0 retournee en valeur. +// Ici on distingue boucles recursives et boucle a requete SQL +// et on insere le code d'envoi au debusqueur du resultat de la fonction. + +// http://doc.spip.org/@calculer_boucle +function calculer_boucle($id_boucle, &$boucles) { + + $boucles[$id_boucle] = pipeline('post_boucle', $boucles[$id_boucle]); + + // en mode debug memoriser les premiers passages dans la boucle, + // mais pas tous, sinon ca pete. + if (_request('var_mode_affiche') != 'resultat') + $trace = ''; + else { + $trace = $boucles[$id_boucle]->descr['nom'] . $id_boucle; + $trace = "if (count(@\$GLOBALS['debug_objets']['resultat']['$trace'])<3) + \$GLOBALS['debug_objets']['resultat']['$trace'][] = \$t0;"; + } + return ($boucles[$id_boucle]->type_requete == 'boucle') + ? calculer_boucle_rec($id_boucle, $boucles, $trace) + : calculer_boucle_nonrec($id_boucle, $boucles, $trace); +} + +// compil d'une boucle recursive. +// il suffit (ET IL FAUT) sauvegarder les valeurs des arguments passes par +// reference, car par definition un tel passage ne les sauvegarde pas + +// http://doc.spip.org/@calculer_boucle_rec +function calculer_boucle_rec($id_boucle, &$boucles, $trace) { + $nom = $boucles[$id_boucle]->param[0]; + return "\n\t\$save_numrows = (\$Numrows['$nom']);" + . "\n\t\$t0 = " . $boucles[$id_boucle]->return . ";" + . "\n\t\$Numrows['$nom'] = (\$save_numrows);" + . $trace + . "\n\treturn \$t0;"; +} + +// Compilation d'une boucle non recursive. +// Ci-dessous la constante donnant le cadre systematique du code: +// %s1: initialisation des arguments de calculer_select +// %s2: appel de calculer_select en donnant un contexte pour les cas d'erreur +// %s3: initialisation du sous-tableau Numrows[id_boucle] +// %s4: sauvegarde de la langue et calcul des invariants de boucle sur elle +// %s5: boucle while sql_fetch ou str_repeat si corps monotone +// %s6: restauration de la langue +// %s7: liberation de la ressource, en tenant compte du serveur SQL +// %s8: code de trace eventuel avant le retour + +define('CODE_CORPS_BOUCLE', '%s + $t0 = ""; + // REQUETE + $result = calculer_select($select, $from, $type, $where, $join, $groupby, $orderby, $limit, $having, $table, $id, $connect, + array(%s)); + if ($result) { + %s%s$SP++; + // RESULTATS + %s + %s@sql_free($result%s); + }%s + return $t0;' +); + +// http://doc.spip.org/@calculer_boucle_nonrec +function calculer_boucle_nonrec($id_boucle, &$boucles, $trace) { + + $boucle = &$boucles[$id_boucle]; + $return = $boucle->return; + $type_boucle = $boucle->type_requete; + $primary = $boucle->primary; + $constant = preg_match(CODE_MONOTONE, str_replace("\\'",'', $return)); + $flag_cpt = $boucle->mode_partie ||$boucle->cptrows; + $corps = ''; + + // faudrait expanser le foreach a la compil, car y en a souvent qu'un + // et puis faire un [] plutot qu'un "','." + if ($boucle->doublons) + $corps .= "\n\t\t\tforeach(" . $boucle->doublons . ' as $k) $doublons[$k] .= "," . ' . + index_pile($id_boucle, $primary, $boucles) + . "; // doublons\n"; + + // La boucle doit-elle selectionner la langue ? + // -. par defaut, les boucles suivantes le font + // (sauf si forcer_lang==true ou si le titre contient ). + // - . a moins d'une demande explicite via {!lang_select} + if (!$constant && $boucle->lang_select != 'non' && + (($boucle->lang_select == 'oui') || + in_array($type_boucle, array( + 'articles', 'rubriques', 'hierarchie', 'breves' + ))) + ) { + // Memoriser la langue avant la boucle et la restituer apres + // afin que le corps de boucle affecte la globale directement + $init_lang = "lang_select(\$GLOBALS['spip_lang']);\n\t"; + $fin_lang = "lang_select();\n\t"; + + $corps .= + "\n\t\tlang_select_public(" + . index_pile($id_boucle, 'lang', $boucles) + . ", '".$boucle->lang_select."'" + . (in_array($type_boucle, array( + 'articles', 'rubriques', 'hierarchie', 'breves' + )) ? ', '.index_pile($id_boucle, 'titre', $boucles) : '') + . ');'; + } + else { + $init_lang = ''; + $fin_lang = ''; + // sortir les appels au traducteur (invariants de boucle) + if (strpos($return, '?php') === false + AND preg_match_all("/\W(_T[(]'[^']*'[)])/", $return, $r)) { + $i = 1; + foreach($r[1] as $t) { + $init_lang .= "\n\t\$l$i = $t;"; + $return = str_replace($t, "\$l$i", $return); + $i++; + } + } + } + + // gestion optimale des separateurs et des boucles constantes + if (count($boucle->separateur)) + $code_sep = ("'" . str_replace("'","\'",join('',$boucle->separateur)) . "'"); + + $corps .= + ((!$boucle->separateur) ? + (($constant && !$corps && !$flag_cpt) ? $return : + (($return==="''") ? '' : + ("\n\t\t" . '$t0 .= ' . $return . ";"))) : + ("\n\t\t\$t1 " . + ((strpos($return, '$t1.') === 0) ? + (".=" . substr($return,4)) : + ('= ' . $return)) . + ";\n\t\t" . + '$t0 .= (($t1 && $t0) ? ' . $code_sep . " : '') . \$t1;")); + + // Calculer les invalideurs si c'est une boucle non constante et si on + // souhaite invalider ces elements + if (!$constant AND $primary) { + include_spip('inc/invalideur'); + if (function_exists($i = 'calcul_invalideurs')) + $corps = $i($corps, $primary, $boucles, $id_boucle); + } + + // gerer le compteur de boucle + // avec ou sans son utilisation par les criteres {1/3} {1,4} {n-2,1}... + + if ($boucle->partie OR $boucle->cptrows) + $corps = "\n\t\t\$Numrows['$id_boucle']['compteur_boucle']++;" + . $boucle->partie + . $corps; + + $serveur = !$boucle->sql_serveur ? '' + : (', ' . _q($boucle->sql_serveur)); + + // si le corps est une constante, ne pas appeler le serveur N fois! + + if (preg_match(CODE_MONOTONE,str_replace("\\'",'',$corps), $r)) { + if (!isset($r[2]) OR (!$r[2])) { + if (!$boucle->numrows) + return "\n\t\$t0 = '';"; + else + $corps = ""; + } else { + $boucle->numrows = true; + $corps = "\n\t\$t0 = str_repeat($corps, \$Numrows['$id_boucle']['total']);"; + } + } else $corps = "while (\$Pile[\$SP] = @sql_fetch(\$result$serveur)) {\n$corps\n }"; + + $count = ''; + if (!$boucle->select) { + if (!$boucle->numrows OR $boucle->limit OR $boucle_mode_partie OR $boucle->group) + $count = '1'; + else $count = 'count(*)'; + $boucles[$id_boucle]->select[]= $count; + } + + if ($flag_cpt) + $nums = "\n\t// COMPTEUR\n\t" + . "\$Numrows['$id_boucle']['compteur_boucle'] = 0;\n\t"; + else $nums = ''; + + if ($boucle->numrows OR $boucle->mode_partie) { + if ($count == 'count(*)') + $count = "array_shift(sql_fetch(\$result$serveur))"; + else $count = "sql_count(\$result$serveur)"; + $nums .= "\$Numrows['$id_boucle']['total'] = @intval($count);" + . $boucle->mode_partie + . "\n\t"; + } + + // Ne calculer la requete que maintenant + // car ce qui precede appelle index_pile qui influe dessus + + $init = (($init = $boucles[$id_boucle]->doublons) + ? ("\n\t$init = array();") : '') + . calculer_requete_sql($boucles[$id_boucle]); + + $contexte = memoriser_contexte_compil($boucle); + + return sprintf(CODE_CORPS_BOUCLE, $init, $contexte, $nums, $init_lang, $corps, $fin_lang, $serveur, $trace); +} + + +// http://doc.spip.org/@calculer_requete_sql +function calculer_requete_sql($boucle) +{ + return ($boucle->hierarchie ? "\n\t$boucle->hierarchie" : '') + . $boucle->in + . $boucle->hash + . calculer_dec('$table', "'" . $boucle->id_table ."'") + . calculer_dec('$id', "'" . $boucle->id_boucle ."'") + # En absence de champ c'est un decompte : + . calculer_dec('$from', calculer_from($boucle)) + . calculer_dec('$type', calculer_from_type($boucle)) + . calculer_dec('$groupby', 'array(' . (($g=join("\",\n\t\t\"",$boucle->group))?'"'.$g.'"':'') . ")") + . calculer_dec('$select', 'array("' . join("\",\n\t\t\"", $boucle->select). "\")") + . calculer_dec('$orderby', 'array(' . calculer_order($boucle) . ")") + . calculer_dec('$where', calculer_dump_array($boucle->where)) + . calculer_dec('$join', calculer_dump_join($boucle->join)) + . calculer_dec('$limit', (strpos($boucle->limit, 'intval') === false ? + "'".$boucle->limit."'" : + $boucle->limit)) + . calculer_dec('$having', calculer_dump_array($boucle->having)); +} + +function memoriser_contexte_compil($p) { + return join(',', array( + _q($p->descr['sourcefile']), + _q($p->descr['nom']), + @_q($p->id_boucle), + intval($p->ligne), + '$GLOBALS[\'spip_lang\']')); +} + +function reconstruire_contexte_compil($context_compil) +{ + if (!is_array($context_compil)) return $context_compil; + include_spip('public/interfaces'); + $p = new Contexte; + $p->descr = array('sourcefile' => $context_compil[0], + 'nom' => $context_compil[1]); + $p->id_boucle = $context_compil[2]; + $p->ligne = $context_compil[3]; + $p->lang = $context_compil[4]; + return $p; +} + +// http://doc.spip.org/@calculer_dec +function calculer_dec($nom, $val) +{ + $static = "static "; + if ( + strpos($val, '$') !== false + OR strpos($val, 'sql_') !== false + OR ( + $test = str_replace(array("array(",'\"',"\'"),array("","",""),$val) // supprimer les array( et les echappements de guillemets + AND strpos($test,"(")!==FALSE // si pas de parenthese ouvrante, pas de fonction, on peut sortir + AND $test = preg_replace(",'[^']*',UimsS","",$test) // supprimer les chaines qui peuvent contenir des fonctions SQL qui ne genent pas + AND preg_match(",\w+\s*\(,UimsS",$test,$regs) // tester la presence de fonctions restantes + ) + ){ + $static = ""; + } + return "\n\t" . $static . $nom . ' = ' . $val . ';'; +} + +// http://doc.spip.org/@calculer_dump_array +function calculer_dump_array($a) +{ + if (!is_array($a)) return $a ; + $res = ""; + if ($a AND $a[0] == "'?'") + return ("(" . calculer_dump_array($a[1]) . + " ? " . calculer_dump_array($a[2]) . + " : " . calculer_dump_array($a[3]) . + ")"); + else { + foreach($a as $v) $res .= ", " . calculer_dump_array($v); + return "\n\t\t\tarray(" . substr($res,2) . ')'; + } +} + +// http://doc.spip.org/@calculer_dump_join +function calculer_dump_join($a) +{ + $res = ""; + foreach($a as $k => $v) + $res .= ", '$k' => array(".implode(',',$v).")"; + return 'array(' . substr($res,2) . ')'; +} + +// http://doc.spip.org/@calculer_from +function calculer_from(&$boucle) +{ + $res = ""; + foreach($boucle->from as $k => $v) $res .= ",'$k' => '$v'"; + return 'array(' . substr($res,1) . ')'; +} + +// http://doc.spip.org/@calculer_from_type +function calculer_from_type(&$boucle) +{ + $res = ""; + foreach($boucle->from_type as $k => $v) $res .= ",'$k' => '$v'"; + return 'array(' . substr($res,1) . ')'; +} + +// http://doc.spip.org/@calculer_order +function calculer_order(&$boucle) +{ + if (!$order = $boucle->order + AND !$order = $boucle->default_order) + $order = array(); + + /*if (isset($boucle->modificateur['collate'])){ + $col = "." . $boucle->modificateur['collate']; + foreach($order as $k=>$o) + if (strpos($order[$k],'COLLATE')===false) + $order[$k].= $col; + }*/ + return join(', ', $order); +} + +// Production du code PHP a partir de la sequence livree par le phraseur +// $boucles est passe par reference pour affectation par index_pile. +// Retourne une expression PHP, +// (qui sera argument d'un Return ou la partie droite d'une affectation). + +// http://doc.spip.org/@calculer_liste +function calculer_liste($tableau, $descr, &$boucles, $id_boucle='') { + if (!$tableau) return "''"; + if (!isset($descr['niv'])) $descr['niv'] = 0; + $codes = compile_cas($tableau, $descr, $boucles, $id_boucle); + if ($codes === false) return false; + $n = count($codes); + if (!$n) return "''"; + $tab = str_repeat("\t", $descr['niv']); + if (_request('var_mode_affiche') != 'validation') { + if ($n==1) + return $codes[0]; + else { + $res = ''; + foreach($codes as $code) { + if (!preg_match("/^'[^']*'$/", $code) + OR substr($res,-1,1)!=="'") + $res .= " .\n$tab$code"; + else { + $res = substr($res,0,-1) . substr($code,1); + } + } + return '(' . substr($res,2+$descr['niv']) . ')'; + } + } else { + $nom = $descr['nom'] . $id_boucle . ($descr['niv']?$descr['niv']:''); + return "join('', array_map('array_shift', \$GLOBALS['debug_objets']['sequence']['$nom'] = array(" . join(" ,\n$tab", $codes) . ")))"; + } +} + +define('_REGEXP_COND_VIDE_NONVIDE',"/^[(](.*)[?]\s*''\s*:\s*('[^']+')\s*[)]$/"); +define('_REGEXP_COND_NONVIDE_VIDE',"/^[(](.*)[?]\s*('[^']+')\s*:\s*''\s*[)]$/"); +define('_REGEXP_CONCAT_NON_VIDE', "/^(.*)[.]\s*'[^']+'\s*$/"); + +// http://doc.spip.org/@compile_cas +function compile_cas($tableau, $descr, &$boucles, $id_boucle) { + + $codes = array(); + // cas de la boucle recursive + if (is_array($id_boucle)) + $id_boucle = $id_boucle[0]; + $type = !$id_boucle ? '' : $boucles[$id_boucle]->type_requete; + $tab = str_repeat("\t", ++$descr['niv']); + $mode = _request('var_mode_affiche'); + $err_e_c = ''; + // chaque commentaire introduit dans le code doit commencer + // par un caractere distinguant le cas, pour exploitation par debug. + foreach ($tableau as $p) { + + switch($p->type) { + // texte seul + case 'texte': + $code = "'".str_replace(array("\\","'"),array("\\\\","\\'"), $p->texte)."'"; + + $commentaire= strlen($p->texte) . " signes"; + $avant=''; + $apres=''; + $altern = "''"; + break; + + case 'polyglotte': + $code = ""; + foreach($p->traductions as $k => $v) { + $code .= ",'" . + str_replace(array("\\","'"),array("\\\\","\\'"), $k) . + "' => '" . + str_replace(array("\\","'"),array("\\\\","\\'"), $v) . + "'"; + } + $code = "choisir_traduction(array(" . + substr($code,1) . + "))"; + $commentaire= '&'; + $avant=''; + $apres=''; + $altern = "''"; + break; + + // inclure + case 'include': + $p->descr = $descr; + $code = calculer_inclure($p, $boucles, $id_boucle); + if ($code === false) { + $err_e_c = true; + $code = "''"; + } else { + $commentaire = ''; + $avant=''; + $apres=''; + $altern = "''"; + } + break; + + // boucle + case 'boucle': + $nom = $p->id_boucle; + $newdescr = $descr; + $newdescr['id_mere'] = $nom; + $newdescr['niv']++; + $avant = calculer_liste($p->avant, + $newdescr, $boucles, $id_boucle); + $apres = calculer_liste($p->apres, + $newdescr, $boucles, $id_boucle); + $newdescr['niv']--; + $altern = calculer_liste($p->altern, + $newdescr, $boucles, $id_boucle); + if (($avant === false) OR ($apres === false) OR ($altern === false)) { + $err_e_c = true; + $code = "''"; + } else { + $code = 'BOUCLE' . + str_replace("-","_", $nom) . $descr['nom'] . + '($Cache, $Pile, $doublons, $Numrows, $SP)'; + $commentaire= "?$nom"; + if (!$boucles[$nom]->milieu + AND $boucles[$nom]->type_requete <> 'boucle') { + if ($altern != "''") $code .= "\n. $altern"; + if ($avant<>"''" OR $apres<>"''") + spip_log("boucle $nom toujours vide, code superflu dans $id"); + $avant = $apres = $altern = "''"; + } else if ($altern != "''") $altern = "($altern)"; + } + break; + + case 'idiome': + $l = array(); + foreach ($p->arg as $k => $v) { + if ($k) $l[]= _q($k).' => '.calculer_liste($v,$descr,$boucles,$id_boucle); + } + $l = !$l ? '' : (", array(".implode(",\n",$l).")"); + $code = "_T('" . $p->module . ":" .$p->nom_champ . "'$l)"; + if ($p->param) { + $p->id_boucle = $id_boucle; + $p->boucles = &$boucles; + $code = compose_filtres($p, $code); + } + $commentaire = ":"; + $avant=''; + $apres=''; + $altern = "''"; + break; + + case 'champ': + + // cette structure pourrait etre completee des le phrase' (a faire) + $p->id_boucle = $id_boucle; + $p->boucles = &$boucles; + $p->descr = $descr; + #$p->interdire_scripts = true; + $p->type_requete = $type; + + $code = calculer_champ($p); + $commentaire = '#' . $p->nom_champ . $p->etoile; + $avant = calculer_liste($p->avant, + $descr, $boucles, $id_boucle); + $apres = calculer_liste($p->apres, + $descr, $boucles, $id_boucle); + $altern = "''"; + // Si la valeur est destinee a une comparaison a '' + // forcer la conversion en une chaine par strval + // si ca peut etre autre chose qu'une chaine + if (($avant != "''" OR $apres != "''") + AND $code[0]!= "'" +# AND (strpos($code,'interdire_scripts') !== 0) + AND !preg_match(_REGEXP_COND_VIDE_NONVIDE, $code) + AND !preg_match(_REGEXP_COND_NONVIDE_VIDE, $code) + AND !preg_match(_REGEXP_CONCAT_NON_VIDE, $code)) + $code = "strval($code)"; + break; + + default: + // Erreur de construction de l'arbre de syntaxe abstraite + $code = "''"; + $p->descr = $descr; + $err_e_c = array('zbug_erreur_compilation'); + erreur_squelette($err_e_c, $p); + } // switch + + if ($code != "''") { + $code = compile_retour($code, $avant, $apres, $altern, $tab, $descr['niv']); + $codes[]= (($mode == 'validation') ? + "array($code, '$commentaire', " . $p->ligne . ")" + : (($mode == 'code') ? + "\n// $commentaire\n$code" : + $code)); + } + } // foreach + + return $err_e_c ? false : $codes; +} + +// production d'une expression conditionnelle ((v=EXP) ? (p . v .s) : a) +// mais si EXP est de la forme (t ? 'C' : '') on produit (t ? (p . C . s) : a) +// de meme si EXP est de la forme (t ? '' : 'C') + +// http://doc.spip.org/@compile_retour +function compile_retour($code, $avant, $apres, $altern, $tab, $n) +{ + if ($avant == "''") $avant = ''; + if ($apres == "''") $apres = ''; + if (!$avant AND !$apres AND ($altern==="''")) return $code; + + if (preg_match(_REGEXP_CONCAT_NON_VIDE, $code)) { + $t = $code; + $cond = ''; + } elseif (preg_match(_REGEXP_COND_VIDE_NONVIDE,$code, $r)) { + $t = $r[2]; + $cond = '!' . $r[1]; + } else if (preg_match(_REGEXP_COND_NONVIDE_VIDE,$code, $r)) { + $t = $r[2]; + $cond = $r[1]; + } else { + $t = '$t' . $n; + $cond = "($t = $code)!==''"; + } + + $res = (!$avant ? "" : "$avant . ") . + $t . + (!$apres ? "" : " . $apres"); + + if ($res !== $t) $res = "($res)"; + return !$cond ? $res : "($cond ?\n\t$tab$res :\n\t$tab$altern)"; +} + + +function compile_inclure_doublons($lexemes) +{ + foreach($lexemes as $v) + if($v->type === 'include' AND $v->param) + foreach($v->param as $r) + if (trim($r[0]) === 'doublons') + return true; + return false; +} + +// Prend en argument le texte d'un squelette, le nom de son fichier d'origine, +// sa grammaire et un nom. Retourne False en cas d'erreur, +// sinon retourne un tableau de fonctions PHP compilees a evaluer, +// notamment une fonction portant ce nom et calculant une page. +// Pour appeler la fonction produite, lui fournir 2 tableaux de 1 e'le'ment: +// - 1er: element 'cache' => nom (du fichier ou` mettre la page) +// - 2e: element 0 contenant un environnement ('id_article => $id_article, etc) +// Elle retournera alors un tableau de 5 e'le'ments: +// - 'texte' => page HTML, application du squelette a` l'environnement; +// - 'squelette' => le nom du squelette +// - 'process_ins' => 'html' ou 'php' selon la pre'sence de PHP dynamique +// - 'invalideurs' => de'pendances de cette page, pour invalider son cache. +// - 'entetes' => tableau des entetes http +// En cas d'erreur, elle retournera un tableau des 2 premiers elements seulement + +// http://doc.spip.org/@public_compiler_dist +function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect=''){ + // Pre-traitement : reperer le charset du squelette, et le convertir + // Bonus : supprime le BOM + include_spip('inc/charsets'); + $squelette = transcoder_page($squelette); + + $descr = array('nom' => $nom, + 'gram' => $gram, + 'sourcefile' => $sourcefile, + 'squelette' => $squelette); + + // Phraser le squelette, selon sa grammaire + + $boucles = array(); + $f = charger_fonction('phraser_' . $gram, 'public'); + + $squelette = $f($squelette, '', $boucles, $descr); + + return compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect); +} + +// Point d'entree pour arbre de syntaxe abstraite fourni en premier argument +// Autres specifications comme ci-dessus + +function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect=''){ + global $tables_jointures; + static $trouver_table; + spip_timer('calcul_skel'); + + if (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode'] == 'debug') { + $GLOBALS['debug_objets']['squelette'][$nom] = $descr['squelette']; + $GLOBALS['debug_objets']['sourcefile'][$nom] = $sourcefile; + + if (!isset($GLOBALS['debug_objets']['principal'])) + $GLOBALS['debug_objets']['principal'] = $nom; + } + foreach ($boucles as $id => $boucle) { + $GLOBALS['debug_objets']['boucle'][$nom.$id] = $boucle; + } + $descr['documents'] = compile_inclure_doublons($squelette); + + // Demander la description des tables une fois pour toutes + // et reperer si les doublons sont demandes + // pour un inclure ou une boucle document + // c'est utile a la fonction champs_traitements + if (!$trouver_table) + $trouver_table = charger_fonction('trouver_table', 'base'); + + foreach($boucles as $id => $boucle) { + if (!($type = $boucle->type_requete)) continue; + if (!$descr['documents'] AND ( + (($type == 'documents') AND $boucle->doublons) OR + compile_inclure_doublons($boucle->avant) OR + compile_inclure_doublons($boucle->apres) OR + compile_inclure_doublons($boucle->milieu) OR + compile_inclure_doublons($boucle->altern))) + $descr['documents'] = true; + if ($type != 'boucle') { + if (!$boucles[$id]->sql_serveur AND $connect) + $boucles[$id]->sql_serveur = $connect; + $show = $trouver_table($type, $boucles[$id]->sql_serveur); + // si la table n'existe pas avec le connecteur par defaut, + // c'est peut etre une table qui necessite son connecteur dedie fourni + // permet une ecriture allegee (GEO) -> (geo:GEO) + if (!$show AND $show=$trouver_table($type, strtolower($type))) + $boucles[$id]->sql_serveur = strtolower($type); + if ($show) { + $boucles[$id]->show = $show; + // recopie les infos les plus importantes + $boucles[$id]->primary = $show['key']["PRIMARY KEY"]; + $boucles[$id]->id_table = $x = $show['id_table']; + $boucles[$id]->from[$x] = $nom_table = $show['table']; + + $boucles[$id]->descr = &$descr; + if ((!$boucles[$id]->jointures) + AND (isset($tables_jointures[$nom_table])) + AND is_array($x = $tables_jointures[$nom_table])) + $boucles[$id]->jointures = $x; + if ($boucles[$id]->jointures_explicites){ + $jointures = preg_split("/\s+/",$boucles[$id]->jointures_explicites); + while ($j=array_pop($jointures)) + array_unshift($boucles[$id]->jointures,$j); + } + } else { + // Pas une erreur si la table est optionnelle + if ($boucles[$id]->table_optionnelle) + $boucles[$id]->type_requete = ''; + else { + $boucles[$id]->type_requete = false; + $boucle = $boucles[$id]; + $x = (!$boucle->sql_serveur ? '' : + ($boucle->sql_serveur . ":")) . + $type; + $msg = array('zbug_table_inconnue', + array('table' => $x)); + erreur_squelette($msg, $boucle); + } + } + } + } + + // Commencer par reperer les boucles appelees explicitement + // car elles indexent les arguments de maniere derogatoire + foreach($boucles as $id => $boucle) { + if ($boucle->type_requete == 'boucle' AND $boucle->param) { + $boucles[$id]->descr = &$descr; + $rec = &$boucles[$boucle->param[0]]; + if (!$rec) { + $msg = array('zbug_boucle_recursive_undef', + array('nom' => $boucle->param[0])); + erreur_squelette($msg, $boucle); + $boucles[$id]->type_requete = false; + } else { + $rec->externe = $id; + $descr['id_mere'] = $id; + $boucles[$id]->return = + calculer_liste(array($rec), + $descr, + $boucles, + $boucle->param); + } + } + } + foreach($boucles as $id => $boucle) { + $id = strval($id); // attention au type dans index_pile + $type = $boucle->type_requete; + if ($type AND $type != 'boucle') { + $crit = !$boucle->param ? true : calculer_criteres($id, $boucles); + $descr['id_mere'] = $id; + $boucles[$id]->return = + calculer_liste($boucle->milieu, + $descr, + $boucles, + $id); + // Si les criteres se sont mal compiles + // ne pas tenter d'assembler le code final + // (mais compiler le corps pour detection d'erreurs) + if (is_array($crit)) + $boucles[$id]->type_requete = false; + } + } + + // idem pour la racine + $descr['id_mere'] = ''; + $corps = calculer_liste($squelette, $descr, $boucles); + $debug = (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode']=='debug'); + + if ($debug) { + include_spip('public/decompiler'); + include_spip('public/format_' . _EXTENSION_SQUELETTES); + } + // Calcul du corps de toutes les fonctions PHP, + // en particulier les requetes SQL et TOTAL_BOUCLE + // de'terminables seulement maintenant + + foreach($boucles as $id => $boucle) { + $boucle = $boucles[$id] = pipeline('pre_boucle', $boucle); + if ($boucle->return === false) continue; + // appeler la fonction de definition de la boucle + + if ($req = $boucle->type_requete) { + $f = 'boucle_'.strtoupper($req); + // si pas de definition perso, definition spip + if (!function_exists($f)) $f = $f.'_dist'; + // laquelle a une definition par defaut + if (!function_exists($f)) $f = 'boucle_DEFAUT'; + if (!function_exists($f)) $f = 'boucle_DEFAUT_dist'; + $req = "\n\n\tstatic \$connect = " . + _q($boucle->sql_serveur) . + ";" . + $f($id, $boucles); + } else $req = ("\n\treturn '';"); + + $boucles[$id]->return = + "function BOUCLE" . strtr($id,"-","_") . $nom . + '(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' . + $req . + "\n}\n\n"; + + if ($debug) + $GLOBALS['debug_objets']['code'][$nom.$id] = $boucles[$id]->return; + } + + // Au final, si le corps ou un critere au moins s'est mal compile + // retourner False, sinon inserer leur decompilation + if (is_bool($corps)) return false; + foreach($boucles as $id => $boucle) { + if ($boucle->return === false) return false; + $boucle->return = "\n\n/* BOUCLE " . + $boucle->type_requete . + " " . + (!$debug ? '' : + str_replace('*/', '* /', + decompiler_criteres($boucle->param, + $boucle->criteres))) . + " */\n\n " . + $boucle->return; + } + + $secondes = spip_timer('calcul_skel'); + spip_log("COMPIL ($secondes) [$sourcefile] $nom.php"); + + // Assimiler la fct principale a une boucle anonyme, c'est plus simple + $code = new Boucle; + $code->descr = $descr; + $code->return = ' +// +// Fonction principale du squelette ' . + $sourcefile . + ($connect ? " pour $connect" : '') . + (!CODE_COMMENTE ? '' : "\n// Temps de compilation total: $secondes") . + "\n//" . + (!$debug ? '' : ("\n/*\n" . + str_replace('*/', '* /', public_decompiler($squelette)) + . "\n*/")) . " + +function " . $nom . '($Cache, $Pile, $doublons=array(), $Numrows=array(), $SP=0) { + +' + // reporter de maniere securisee les doublons inclus +.' + if (isset($Pile[0]["doublons"]) AND is_array($Pile[0]["doublons"])) + $doublons = nettoyer_env_doublons($Pile[0]["doublons"]); + + $connect = ' . + _q($connect) . '; + $page = ' . + // ATTENTION, le calcul de l'expression $corps affectera $Cache + // c'est pourquoi on l'affecte a la variable auxiliaire $page. + // avant de referencer $Cache + $corps . "; + + return analyse_resultat_skel(".var_export($nom,true) + .", \$Cache, \$page, ".var_export($sourcefile,true)."); +}"; + + $boucles[''] = $code; + return $boucles; +} + +?>