/** * Compilation d'une boucle (non recursive). * * @param string $id_boucle * Identifiant de la boucle * @param array $boucles * AST du squelette * @param string $trace * Code PHP (en mode debug uniquement) servant à conserver une * trace des premières valeurs de la boucle afin de pouvoir * les afficher dans le débugueur ultérieurement * @return string * Code PHP compilé de la boucle récursive **/ 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 <multi>). // - 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"; $fin_lang_select_public = "\n\t\tlang_select();"; $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 = ''; $fin_lang_select_public = ''; // 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 .= ((strlen($t1) && strlen($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; } // depiler la lang de la boucle si besoin $corps .= $fin_lang_select_public; // 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]=\$iter->fetch()) {\n{$corps}\n\t}"; } $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) { $nums .= "\$Numrows['{$id_boucle}']['total'] = @intval(\$iter->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); $a = sprintf(CODE_CORPS_BOUCLE, $init, $boucle->iterateur, "\$command", $contexte, $nums, $init_lang, $corps, $fin_lang, $trace, 'BOUCLE' . $id_boucle . ' @ ' . $boucle->descr['sourcefile']); # var_dump($a);exit; return $a; }
/** * Compile la balise `#TRI` permettant d'afficher un lien de changement d'ordre de tri * d'une colonne de la boucle * * La balise `#TRI{champ[,libelle]}` champ prend `>` ou `<` pour afficher * le lien de changement de sens croissant ou decroissant (`>` `<` indiquent * un sens par une flèche) * * @balise * @example * ``` * <th>[(#TRI{titre,<:info_titre:>,ajax})]</th> * ``` * * @param Champ $p * Pile au niveau de la balise * @param string $liste * Inutilisé * @return Champ * Pile complétée par le code à générer */ function balise_TRI_dist($p, $liste = 'true') { $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere']; // s'il n'y a pas de nom de boucle, on ne peut pas trier if ($b === '') { erreur_squelette(_T('zbug_champ_hors_boucle', array('champ' => '#TRI')), $p->id_boucle); $p->code = "''"; return $p; } $boucle = $p->boucles[$b]; // s'il n'y a pas de tri_champ, c'est qu'on se trouve // dans un boucle recursive ou qu'on a oublie le critere {tri} if (!isset($boucle->modificateur['tri_champ'])) { erreur_squelette(_T('zbug_tri_sans_critere', array('champ' => '#TRI')), $p->id_boucle); $p->code = "''"; return $p; } $_champ = interprete_argument_balise(1, $p); // si pas de champ, renvoyer le critere de tri utilise if (!$_champ) { $p->code = $boucle->modificateur['tri_champ']; return $p; } // forcer la jointure si besoin, et si le champ est statique if (preg_match(",^'([\\w.]+)'\$,i", $_champ, $m)) { index_pile($b, $m[1], $p->boucles, '', null, true, false); } $_libelle = interprete_argument_balise(2, $p); $_libelle = $_libelle ? $_libelle : $_champ; $_class = interprete_argument_balise(3, $p); // si champ = ">" c'est un lien vers le tri croissant : de gauche a droite ==> 1 // si champ = "<" c'est un lien vers le tri decroissant : (sens inverse) == -1 $_issens = "in_array({$_champ},array('>','<'))"; $_sens = "(strpos('< >',{$_champ})-1)"; $_variable = "((\$s={$_issens})?'sens':'tri')." . $boucle->modificateur['tri_nom']; $_url = "parametre_url(self(),{$_variable},\$s?{$_sens}:{$_champ})"; $_url = "parametre_url({$_url},'var_memotri',strncmp(" . $boucle->modificateur['tri_nom'] . ",'session',7)==0?{$_variable}:'')"; $_on = "\$s?(" . $boucle->modificateur['tri_sens'] . "=={$_sens}" . '):(' . $boucle->modificateur['tri_champ'] . "=={$_champ})"; $p->code = "lien_ou_expose({$_url},{$_libelle},{$_on}" . ($_class ? ",{$_class}" : "") . ")"; //$p->code = "''"; $p->interdire_scripts = false; return $p; }
function calculer_argument_precedent($idb, $nom_champ, &$boucles) { // si recursif, forcer l'extraction du champ SQL mais ignorer le code if ($boucles[$idb]->externe) { index_pile ($idb, $nom_champ, $boucles); $zero = '$SP'; } else $zero = '0'; // retourner $Pile[$SP] et pas $Pile[0] si recursion en 1ere boucle $prec = $boucles[$idb]->id_parent; return (($prec === '') ? ('$Pile[' . $zero . "]['$nom_champ']") : index_pile($prec, $nom_champ, $boucles)); }
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 <multi>). // - . 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); }
/** * Réserve les champs necessaires à la comparaison avec le contexte donné par * la boucle parente. * * Attention en recursif il faut les réserver chez soi-même ET chez sa maman * * @param string $idb Identifiant de la boucle * @param string $nom_champ * @param array $boucles AST du squelette * @param null|string $defaut * @return **/ function calculer_argument_precedent($idb, $nom_champ, &$boucles, $defaut = null) { // si recursif, forcer l'extraction du champ SQL mais ignorer le code if ($boucles[$idb]->externe) { index_pile($idb, $nom_champ, $boucles, '', $defaut); // retourner $Pile[$SP] et pas $Pile[0] si recursion en 1ere boucle // on ignore le defaut fourni dans ce cas $defaut = "@\$Pile[\$SP]['{$nom_champ}']"; } return index_pile($boucles[$idb]->id_parent, $nom_champ, $boucles, '', $defaut); }
function calculer_balise_expose($p, $on, $off) { $b = $p->nom_boucle ? $p->nom_boucle : $p->id_boucle; $key = $p->boucles[$b]->primary; $type = $p->boucles[$p->id_boucle]->primary; $desc = $p->boucles[$b]->show; $connect = sql_quote($p->boucles[$b]->sql_serveur); if (!$key) { $msg = array('zbug_champ_hors_boucle', array('champ' => '#EXPOSER')); erreur_squelette($msg, $p); } // Ne pas utiliser champ_sql, on jongle avec le nom boucle explicite $c = index_pile($p->id_boucle, $type, $p->boucles); if (isset($desc['field']['id_parent'])) { $parent = 0; // pour if (!$parent) dans calculer_expose } elseif (isset($desc['field']['id_rubrique'])) { $parent = index_pile($p->id_boucle, 'id_rubrique', $p->boucles, $b); } elseif (isset($desc['field']['id_groupe'])) { $parent = index_pile($p->id_boucle, 'id_groupe', $p->boucles, $b); } else $parent = "''"; $p->code = "(calcul_exposer($c, '$type', \$Pile[0], $parent, '$key', $connect) ? $on : $off)"; $p->interdire_scripts = false; return $p; }