X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=www%2Fecrire%2Fpublic%2Freferences.php;h=0fea64178117c69d0c03329bea0108c2bfecbefc;hb=4f443dce95ff6f8221c189880a70c74ce1c1f238;hp=e5b1b40500e04dd983235fa0c6d69228e4d51fb7;hpb=4a628e9b277d3617535f99d663ca79fa2e891177;p=lhc%2Fweb%2Fwww.git diff --git a/www/ecrire/public/references.php b/www/ecrire/public/references.php index e5b1b405..0fea6417 100644 --- a/www/ecrire/public/references.php +++ b/www/ecrire/public/references.php @@ -3,38 +3,51 @@ /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * - * Copyright (c) 2001-2016 * + * Copyright (c) 2001-2017 * * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ -// fonctions de recherche et de reservation -// dans l'arborescence des boucles - -if (!defined('_ECRIRE_INC_VERSION')) return; +/** + * Fonctions de recherche et de reservation dans l'arborescence des boucles + * + * @package SPIP\Core\Compilateur\References + **/ +if (!defined('_ECRIRE_INC_VERSION')) { + return; +} /** - * Retrouver l'index de la boucle dans le cas ou une reference explicite est demandee - * #MABALISE : l'index est celui de la premiere boucle englobante - * #_autreboucle:MABALISE : l'index est celui de la boucle _autreboucle si elle est bien englobante - * renvoi '' si une reference explicite incorrecte est envoyee + * Retrouver l'index de la boucle d'une balise + * + * Retrouve à quelle boucle appartient une balise, utile dans le cas + * où une référence explicite est demandée * - * Dans une balise dynamique : - * $idb = index_boucle($p); + * - `#MABALISE` : l'index est celui de la première boucle englobante + * - `#_autreboucle:MABALISE` : l'index est celui de la boucle _autreboucle si elle est bien englobante * - * @param Object $p + * @example + * Dans une balise dynamique ou calculée : + * ``` + * $idb = index_boucle($p); + * ``` + * + * @param Champ $p AST au niveau de la balise * @return string + * + * - Identifiant de la boucle possédant ce champ. + * - '' si une référence explicite incorrecte est envoyée */ -function index_boucle($p){ +function index_boucle($p) { $idb = $p->id_boucle; $explicite = $p->nom_boucle; if (strlen($explicite)) { // Recherche d'un champ dans un etage superieur - while (($idb !== $explicite) && ($idb !=='')) { + while (($idb !== $explicite) && ($idb !== '')) { $idb = $p->boucles[$idb]->id_parent; } } @@ -43,35 +56,52 @@ function index_boucle($p){ } /** - * index_pile retourne la position dans la pile du champ SQL $nom_champ - * en prenant la boucle la plus proche du sommet de pile (indique par $idb). - * Si on ne trouve rien, on considere que ca doit provenir du contexte - * (par l'URL ou l'include) qui a ete recopie dans Pile[0] - * (un essai d'affinage a debouche sur un bug vicieux) - * Si ca reference un champ SQL, on le memorise dans la structure $boucles - * afin de construire un requete SQL minimale (plutot qu'un brutal 'SELECT *') + * Retourne la position dans la pile d'un champ SQL * - * http://doc.spip.org/@index_pile + * Retourne le code PHP permettant de récupérer un champ SQL dans + * une boucle parente, en prenant la boucle la plus proche du sommet de pile + * (indiqué par $idb). * - * @param string $idb - * @param string $nom_champ - * @param Object $boucles + * Si on ne trouve rien, on considère que ça doit provenir du contexte + * (par l'URL ou l'include) qui a été recopié dans Pile[0] + * (un essai d'affinage a débouché sur un bug vicieux) + * + * Si ca référence un champ SQL, on le mémorise dans la structure $boucles + * afin de construire un requête SQL minimale (plutôt qu'un brutal 'SELECT *') + * + * @param string $idb Identifiant de la boucle + * @param string $nom_champ Nom du champ SQL cherché + * @param array $boucles AST du squelette * @param string $explicite - * indique que le nom de la boucle explicite dans la balise #_nomboucletruc:CHAMP - * @param string $defaut - * code par defaut si champ pas trouve dans l'index. @$Pile[0][$nom_champ] si non fourni + * Indique que le nom de la boucle est explicite dans la balise #_nomboucletruc:CHAMP + * @param null|string $defaut + * Code par defaut si le champ n'est pas trouvé dans l'index. + * Utilise @$Pile[0][$nom_champ] si non fourni * @param bool $remonte_pile - * permettre de remonter la pile des boucles ou non (dans ce cas on ne cherche que ds la 1ere boucle englobante) + * Permettre de remonter la pile des boucles ou non (dans ce cas on + * ne cherche que danss la 1ère boucle englobante) + * @param bool $select + * Pour ajouter au select de la boucle, par defaut true * @return string + * Code PHP pour obtenir le champ SQL */ -function index_pile($idb, $nom_champ, &$boucles, $explicite='', $defaut=null, $remonte_pile=true) { - if (!is_string($defaut)) - $defaut = '@$Pile[0][\''. strtolower($nom_champ) . '\']'; +function index_pile( + $idb, + $nom_champ, + &$boucles, + $explicite = '', + $defaut = null, + $remonte_pile = true, + $select = true +) { + if (!is_string($defaut)) { + $defaut = '@$Pile[0][\'' . strtolower($nom_champ) . '\']'; + } $i = 0; if (strlen($explicite)) { // Recherche d'un champ dans un etage superieur - while (($idb !== $explicite) && ($idb !=='')) { + while (($idb !== $explicite) && ($idb !== '')) { # spip_log("Cherchexpl: $nom_champ '$explicite' '$idb' '$i'"); $i++; $idb = $boucles[$idb]->id_parent; @@ -85,131 +115,218 @@ function index_pile($idb, $nom_champ, &$boucles, $explicite='', $defaut=null, $r // il y a incoherences qu'il vaut mieux eviter while (isset($boucles[$idb])) { $joker = true; - list ($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles, $joker); + // modifie $joker si tous les champs sont autorisés. + // $t = le select pour le champ, si on l'a trouvé (ou si joker) + // $c = le nom du champ demandé + list($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles, $joker); if ($t) { - if (!in_array($t, $boucles[$idb]->select)) { + if ($select and !in_array($t, $boucles[$idb]->select)) { $boucles[$idb]->select[] = $t; } $champ = '$Pile[$SP' . ($i ? "-$i" : "") . '][\'' . $c . '\']'; - if (!$joker) - return index_compose($conditionnel,$champ); + if (!$joker) { + return index_compose($conditionnel, $champ); + } + // tant que l'on trouve des tables avec joker, on continue + // avec la boucle parente et on conditionne à l'exécution + // la présence du champ. Si le champ existe à l'exécution + // dans une boucle, il est pris, sinon on le cherche dans le parent... $conditionnel[] = "isset($champ)?$champ"; } - if ($remonte_pile){ + if ($remonte_pile) { # spip_log("On remonte vers $i"); // Sinon on remonte d'un cran $idb = $boucles[$idb]->id_parent; $i++; - } - else + } else { $idb = null; + } } # spip_log("Pas vu $nom_champ"); // esperons qu'il y sera - // on qu'on a fourni une valeur par "defaut" plus pertinent - return index_compose($conditionnel,$defaut); + // ou qu'on a fourni une valeur par "defaut" plus pertinent + return index_compose($conditionnel, $defaut); } /** - * Reconstuire la cascade de condition avec la valeur finale par defaut - * pour les balises dont on ne saura qu'a l'execution si elles sont definies ou non - * (boucle DATA) - * - * @param array $conditionnel - * @param string $defaut - * @return string + * Reconstuire la cascade de condition de recherche d'un champ + * + * On ajoute la valeur finale par défaut pour les balises dont on ne saura + * qu'à l'exécution si elles sont definies ou non (boucle DATA) + * + * @param array $conditionnel Liste de codes PHP pour retrouver un champ + * @param string $defaut Valeur par défaut si aucun des moyens ne l'a trouvé + * @return string Code PHP complet de recherche d'un champ */ -function index_compose($conditionnel,$defaut){ - while ($c = array_pop($conditionnel)) +function index_compose($conditionnel, $defaut) { + while ($c = array_pop($conditionnel)) { // si on passe defaut = '', ne pas générer d'erreur de compilation. - $defaut = "($c:(".($defaut?$defaut:"''")."))"; + $defaut = "($c:(" . ($defaut ? $defaut : "''") . "))"; + } + return $defaut; } -// http://doc.spip.org/@index_tables_en_pile +/** + * Cherche un champ dans une boucle + * + * Le champ peut être : + * + * - un alias d'un autre : il faut alors le calculer, éventuellement en + * construisant une jointure. + * - présent dans la table : on l'utilise + * - absent, mais le type de boucle l'autorise (joker des itérateurs DATA) : + * on l'utilise et lève le drapeau joker + * - absent, on cherche une jointure et on l'utilise si on en trouve. + * + * @todo + * Ici la recherche de jointure sur l'absence d'un champ ne cherche + * une jointure que si des jointures explicites sont demandées, + * et non comme à d'autres endroits sur toutes les jointures possibles. + * Il faut homogénéiser cela. + * + * + * @param string $idb Identifiant de la boucle + * @param string $nom_champ Nom du champ SQL cherché + * @param Boucle $boucles AST du squelette + * @param bool $joker + * Le champ peut-il être inconnu à la compilation ? + * Ce drapeau sera levé si c'est le cas. + * @return array + * Liste (Nom du champ véritable, nom du champ demandé). + * Le nom du champ véritable est une expression pour le SELECT de + * la boucle tel que "rubriques.titre" ou "mots.titre AS titre_mot". + * Les éléments de la liste sont vides si on ne trouve rien. + **/ function index_tables_en_pile($idb, $nom_champ, &$boucles, &$joker) { - global $exceptions_des_tables; $r = $boucles[$idb]->type_requete; - - if ($r == 'boucle') return array(); + // boucle recursive, c'est foutu... + if ($r == TYPE_RECURSIF) { + return array(); + } if (!$r) { $joker = false; // indiquer a l'appelant # continuer pour chercher l'erreur suivante - return array("'#" . $r . ':' . $nom_champ . "'",''); + return array("'#" . $r . ':' . $nom_champ . "'", ''); } $desc = $boucles[$idb]->show; - $excep = isset($exceptions_des_tables[$r]) ? $exceptions_des_tables[$r] : ''; - if ($excep) + // le nom du champ est il une exception de la table ? un alias ? + $excep = isset($GLOBALS['exceptions_des_tables'][$r]) ? $GLOBALS['exceptions_des_tables'][$r] : ''; + if ($excep) { $excep = isset($excep[$nom_champ]) ? $excep[$nom_champ] : ''; + } if ($excep) { $joker = false; // indiquer a l'appelant - return index_exception($boucles[$idb], $desc, $nom_champ, $excep); - } + return index_exception($boucles[$idb], $desc, $nom_champ, $excep); + } // pas d'alias. Le champ existe t'il ? else { + // le champ est réellement présent, on le prend. if (isset($desc['field'][$nom_champ])) { $t = $boucles[$idb]->id_table; $joker = false; // indiquer a l'appelant return array("$t.$nom_champ", $nom_champ); } - // Champ joker * des iterateurs DATA qui accepte tout - elseif (/*$joker AND */isset($desc['field']['*'])) { + // Tous les champs sont-ils acceptés ? + // Si oui, on retourne le champ, et on lève le flag joker + // C'est le cas des itérateurs DATA qui acceptent tout + // et testent la présence du champ à l'exécution et non à la compilation + // car ils ne connaissent pas ici leurs contenus. + elseif (/*$joker AND */ + isset($desc['field']['*']) + ) { $joker = true; // indiquer a l'appelant return array($nom_champ, $nom_champ); } + // pas d'alias, pas de champ, pas de joker... + // tenter via une jointure... else { $joker = false; // indiquer a l'appelant - if ($boucles[$idb]->jointures_explicites) { - $t = trouver_champ_exterieur($nom_champ, - $boucles[$idb]->jointures, - $boucles[$idb]); - if ($t) - return index_exception($boucles[$idb], - $desc, - $nom_champ, - array($t[1]['id_table'], $nom_champ)); - } - return array('',''); + // regarder si le champ est deja dans une jointure existante + // sinon, si il y a des joitures explicites, la construire + if (!$t = trouver_champ_exterieur($nom_champ, $boucles[$idb]->from, $boucles[$idb])) { + if ($boucles[$idb]->jointures_explicites) { + // [todo] Ne pas lancer que lorsque il y a des jointures explicites !!!! + // fonctionnel, il suffit d'utiliser $boucles[$idb]->jointures au lieu de jointures_explicites + // mais est-ce ce qu'on veut ? + $jointures = preg_split("/\s+/", $boucles[$idb]->jointures_explicites); + if ($cle = trouver_jointure_champ($nom_champ, $boucles[$idb], $jointures)) { + $t = trouver_champ_exterieur($nom_champ, $boucles[$idb]->from, $boucles[$idb]); + } + } + } + if ($t) { + // si on a trouvé une jointure possible, on fait comme + // si c'était une exception pour le champ demandé + return index_exception($boucles[$idb], + $desc, + $nom_champ, + array($t[1]['id_table'], reset($t[2]))); + } + + return array('', ''); } } } -// Reference a une entite SPIP alias d'un champ SQL -// Ca peut meme etre d'un champ dans une jointure -// qu'il faut provoquer si ce n'est fait -// http://doc.spip.org/@index_exception -function index_exception(&$boucle, $desc, $nom_champ, $excep) -{ +/** + * Retrouve un alias d'un champ dans une boucle + * + * Référence à une entite SPIP alias d'un champ SQL. + * Ça peut même être d'un champ dans une jointure qu'il faut provoquer + * si ce n'est fait + * + * @param Boucle $boucle Boucle dont on prend un alias de champ + * @param array $desc Description de la table SQL de la boucle + * @param string $nom_champ Nom du champ original demandé + * @param array $excep + * Description de l'exception pour ce champ. Peut être : + * + * - string : nom du champ véritable dans la table + * - array : + * - liste (table, champ) indique que le véritable champ + * est dans une autre table et construit la jointure dessus + * - liste (table, champ, fonction) idem, mais en passant un + * nom de fonction qui s'occupera de créer la jointure. + * @return array + * Liste (nom du champ alias, nom du champ). Le nom du champ alias + * est une expression pour le SELECT de la boucle du style "mots.titre AS titre_mot" + **/ +function index_exception(&$boucle, $desc, $nom_champ, $excep) { static $trouver_table; - if (!$trouver_table) + if (!$trouver_table) { $trouver_table = charger_fonction('trouver_table', 'base'); + } if (is_array($excep)) { // permettre aux plugins de gerer eux meme des jointures derogatoire ingerables - $t = NULL; - if (count($excep)==3){ + $t = null; + if (count($excep) == 3) { $index_exception_derogatoire = array_pop($excep); $t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep); } - if ($t == NULL) { - list($e, $x) = $excep; #PHP4 affecte de gauche a droite - $excep = $x; #PHP5 de droite a gauche ! + if ($t == null) { + list($e, $x) = $excep; #PHP4 affecte de gauche a droite + $excep = $x; #PHP5 de droite a gauche ! $j = $trouver_table($e, $boucle->sql_serveur); - if (!$j) return array('',''); + if (!$j) { + return array('', ''); + } $e = $j['table']; if (!$t = array_search($e, $boucle->from)) { $k = $j['key']['PRIMARY KEY']; - if (strpos($k,',')) { + if (strpos($k, ',')) { $l = (preg_split('/\s*,\s*/', $k)); $k = $desc['key']['PRIMARY KEY']; if (!in_array($k, $l)) { spip_log("jointure impossible $e " . join(',', $l)); - return array('',''); + + return array('', ''); } } $k = array($boucle->id_table, array($e), $k); @@ -217,67 +334,116 @@ function index_exception(&$boucle, $desc, $nom_champ, $excep) $t = array_search($e, $boucle->from); } } + } else { + $t = $boucle->id_table; } - else $t = $boucle->id_table; // demander a SQL de gerer le synonyme // ca permet que excep soit dynamique (Cedric, 2/3/06) - if ($excep != $nom_champ) $excep .= ' AS '. $nom_champ; + if ($excep != $nom_champ) { + $excep .= ' AS ' . $nom_champ; + } + return array("$t.$excep", $nom_champ); } /** - * cette fonction sert d'API pour demander le champ '$champ' dans la pile + * Demande le champ '$champ' dans la pile * - * http://doc.spip.org/@champ_sql + * Le champ est cherché dans l'empilement de boucles, sinon dans la valeur + * par défaut (qui est l'environnement du squelette si on ne la précise pas). * + * @api * @param string $champ - * champ recherch� - * @param object $p - * contexte de compilation - * @param bool $defaut - * valeur par defaut si rien trouve dans la pile (si null, on prend le #ENV) + * Champ recherché + * @param Champ $p + * AST au niveau de la balise + * @param null|string $defaut + * Code de la valeur par défaut si on ne trouve pas le champ dans une + * des boucles parentes. Sans précision, il sera pris dans l'environnement + * du squelette. + * Passer $defaut = '' pour ne pas prendre l'environnement. * @param bool $remonte_pile - * permettre de remonter dans la pile des boucles pour trouver le champ + * Permettre de remonter dans la pile des boucles pour trouver le champ * @return string + * Code PHP pour retrouver le champ */ function champ_sql($champ, $p, $defaut = null, $remonte_pile = true) { return index_pile($p->id_boucle, $champ, $p->boucles, $p->nom_boucle, $defaut, $remonte_pile); } -// cette fonction sert d'API pour demander une balise Spip avec filtres -// http://doc.spip.org/@calculer_champ +/** + * Calcule et retourne le code PHP d'exécution d'une balise SPIP et des ses filtres + * + * Cette fonction qui sert d'API au compilateur demande à calculer + * le code PHP d'une balise, puis lui applique les filtres (automatiques + * et décrits dans le squelette) + * + * @uses calculer_balise() + * @uses applique_filtres() + * + * @param Champ $p + * AST au niveau de la balise + * @return string + * Code PHP pour d'exécution de la balise et de ses filtres + **/ function calculer_champ($p) { $p = calculer_balise($p->nom_champ, $p); + return applique_filtres($p); } -// Cette fonction sert d'API pour demander une balise SPIP sans filtres. -// Pour une balise nommmee NOM, elle demande a charger_fonction de chercher -// s'il existe une fonction balise_NOM ou balise_NOM_dist -// eventuellement en chargeant le fichier balise/NOM.php. -// Si la balise est de la forme PREFIXE_SUFFIXE (cf LOGO_* et URL_*) -// elle fait de meme avec juste le PREFIXE. -// Si pas de fonction, c'est une reference a une colonne de table SQL connue. -// Les surcharges des colonnes SQL via charger_fonction sont donc possibles. -// http://doc.spip.org/@calculer_balise +/** + * Calcule et retourne le code PHP d'exécution d'une balise SPIP + * + * Cette fonction qui sert d'API au compilateur demande à calculer + * le code PHP d'une balise (cette fonction ne calcule pas les éventuels + * filtres de la balise). + * + * Pour une balise nommmée `NOM`, elle demande à `charger_fonction()` de chercher + * s'il existe une fonction `balise_NOM` ou `balise_NOM_dist` + * éventuellement en chargeant le fichier `balise/NOM.php.` + * + * Si la balise est de la forme `PREFIXE_SUFFIXE` (cf `LOGO_*` et `URL_*`) + * elle fait de même avec juste le `PREFIXE`. + * + * S'il n'y a pas de fonction trouvée, on considère la balise comme une référence + * à une colonne de table SQL connue, sinon à l'environnement (cf. `calculer_balise_DEFAUT_dist()`). + * + * Les surcharges des colonnes SQL via charger_fonction sont donc possibles. + * + * @uses calculer_balise_DEFAUT_dist() + * Lorsqu'aucune fonction spécifique n'est trouvée. + * @see charger_fonction() + * Pour la recherche des fonctions de balises + * + * @param string $nom + * Nom de la balise + * @param Champ $p + * AST au niveau de la balise + * @return Champ + * Pile complétée par le code PHP pour l'exécution de la balise et de ses filtres + **/ function calculer_balise($nom, $p) { // S'agit-t-il d'une balise_XXXX[_dist]() ? if ($f = charger_fonction($nom, 'balise', true)) { $p->balise_calculee = true; $res = $f($p); - if ($res !== NULL) + if ($res !== null and is_object($res)) { return $res; + } } // Certaines des balises comportant un _ sont generiques if ($f = strpos($nom, '_') - AND $f = charger_fonction(substr($nom,0,$f+1), 'balise', true)) { + and $f = charger_fonction(substr($nom, 0, $f + 1), 'balise', true) + ) { $res = $f($p); - if ($res !== NULL) + if ($res !== null and is_object($res)) { return $res; + } } $f = charger_fonction('DEFAUT', 'calculer_balise'); @@ -285,6 +451,29 @@ function calculer_balise($nom, $p) { return $f($nom, $p); } + +/** + * Calcule et retourne le code PHP d'exécution d'une balise SPIP non déclarée + * + * Cette fonction demande à calculer le code PHP d'une balise qui + * n'a pas de fonction spécifique. + * + * On considère la balise comme une référence à une colonne de table SQL + * connue, sinon à l'environnement. + * + * @uses index_pile() + * Pour la recherche de la balise comme colonne SQL ou comme environnement + * @note + * Le texte de la balise est retourné si il ressemble à une couleur + * et qu'aucun champ correspondant n'a été trouvé, comme `#CCAABB` + * + * @param string $nom + * Nom de la balise + * @param Champ $p + * AST au niveau de la balise + * @return string + * Code PHP pour d'exécution de la balise et de ses filtres + **/ function calculer_balise_DEFAUT_dist($nom, $p) { // ca pourrait etre un champ SQL homonyme, @@ -292,14 +481,15 @@ function calculer_balise_DEFAUT_dist($nom, $p) { // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour // il faut recracher {...} quand ce n'est finalement pas des args - if ($p->fonctions AND (!$p->fonctions[0][0]) AND $p->fonctions[0][1]) { + if ($p->fonctions and (!$p->fonctions[0][0]) and $p->fonctions[0][1]) { $code = addslashes($p->fonctions[0][1]); $p->code .= " . '$code'"; } // ne pas passer le filtre securite sur les id_xxx - if (strpos($nom, 'ID_') === 0) + if (strpos($nom, 'ID_') === 0) { $p->interdire_scripts = false; + } // Compatibilite ascendante avec les couleurs html (#FEFEFE) : // SI le champ SQL n'est pas trouve @@ -308,8 +498,9 @@ function calculer_balise_DEFAUT_dist($nom, $p) { // ALORS retourner la couleur. // Ca permet si l'on veut vraiment de recuperer [(#ACCEDE*)] if (preg_match("/^[A-F]{1,6}$/i", $nom) - AND !$p->etoile - AND !$p->fonctions) { + and !$p->etoile + and !$p->fonctions + ) { $p->code = "'#$nom'"; $p->interdire_scripts = false; } @@ -318,87 +509,138 @@ function calculer_balise_DEFAUT_dist($nom, $p) { } -// -// Traduction des balises dynamiques, notamment les "formulaire_*" -// Inclusion du fichier associe a son nom, qui contient la fonction homonyme -// donnant les arguments a chercher dans la pile, et qui sont donc compiles. -// On leur adjoint les arguments explicites de la balise (cf #LOGIN{url}) -// et d'eventuelles valeurs transmises d'autorite par la balise. -// (cf http://trac.rezo.net/trac/spip/ticket/1728) -// La fonction nommee ci-dessous recevra a l'execution la valeur de tout ca. - +/** Code PHP d'exécution d'une balise dynamique */ define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s', array(%s%s), array(%s%s))"); -// http://doc.spip.org/@calculer_balise_dynamique -function calculer_balise_dynamique($p, $nom, $l, $supp=array()) { + +/** + * Calcule le code PHP d'exécution d'une balise SPIP dynamique + * + * Calcule les balises dynamiques, notamment les `formulaire_*`. + * + * Inclut le fichier associé à son nom, qui contient la fonction homonyme + * donnant les arguments à chercher dans la pile, et qui sont donc compilés. + * + * On leur adjoint les arguments explicites de la balise (cf `#LOGIN{url}`) + * et d'éventuelles valeurs transmises d'autorité par la balise. + * (cf http://core.spip.net/issues/1728) + * + * La fonction `executer_balise_dynamique()` définie par la + * constante `CODE_EXECUTER_BALISE` recevra à l'exécution la valeur de tout ca. + * + * @uses collecter_balise_dynamique() + * Qui calcule le code d'exécution de chaque argument de la balise + * @see executer_balise_dynamique() + * Code PHP produit qui chargera les fonctions de la balise dynamique à l'exécution, + * appelée avec les arguments calculés. + * @param Champ $p + * AST au niveau de la balise + * @param string $nom + * Nom de la balise dynamique + * @param array $l + * Liste des noms d'arguments (balises) à collecter + * @param array $supp + * Liste de données supplémentaires à transmettre au code d'exécution. + * @return Champ + * Balise complétée de son code d'exécution + **/ +function calculer_balise_dynamique($p, $nom, $l, $supp = array()) { if (!balise_distante_interdite($p)) { $p->code = "''"; + return $p; } // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour // il faut recracher {...} quand ce n'est finalement pas des args - if ($p->fonctions AND (!$p->fonctions[0][0]) AND $p->fonctions[0][1]) { + if ($p->fonctions and (!$p->fonctions[0][0]) and $p->fonctions[0][1]) { $p->fonctions = null; } - if ($p->param AND ($c = $p->param[0])) { + if ($p->param and ($c = $p->param[0])) { // liste d'arguments commence toujours par la chaine vide array_shift($c); // construire la liste d'arguments comme pour un filtre $param = compose_filtres_args($p, $c, ','); - } else $param = ""; + } else { + $param = ""; + } $collecte = collecter_balise_dynamique($l, $p, $nom); $p->code = sprintf(CODE_EXECUTER_BALISE, $nom, join(',', $collecte), - ($collecte ? $param : substr($param,1)), # virer la virgule + ($collecte ? $param : substr($param, 1)), # virer la virgule memoriser_contexte_compil($p), (!$supp ? '' : (', ' . join(',', $supp)))); $p->interdire_scripts = false; + return $p; } -// Construction du tableau des arguments d'une balise dynamique. -// Ces arguments peuvent etre eux-meme des balises (cf FORMULAIRE_SIGNATURE) -// mais gare au bouclage (on peut s'aider de $nom pour le reperer au besoin) -// En revanche ils n'ont pas de filtres, donc on appelle calculer_balise qui -// ne s'occupe pas de ce qu'il y a dans $p (mais qui va y ecrire le code) -// http://doc.spip.org/@collecter_balise_dynamique +/** + * Construction du tableau des arguments d'une balise dynamique. + * + * Pour chaque argument (un nom de balise), crée le code PHP qui le calculera. + * + * @note + * Ces arguments peuvent être eux-même des balises (cf FORMULAIRE_SIGNATURE) + * mais gare au bouclage (on peut s'aider de `$nom` pour le réperer au besoin) + * + * En revanche ils n'ont pas de filtres, donc on appelle `calculer_balise()` qui + * ne s'occupe pas de ce qu'il y a dans `$p` (mais qui va y ecrire le code) + * + * @uses calculer_balise() + * Pour obtenir le code d'éxécution de chaque argument. + * + * @param array $l + * Liste des noms d'arguments (balises) à collecter (chaque argument + * de la balise dynamique est considéré comme étant un nom de balise) + * @param Champ $p + * AST au niveau de la balise + * @param string $nom + * Nom de la balise + * @return array + * Liste des codes PHP d'éxecution des balises collectées + **/ function collecter_balise_dynamique($l, &$p, $nom) { $args = array(); - foreach($l as $c) { $x = calculer_balise($c, $p); $args[] = $x->code;} + foreach ($l as $c) { + $x = calculer_balise($c, $p); + $args[] = $x->code; + } + return $args; } - - /** * Récuperer le nom du serveur - * - * Mais pas si c'est un serveur specifique derogatoire - * + * + * Mais pas si c'est un serveur spécifique dérogatoire + * * @param Champ $p * AST positionné sur la balise * @return string * Nom de la connexion -**/ + **/ function trouver_nom_serveur_distant($p) { $nom = $p->id_boucle; if ($nom - AND isset($p->boucles[$nom])) { + and isset($p->boucles[$nom]) + ) { $s = $p->boucles[$nom]->sql_serveur; if (strlen($s) - AND strlen($serveur = strtolower($s)) - AND !in_array($serveur,$GLOBALS['exception_des_connect'])) { - return $serveur; + and strlen($serveur = strtolower($s)) + and !in_array($serveur, $GLOBALS['exception_des_connect']) + ) { + return $serveur; } } + return ""; } @@ -408,26 +650,29 @@ function trouver_nom_serveur_distant($p) { * * La fonction loge une erreur si la balise est utilisée sur une * base distante et retourne false dans ce cas. - * - * Note : - * Il faudrait savoir traiter les formulaires en local - * tout en appelant le serveur SQL distant. - * En attendant, cette fonction permet de refuser une authentification - * sur qqch qui n'a rien a voir. - * + * + * @note + * Il faudrait savoir traiter les formulaires en local + * tout en appelant le serveur SQL distant. + * En attendant, cette fonction permet de refuser une authentification + * sur quelque-chose qui n'a rien a voir. + * * @param Champ $p * AST positionné sur la balise * @return bool + * * - true : La balise est autorisée * - false : La balise est interdite car le serveur est distant -**/ + **/ function balise_distante_interdite($p) { $nom = $p->id_boucle; - if ($nom AND trouver_nom_serveur_distant($p)) { - spip_log( $nom .':' . $p->nom_champ .' '._T('zbug_distant_interdit')); + if ($nom and trouver_nom_serveur_distant($p)) { + spip_log($nom . ':' . $p->nom_champ . ' ' . _T('zbug_distant_interdit')); + return false; } + return true; } @@ -436,20 +681,20 @@ function balise_distante_interdite($p) { // Traitements standard de divers champs // definis par $table_des_traitements, cf. ecrire/public/interfaces // -// http://doc.spip.org/@champs_traitements -function champs_traitements ($p) { - global $table_des_traitements; +// http://code.spip.net/@champs_traitements +function champs_traitements($p) { - if (isset($table_des_traitements[$p->nom_champ])) - $ps = $table_des_traitements[$p->nom_champ]; - else { + if (isset($GLOBALS['table_des_traitements'][$p->nom_champ])) { + $ps = $GLOBALS['table_des_traitements'][$p->nom_champ]; + } else { // quand on utilise un traitement catch-all * // celui-ci ne s'applique pas sur les balises calculees qui peuvent gerer // leur propre securite - if (!$p->balise_calculee) - $ps = $table_des_traitements['*']; - else + if (!$p->balise_calculee) { + $ps = $GLOBALS['table_des_traitements']['*']; + } else { $ps = false; + } } if (is_array($ps)) { @@ -457,22 +702,25 @@ function champs_traitements ($p) { $idb = index_boucle($p); // mais on peut aussi etre hors boucle. Se mefier. $type_requete = isset($p->boucles[$idb]->type_requete) ? $p->boucles[$idb]->type_requete : false; - $table_sql = isset($p->boucles[$idb]->show['table_sql'])?$p->boucles[$idb]->show['table_sql']:false; + $table_sql = isset($p->boucles[$idb]->show['table_sql']) ? $p->boucles[$idb]->show['table_sql'] : false; // le traitement peut n'etre defini que pour une table en particulier "spip_articles" - if ($table_sql AND isset($ps[$table_sql])) + if ($table_sql and isset($ps[$table_sql])) { $ps = $ps[$table_sql]; - // ou pour une boucle en particulier "DATA","articles" - elseif ($type_requete AND isset($ps[$type_requete])) + } // ou pour une boucle en particulier "DATA","articles" + elseif ($type_requete and isset($ps[$type_requete])) { $ps = $ps[$type_requete]; - // ou pour indiferrement quelle que soit la boucle - elseif(isset($ps[0])) + } // ou pour indiferrement quelle que soit la boucle + elseif (isset($ps[0])) { $ps = $ps[0]; - else - $ps=false; + } else { + $ps = false; + } } - if (!$ps) return $p->code; + if (!$ps) { + return $p->code; + } // Si une boucle DOCUMENTS{doublons} est presente dans le squelette, // ou si in INCLURE contient {doublons} @@ -480,15 +728,17 @@ function champs_traitements ($p) { // dans les filtres propre() ou typo() // (qui traitent les raccourcis referencant les docs) - if (isset($p->descr['documents']) - AND - $p->descr['documents'] - AND ( - (strpos($ps,'propre') !== false) - OR - (strpos($ps,'typo') !== false) - )) - $ps = 'traiter_doublons_documents($doublons, '.$ps.')'; + if (isset($p->descr['documents']) + and + $p->descr['documents'] + and ( + (strpos($ps, 'propre') !== false) + or + (strpos($ps, 'typo') !== false) + ) + ) { + $ps = 'traiter_doublons_documents($doublons, ' . $ps . ')'; + } // La protection des champs par |safehtml est assuree par les extensions // dans la declaration des traitements des champs sensibles @@ -504,40 +754,46 @@ function champs_traitements ($p) { // - une etoile => pas de processeurs standards // - deux etoiles => pas de securite non plus ! // -// http://doc.spip.org/@applique_filtres +// http://code.spip.net/@applique_filtres function applique_filtres($p) { // Traitements standards (cf. supra) - if ($p->etoile == '') + if ($p->etoile == '') { $code = champs_traitements($p); - else + } else { $code = $p->code; + } // Appliquer les filtres perso - if ($p->param) + if ($p->param) { $code = compose_filtres($p, $code); + } // S'il y a un lien avec la session, ajouter un code qui levera // un drapeau dans la structure d'invalidation $Cache - if (isset($p->descr['session'])) + if (isset($p->descr['session'])) { $code = "invalideur_session(\$Cache, $code)"; + } $code = sandbox_composer_interdire_scripts($code, $p); + return $code; } // Cf. function pipeline dans ecrire/inc_utils.php -// http://doc.spip.org/@compose_filtres +// http://code.spip.net/@compose_filtres function compose_filtres(&$p, $code) { $image_miette = false; - foreach($p->param as $filtre) { + foreach ($p->param as $filtre) { $fonc = array_shift($filtre); - if (!$fonc) continue; // normalement qu'au premier tour. - $is_filtre_image = ((substr($fonc,0,6)=='image_') AND $fonc!='image_graver'); - if ($image_miette AND !$is_filtre_image){ - // il faut graver maintenant car apres le filtre en cours - // on est pas sur d'avoir encore le nom du fichier dans le pipe + if (!$fonc) { + continue; + } // normalement qu'au premier tour. + $is_filtre_image = ((substr($fonc, 0, 6) == 'image_') and $fonc != 'image_graver'); + if ($image_miette and !$is_filtre_image) { + // il faut graver maintenant car apres le filtre en cours + // on est pas sur d'avoir encore le nom du fichier dans le pipe $code = "filtrer('image_graver', $code)"; $image_miette = false; } @@ -545,79 +801,91 @@ function compose_filtres(&$p, $code) { // a separer par "," ou ":" dans le cas du filtre "?{a,b}" if ($fonc !== '?') { $sep = ','; - } else {$sep = ':'; + } else { + $sep = ':'; // |?{a,b} *doit* avoir exactement 2 arguments ; on les force - if (count($filtre) != 2) - $filtre = array(isset($filtre[0])?$filtre[0]:"", isset($filtre[1])?$filtre[1]:""); + if (count($filtre) != 2) { + $filtre = array(isset($filtre[0]) ? $filtre[0] : "", isset($filtre[1]) ? $filtre[1] : ""); + } } $arglist = compose_filtres_args($p, $filtre, $sep); - $logique = filtre_logique($fonc, $code, substr($arglist,1)); - if ($logique) + $logique = filtre_logique($fonc, $code, substr($arglist, 1)); + if ($logique) { $code = $logique; - else { - $code = sandbox_composer_filtre($fonc,$code,$arglist,$p); - if ($is_filtre_image) $image_miette = true; + } else { + $code = sandbox_composer_filtre($fonc, $code, $arglist, $p); + if ($is_filtre_image) { + $image_miette = true; + } } } // ramasser les images intermediaires inutiles et graver l'image finale - if ($image_miette) + if ($image_miette) { $code = "filtrer('image_graver',$code)"; + } return $code; } // Filtres et,ou,oui,non,sinon,xou,xor,and,or,not,yes // et comparateurs -function filtre_logique($fonc, $code, $arg) -{ - global $table_criteres_infixes; +function filtre_logique($fonc, $code, $arg) { + switch (true) { - case in_array($fonc, $table_criteres_infixes): + case in_array($fonc, $GLOBALS['table_criteres_infixes']): return "($code $fonc $arg)"; - case ($fonc == 'and') OR ($fonc == 'et'): + case ($fonc == 'and') or ($fonc == 'et'): return "((($code) AND ($arg)) ?' ' :'')"; - case ($fonc == 'or') OR ($fonc == 'ou'): + case ($fonc == 'or') or ($fonc == 'ou'): return "((($code) OR ($arg)) ?' ' :'')"; - case ($fonc == 'xor') OR ($fonc == 'xou'): + case ($fonc == 'xor') or ($fonc == 'xou'): return "((($code) XOR ($arg)) ?' ' :'')"; case ($fonc == 'sinon'): return "(((\$a = $code) OR (is_string(\$a) AND strlen(\$a))) ? \$a : $arg)"; - case ($fonc == 'not') OR ($fonc == 'non'): + case ($fonc == 'not') or ($fonc == 'non'): return "(($code) ?'' :' ')"; - case ($fonc == 'yes') OR ($fonc == 'oui'): + case ($fonc == 'yes') or ($fonc == 'oui'): return "(($code) ?' ' :'')"; } + return ''; } -// http://doc.spip.org/@compose_filtres_args -function compose_filtres_args($p, $args, $sep) -{ +// http://code.spip.net/@compose_filtres_args +function compose_filtres_args($p, $args, $sep) { $arglist = ""; foreach ($args as $arg) { - $arglist .= $sep . - calculer_liste($arg, $p->descr, $p->boucles, $p->id_boucle); + $arglist .= $sep . + calculer_liste($arg, $p->descr, $p->boucles, $p->id_boucle); } + return $arglist; } -// -// Reserve les champs necessaires a la comparaison avec le contexte donne par -// la boucle parente ; attention en recursif il faut les reserver chez soi-meme -// ET chez sa maman -// -// http://doc.spip.org/@calculer_argument_precedent -function calculer_argument_precedent($idb, $nom_champ, &$boucles, $defaut=null) { + +/** + * 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); + 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); + return index_pile($boucles[$idb]->id_parent, $nom_champ, $boucles, '', $defaut); } // @@ -630,16 +898,15 @@ function calculer_argument_precedent($idb, $nom_champ, &$boucles, $defaut=null) // "SELECT XXXX AS points" // -// http://doc.spip.org/@rindex_pile -function rindex_pile($p, $champ, $motif) -{ +// http://code.spip.net/@rindex_pile +function rindex_pile($p, $champ, $motif) { $n = 0; $b = $p->id_boucle; $p->code = ''; while ($b != '') { - foreach($p->boucles[$b]->criteres as $critere) { + foreach ($p->boucles[$b]->criteres as $critere) { if ($critere->op == $motif) { - $p->code = '$Pile[$SP' . (($n==0) ? "" : "-$n") . + $p->code = '$Pile[$SP' . (($n == 0) ? "" : "-$n") . "]['$champ']"; $b = ''; break 2; @@ -650,11 +917,11 @@ function rindex_pile($p, $champ, $motif) } // si on est hors d'une boucle de {recherche}, cette balise est vide - if (!$p->code) + if (!$p->code) { $p->code = "''"; + } $p->interdire_scripts = false; + return $p; } - -?>