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 = "(\$cc=sql_fetch(\$result$serveur))?array_shift(\$cc):0"; 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(); $code = ''; foreach ($p->arg as $k => $v){ $_v = calculer_liste($v, $descr, $boucles, $id_boucle); if ($k) $l[] = _q($k) . ' => ' . $_v; else $code = $_v; } /// Si le module n'est pas fourni, /// l'expliciter sauf si calcule if ($p->module) { $m = $p->module .':'.$p->nom_champ; } elseif ($p->nom_champ) { $m = MODULES_IDIOMES .':'.$p->nom_champ; } else $m = ''; $code = (!$code ? "'$m'" : ($m ? "'$m' . $code" : ("(strpos(\$x=$code, ':') ? \$x : ('" . MODULES_IDIOMES . ":' . \$x))"))) . (!$l ? '' : (", array(" . implode(",\n", $l) . ")")); $code = "_T($code)"; 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"); // $connect n'est pas sûr : on nettoie $connect = preg_replace(',[^\w],', '', $connect); // 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; } ?>