function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect = '') { static $trouver_table; spip_timer('calcul_skel'); if (defined('_VAR_MODE') and _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 != TYPE_RECURSIF) { if (!$boucles[$id]->sql_serveur and $connect) { $boucles[$id]->sql_serveur = $connect; } // chercher dans les iterateurs du repertoire iterateur/ if ($g = charger_fonction(preg_replace('/\\W/', '_', $boucle->type_requete), 'iterateur', true)) { $boucles[$id] = $g($boucle); // sinon, en cas de requeteur d'un type predefini, // utiliser les informations donnees par le requeteur // cas "php:xx" et "data:xx". } else { if ($boucle->sql_serveur and $requeteur = charger_fonction($boucle->sql_serveur, 'requeteur', true)) { $requeteur($boucles, $boucle, $id); // utiliser la description des champs transmis } else { $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 = isset($show['key']["PRIMARY KEY"]) ? $show['key']["PRIMARY KEY"] : ''; $boucles[$id]->id_table = $x = preg_replace(",^spip_,", "", $show['id_table']); $boucles[$id]->from[$x] = $nom_table = $show['table']; $boucles[$id]->iterateur = 'SQL'; $boucles[$id]->descr =& $descr; if (!$boucles[$id]->jointures and is_array($show['tables_jointures']) and count($x = $show['tables_jointures'])) { $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 == TYPE_RECURSIF 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 != TYPE_RECURSIF) { $res = ''; if ($boucle->param) { // retourne un tableau en cas d'erreur $res = 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($res)) { $boucles[$id]->type_requete = false; } } } // idem pour la racine $descr['id_mere'] = ''; $corps = calculer_liste($squelette, $descr, $boucles); // 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) { $corps = false; continue; } // appeler la fonction de definition de la boucle if ($req = $boucle->type_requete) { // boucle personnalisée ? $table = strtoupper($boucle->type_requete); $serveur = strtolower($boucle->sql_serveur); if ((!$serveur or !function_exists($f = "boucle_" . $serveur . "_" . $table) and !function_exists($f = $f . "_dist")) and !function_exists($f = "boucle_" . $table) and !function_exists($f = $f . "_dist")) { // fonction de boucle standard if (!function_exists($f = 'boucle_DEFAUT')) { $f = 'boucle_DEFAUT_dist'; } } $req = "\n\n\tstatic \$command = array();\n\t" . "static \$connect;\n\t" . "\$command['connect'] = \$connect = " . _q($boucle->sql_serveur) . ";" . $f($id, $boucles); } else { $req = "\n\treturn '';"; } $boucles[$id]->return = "\n\nfunction BOUCLE" . strtr($id, "-", "_") . $nom . '(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' . $req . "\n}\n"; } // 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; } $principal = "\nfunction " . $nom . '($Cache, $Pile, $doublons=array(), $Numrows=array(), $SP=0) { ' . ' if (isset($Pile[0]["doublons"]) AND is_array($Pile[0]["doublons"])) $doublons = nettoyer_env_doublons($Pile[0]["doublons"]); $connect = ' . _q($connect) . '; $page = ' . $corps . ";\n\n\treturn analyse_resultat_skel(" . var_export($nom, true) . ", \$Cache, \$page, " . var_export($sourcefile, true) . ");\n}"; $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, pour retourner un resultat 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//\n" . $principal; $boucles[''] = $code; return $boucles; }
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; }