// function calculer_inclure($struct, $descr, &$boucles, $id_boucle) { $fichier = $struct->texte; if (!($path = find_in_path($fichier))) { spip_log("ERREUR: impossible"); erreur_squelette(_T('zbug_info_erreur_squelette'), "<INCLURE($fichier)> - " ._T('fichier_introuvable', array('fichier' => $fichier))); return "''"; } $l = array(); foreach($struct->param as $val) { $var = array_shift($val); $l[] = "\'$var\' => \'' . addslashes(" . ($val ? calculer_liste($val[0], $descr, $boucles, $id_boucle) : (($var =='lang') ? '$GLOBALS["spip_lang"]' : index_pile($id_boucle, $var, $boucles))) . ") . '\\'"; } return "\n'<". "?php\n\t\$contexte_inclus = array(" . join(",\n\t",$l) . ");" . "\n\tinclude(\\'$path\\');" . "\n?'." . "'>'"; } // // calculer_boucle() produit le corps PHP d'une boucle Spip // Sauf pour les recursives, ce corps est un Select SQL + While PHP // remplissant une variable $t0 retournee en valeur // function calculer_boucle($id_boucle, &$boucles) { $boucle = &$boucles[$id_boucle]; $return = $boucle->return; $type_boucle = $boucle->type_requete; if ($type_boucle == 'boucle') { $corps = "\n \$t0 = " . $return . ";"; $init = ""; } else { $primary = $boucle->primary; $constant = ereg(CODE_MONOTONE,$return); // Cas {1/3} {1,4} {n-2,1}... $flag_cpt = $boucle->mode_partie || // pas '$compteur' a cause du cas 0 strpos($return,'compteur_boucle'); // // Creer le debut du corps de la boucle : // $corps = ''; if ($flag_cpt) $corps = "\n \$Numrows['$id_boucle']['compteur_boucle']++;"; if ($boucle->mode_partie) $corps .= " if (\$Numrows['$id_boucle']['compteur_boucle']-1 >= \$debut_boucle AND \$Numrows['$id_boucle']['compteur_boucle']-1 <= \$fin_boucle) {"; // Calculer les invalideurs si c'est une boucle non constante if ($primary && !$constant) $corps .= "\n\t\t\$Cache['$primary'][" . (($primary != 'id_forum') ? index_pile($id_boucle, $primary, $boucles) : ("calcul_index_forum(" . // Retournera 4 [$SP] mais force la demande du champ a MySQL index_pile($id_boucle, 'id_article', $boucles) . ',' . index_pile($id_boucle, 'id_breve', $boucles) . ',' . index_pile($id_boucle, 'id_rubrique', $boucles) .',' . index_pile($id_boucle, 'id_syndic', $boucles) . ")")) . "] = 1; // invalideurs\n"; if ($boucle->doublons) $corps .= " \$doublons[".$boucle->doublons."] .= ','. " . index_pile($id_boucle, $primary, $boucles) . "; // doublons\n"; if (count($boucle->separateur)) $code_sep = ("'" . ereg_replace("'","\'",join('',$boucle->separateur)) . "'"); $init = ''; $fin = ''; // La boucle doit-elle selectionner la langue ? // -. par defaut, les boucles suivantes le font // "peut-etre", c'est-a-dire si forcer_lang == false. // - . a moins d'une demande explicite if (!$constant && $boucle->lang_select != 'non' && (($boucle->lang_select == 'oui') || ( $type_boucle == 'articles' OR $type_boucle == 'rubriques' OR $type_boucle == 'hierarchie' OR $type_boucle == 'breves' ))) { $corps .= (($boucle->lang_select != 'oui') ? "\t\tif (!\$GLOBALS['forcer_lang'])\n\t " : '') . "\t\t\$GLOBALS['spip_lang'] = (\$x = " . index_pile($id_boucle, 'lang', $boucles) . ') ? $x : $old_lang;'; // Memoriser la langue avant la boucle pour la restituer apres $init .= "\n \$old_lang = \$GLOBALS['spip_lang'];"; $fin .= "\n \$GLOBALS['spip_lang'] = \$old_lang;"; } // gestion optimale des separateurs et des boucles constantes $corps .= ((!$boucle->separateur) ? (($constant && !$corps) ? $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;")); // Fin de parties if ($boucle->mode_partie) $corps .= "\n }\n"; // Gestion de la hierarchie (voir inc-boucles.php3) if ($boucle->hierarchie) $init .= "\n ".$boucle->hierarchie; // si le corps est une constante, ne pas appeler le serveur N fois! if (ereg(CODE_MONOTONE,$corps, $r)) { if (!$r[2]) { if (!$boucle->numrows) return 'return "";'; else $corps = ""; } else { $boucle->numrows = true; $corps = "\n ".'for($x=$Numrows["'.$id_boucle.'"]["total"];$x>0;$x--) $t0 .= ' . $corps .';'; } } else { $corps = ' // RESULTATS while ($Pile[$SP] = @spip_abstract_fetch($result,"' . $boucle->sql_serveur . '")) {' . "\n$corps\n }\n" . $fin ; } // // Requete // if (!$order = $boucle->order AND !$order = $boucle->default_order) $order = array(); $init .= $boucle->hash . "\n\n // REQUETE \$result = spip_abstract_select(\n\t\tarray(\"". # En absence de champ c'est un decompte : # prendre la primary pour avoir qqch # (COUNT incompatible avec le cas general ($boucle->select ? join("\",\n\t\t\"", $boucle->select) : ($boucle->id_table . "." . (($p = strpos($primary, ',')) ? substr($primary, 0, $p) : $primary))) . '"), # SELECT array("' . join('","', array_unique($boucle->from)) . '"), # FROM array(' . (!$boucle->where ? '' : ( '"' . join('", "', $boucle->where) . '"')) . "), # WHERE '".addslashes($boucle->group)."', # GROUP array(" . join(', ', $order) . "), # ORDER " . (strpos($boucle->limit, 'intval') === false ? "'".$boucle->limit."'" : $boucle->limit). ", # LIMIT '".$boucle->sous_requete."', # sous '" . (!$boucle->having ? "" : "(COUNT(*)> $boucle->having)")."', # HAVING '".$boucle->id_table."', # table '".$boucle->id_boucle."', # boucle '".$boucle->sql_serveur."'); # serveur"; $init .= "\n ".'$t0 = ""; $SP++;'; if ($flag_cpt) $init .= "\n \$Numrows['$id_boucle']['compteur_boucle'] = 0;"; if ($boucle->mode_partie) $init .= calculer_parties($boucles, $id_boucle); else if ($boucle->numrows) $init .= "\n \$Numrows['" . $id_boucle . "']['total'] = @spip_abstract_count(\$result,'" . $boucle->sql_serveur . "');"; // // Conclusion et retour // $corps .= "\n @spip_abstract_free(\$result,'" . $boucle->sql_serveur . "');"; } return $init . $corps . ## inserer le code d'envoi au debusqueur du resultat de la fonction (($GLOBALS['var_mode_affiche'] != 'resultat') ? "" : " boucle_debug_resultat('$id_boucle', 'resultat', \$t0);") . "\n return \$t0;"; } // // fonction traitant les criteres {1,n} (analyses dans inc-criteres) // ## a deplacer dans inc-criteres ?? function calculer_parties($boucles, $id_boucle) { $boucle = &$boucles[$id_boucle]; $partie = $boucle->partie; $mode_partie = $boucle->mode_partie; $total_parties = $boucle->total_parties; // Notes : // $debut_boucle et $fin_boucle sont les indices SQL du premier // et du dernier demandes dans la boucle : 0 pour le premier, // n-1 pour le dernier ; donc total_boucle = 1 + debut - fin // nombre total avant partition $retour = "\n\n // Partition\n " . '$nombre_boucle = @spip_abstract_count($result,"' . $boucle->sql_serveur . '");'; ereg("([+-/])([+-/])?", $mode_partie, $regs); list(,$op1,$op2) = $regs; // {1/3} if ($op1 == '/') { $pmoins1 = is_numeric($partie) ? ($partie-1) : "($partie-1)"; $totpos = is_numeric($total_parties) ? ($total_parties) : "($total_parties ? $total_parties : 1)"; $retour .= "\n " .'$debut_boucle = ceil(($nombre_boucle * ' . $pmoins1 . ')/' . $totpos . ");"; $fin = 'ceil (($nombre_boucle * ' . $partie . ')/' . $totpos . ") - 1"; } // {1,x} elseif ($op1 == '+') { $retour .= "\n " . '$debut_boucle = ' . $partie . ';'; } // {n-1,x} elseif ($op1 == '-') { $retour .= "\n " . '$debut_boucle = $nombre_boucle - ' . $partie . ';'; } // {x,1} if ($op2 == '+') { $fin = '$debut_boucle' . (is_numeric($total_parties) ? (($total_parties==1) ? "" :(' + ' . ($total_parties-1))): ('+' . $total_parties . ' - 1')); } // {x,n-1} elseif ($op2 == '-') { $fin = '$debut_boucle + $nombre_boucle - ' . (is_numeric($total_parties) ? ($total_parties+1) : ($total_parties . ' - 1')); } // Rabattre $fin_boucle sur le maximum $retour .= "\n " .'$fin_boucle = min(' . $fin . ', $nombre_boucle - 1);'; // calcul du total boucle final $retour .= "\n " .'$Numrows[\''.$id_boucle.'\']["total"] = max(0,$fin_boucle - $debut_boucle + 1);'; return $retour; } // 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). function calculer_liste($tableau, $descr, &$boucles, $id_boucle='') { if (!$tableau) return "''"; $codes = compile_cas($tableau, $descr, $boucles, $id_boucle); $n = count($codes); if (!$n) return "''"; if ($GLOBALS['var_mode_affiche'] != 'validation') return (($n==1) ? $codes[0] : "(" . join (" .\n$tab", $codes) . ")"); else return "debug_sequence('$id_boucle', '" . ($descr['nom']) . "', " . intval($descr['niv']) . ", array(" . join(" ,\n$tab", $codes) . "))"; } 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 = $boucles[$id_boucle]->type_requete; $descr['niv']++; for ($i=0; $i<=$descr['niv']; $i++) $tab .= "\t"; // 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 = "'".ereg_replace("([\\\\'])", "\\\\1", $p->texte)."'"; $commentaire= strlen($p->texte) . " signes"; $avant=''; $apres=''; $altern = "''"; break; case 'polyglotte': $code = ""; foreach($p->traductions as $k => $v) { $code .= ",'" . ereg_replace("([\\\\'])", "\\\\1", $k) . "' => '" . ereg_replace("([\\\\'])", "\\\\1", $v) . "'"; } $code = "multi_trad(array(" . substr($code,1) . "))"; $commentaire= '&'; $avant=''; $apres=''; $altern = "''"; break; // inclure case 'include': $code = calculer_inclure($p, $descr, $boucles, $id_boucle); $commentaire = '!' . $p->texte; $avant=''; $apres=''; $altern = "''"; break; // boucle case 'boucle': $nom = $p->id_boucle; $newdescr = $descr; $newdescr['id_mere'] = $nom; $newdescr['niv']++; $code = 'BOUCLE' . ereg_replace("-","_", $nom) . $descr['nom'] . '($Cache, $Pile, $doublons, $Numrows, $SP)'; $commentaire= "?$nom"; $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); break; case 'idiome': $p->code = "_T('" . $p->module . ":" .$p->nom_champ . "')"; $p->id_boucle = $id_boucle; $p->boucles = &$boucles; $p->statut = 'php'; // ne pas manger les espaces avec trim() $commentaire = ":"; $code = applique_filtres($p); $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->statut = 'html'; $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 = "''"; break; default: erreur_squelette(_T('zbug_info_erreur_squelette')); } // switch if ($avant == "''") $avant = ''; if ($apres == "''") $apres = ''; if ($avant||$apres||($altern!="''")) { $t = '$t' . $descr['niv']; $res = (!$avant ? "" : "$avant . ") . $t . (!$apres ? "" : " . $apres"); $code = "(($t = $code) ?\n\t$tab($res) :\n\t$tab($altern))"; } if ($code != "''") $codes[]= (($GLOBALS['var_mode_affiche'] == 'validation') ? "array(" . $p->ligne . ", '$commentaire', $code)" : (($GLOBALS['var_mode_affiche'] == 'code') ? "\n// $commentaire\n$code" : $code)); } // foreach return $codes; } // affichage du code produit function code_boucle(&$boucles, $id, $nom) { $boucle = &$boucles[$id]; // Indiquer la boucle en commentaire $pretty = ''; if ($boucle->type_requete != 'boucle') { // Resynthetiser les criteres foreach ($boucle->param as $param) { $s = ""; $sep = ""; foreach ($param as $t) { if (is_array($t)) { // toujours vrai normalement $s .= $sep; $c = $t[0]; if ($c->apres) $s .= ($c->apres . $c->texte . $c->apres); else { // faudrait decompiler aussi les balises... foreach ($t as $c) $s .= ($c->type == 'texte') ? $c->texte : '#...'; } $sep = ", "; } } $pretty .= ' {' . $s . '}'; } } $pretty = "BOUCLE$id(".strtoupper($boucle->type_requete) . ")" . ereg_replace("[\r\n]", " ", $pretty); return $pretty; } // Prend en argument le source d'un squelette, sa grammaire et un nom. // Retourne une fonction PHP/SQL portant ce nom et calculant une page HTML. // 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 retourne alors un tableau de 4 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. // (voir son utilisation, optionnelle, dans invalideur.php) // En cas d'erreur, elle retourne un tableau des 2 premiers elements seulement function calculer_squelette($squelette, $nom, $gram, $sourcefile) { # 3 variables qui sont en fait des constantes après chargement global $table_primary, $table_des_tables, $tables_des_serveurs_sql; // Phraser le squelette, selon sa grammaire // pour le moment: "html" seul connu (HTML+balises BOUCLE) $boucles = array(); spip_timer('calcul_skel'); include_local("inc-$gram-squel.php3"); $racine = phraser($squelette, '',$boucles, $nom); // tableau des informations sur le squelette $descr = array('nom' => $nom, 'documents' => false, 'sourcefile' => $sourcefile); // une boucle documents est conditionnee par tout le reste! foreach($boucles as $idb => $boucle) { if (($boucle->type_requete == 'documents') && $boucle->doublons) { $descr['documents'] = true; break; } } // 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') { $rec = &$boucles[$boucle->param[0]]; if (!$rec) { return array(_T('zbug_info_erreur_squelette'), ($boucle->param[0] . ' '. _T('zbug_boucle_recursive_undef'))); } else { $rec->externe = $id; $descr['id_mere'] = $id; $boucles[$id]->return = calculer_liste(array($rec), $descr, $boucles, $boucle->param); } } } foreach($boucles as $id => $boucle) { $type = $boucle->type_requete; if ($type != 'boucle') { $boucles[$id]->id_table = $table_des_tables[$type]; if ($boucles[$id]->id_table) { $boucles[$id]->primary = $table_primary[$type]; } else { // table non Spip. $boucles[$id]->id_table = $type; $serveur = $boucle->sql_serveur; $x = &$tables_des_serveurs_sql[$serveur ? $serveur : 'localhost'][$type]['key']; $boucles[$id]->primary = ($x["PRIMARY KEY"] ? $x["PRIMARY KEY"] : $x["KEY"]); } if ($boucle->param) { $res = calculer_criteres($id, $boucles); if (is_array($res)) return $res; # erreur } $descr['id_mere'] = $id; $boucles[$id]->return = calculer_liste($boucle->milieu, $descr, $boucles, $id); } } // idem pour la racine $descr['id_mere'] = ''; $corps = calculer_liste($racine, $descr, $boucles); // Calcul du corps de toutes les fonctions PHP, // en particulier les requetes SQL et TOTAL_BOUCLE // de'terminables seulement maintenant // Les 4 premiers parame`tres sont passe's par re'fe'rence // (les 1er et 3e pour modif, les 2 et 4 pour gain de place) foreach($boucles as $id => $boucle) { // appeler la fonction de definition de la boucle $f = 'boucle_'.strtoupper($boucle->type_requete); // 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'; $boucles[$id]->return = "function BOUCLE" . ereg_replace("-","_",$id) . $nom . '(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' . $f($id, $boucles) . "\n}\n\n"; if ($GLOBALS['var_mode'] == 'debug') boucle_debug_compile ($id, $nom, $boucles[$id]->return); } $code = ""; foreach($boucles as $id => $boucle) { $code .= "\n//\n// type_requete . ">\n//\n" . $boucle->return; } $secondes = spip_timer('calcul_skel'); spip_log("calcul skel $sourcefile ($secondes)"); $squelette_compile = "<"."?php /* * Squelette : $sourcefile * Date : ".http_gmoddate(@filemtime($sourcefile))." GMT * Compile : ".http_gmoddate(time())." GMT ($secondes) * " . (!$boucles ? "Pas de boucle" : ("Boucles : " . join (', ', array_keys($boucles)))) ." */ " . $code . " // // Fonction principale du squelette $sourcefile // function $nom (\$Cache, \$Pile, \$doublons=array(), \$Numrows='', \$SP=0) { \$t0 = $corps; return array( 'texte' => \$t0, 'squelette' => '$nom', 'process_ins' => ((strpos(\$t0,'<'.'?')=== false) ? 'html' : 'php'), 'invalideurs' => \$Cache ); } ?".">"; if ($GLOBALS['var_mode'] == 'debug') squelette_debug_compile($nom, $sourcefile, $squelette_compile, $squelette); return $squelette_compile; } ?>