/ecrire/public/references.php
PHP | 927 lines | 405 code | 95 blank | 427 comment | 99 complexity | d0ed0afac8c154bc0404798964e900d8 MD5 | raw file
Possible License(s): LGPL-2.1, MIT
- <?php
- /***************************************************************************\
- * SPIP, Systeme de publication pour l'internet *
- * *
- * 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
- *
- * @package SPIP\Core\Compilateur\References
- **/
- if (!defined('_ECRIRE_INC_VERSION')) {
- return;
- }
- /**
- * 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
- *
- * - `#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
- *
- * @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) {
- $idb = $p->id_boucle;
- $explicite = $p->nom_boucle;
- if (strlen($explicite)) {
- // Recherche d'un champ dans un etage superieur
- while (($idb !== $explicite) && ($idb !== '')) {
- $idb = $p->boucles[$idb]->id_parent;
- }
- }
- return $idb;
- }
- /**
- * Retourne la position dans la pile d'un champ SQL
- *
- * 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).
- *
- * 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 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 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,
- $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 !== '')) {
- # spip_log("Cherchexpl: $nom_champ '$explicite' '$idb' '$i'");
- $i++;
- $idb = $boucles[$idb]->id_parent;
- }
- }
- # spip_log("Cherche: $nom_champ a partir de '$idb'");
- $nom_champ = strtolower($nom_champ);
- $conditionnel = array();
- // attention: entre la boucle nommee 0, "" et le tableau vide,
- // il y a incoherences qu'il vaut mieux eviter
- while (isset($boucles[$idb])) {
- $joker = true;
- // 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 ($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);
- }
- // 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) {
- # spip_log("On remonte vers $i");
- // Sinon on remonte d'un cran
- $idb = $boucles[$idb]->id_parent;
- $i++;
- } else {
- $idb = null;
- }
- }
- # spip_log("Pas vu $nom_champ");
- // esperons qu'il y sera
- // ou qu'on a fourni une valeur par "defaut" plus pertinent
- return index_compose($conditionnel, $defaut);
- }
- /**
- * 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)) {
- // si on passe defaut = '', ne pas générer d'erreur de compilation.
- $defaut = "($c:(" . ($defaut ? $defaut : "''") . "))";
- }
- return $defaut;
- }
- /**
- * 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) {
- $r = $boucles[$idb]->type_requete;
- // 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 . "'", '');
- }
- $desc = $boucles[$idb]->show;
- // 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);
- } // 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);
- }
- // 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
- // 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('', '');
- }
- }
- }
- /**
- * 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) {
- $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) {
- $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 !
- $j = $trouver_table($e, $boucle->sql_serveur);
- if (!$j) {
- return array('', '');
- }
- $e = $j['table'];
- if (!$t = array_search($e, $boucle->from)) {
- $k = $j['key']['PRIMARY KEY'];
- 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('', '');
- }
- }
- $k = array($boucle->id_table, array($e), $k);
- fabrique_jointures($boucle, array($k));
- $t = array_search($e, $boucle->from);
- }
- }
- } 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;
- }
- return array("$t.$excep", $nom_champ);
- }
- /**
- * Demande le champ '$champ' dans la pile
- *
- * 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 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
- * @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);
- }
- /**
- * 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);
- }
- /**
- * 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 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)
- ) {
- $res = $f($p);
- if ($res !== null and is_object($res)) {
- return $res;
- }
- }
- $f = charger_fonction('DEFAUT', 'calculer_balise');
- 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,
- $p->code = index_pile($p->id_boucle, $nom, $p->boucles, $p->nom_boucle);
- // 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]) {
- $code = addslashes($p->fonctions[0][1]);
- $p->code .= " . '$code'";
- }
- // ne pas passer le filtre securite sur les id_xxx
- if (strpos($nom, 'ID_') === 0) {
- $p->interdire_scripts = false;
- }
- // Compatibilite ascendante avec les couleurs html (#FEFEFE) :
- // SI le champ SQL n'est pas trouve
- // ET si la balise a une forme de couleur
- // ET s'il n'y a ni filtre ni etoile
- // 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
- ) {
- $p->code = "'#$nom'";
- $p->interdire_scripts = false;
- }
- return $p;
- }
- /** Code PHP d'exécution d'une balise dynamique */
- define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s',
- array(%s%s),
- array(%s%s))");
- /**
- * 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]) {
- $p->fonctions = null;
- }
- 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 = "";
- }
- $collecte = collecter_balise_dynamique($l, $p, $nom);
- $p->code = sprintf(CODE_EXECUTER_BALISE, $nom,
- join(',', $collecte),
- ($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.
- *
- * 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;
- }
- return $args;
- }
- /**
- * Récuperer le nom du serveur
- *
- * 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])
- ) {
- $s = $p->boucles[$nom]->sql_serveur;
- if (strlen($s)
- and strlen($serveur = strtolower($s))
- and !in_array($serveur, $GLOBALS['exception_des_connect'])
- ) {
- return $serveur;
- }
- }
- return "";
- }
- /**
- * Teste si une balise est appliquée sur une base distante
- *
- * 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 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'));
- return false;
- }
- return true;
- }
- //
- // Traitements standard de divers champs
- // definis par $table_des_traitements, cf. ecrire/public/interfaces
- //
- // http://code.spip.net/@champs_traitements
- function champs_traitements($p) {
- 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 = $GLOBALS['table_des_traitements']['*'];
- } else {
- $ps = false;
- }
- }
- if (is_array($ps)) {
- // Recuperer le type de boucle (articles, DATA) et la table SQL sur laquelle elle porte
- $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;
- // le traitement peut n'etre defini que pour une table en particulier "spip_articles"
- 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])) {
- $ps = $ps[$type_requete];
- } // ou pour indiferrement quelle que soit la boucle
- elseif (isset($ps[0])) {
- $ps = $ps[0];
- } else {
- $ps = false;
- }
- }
- if (!$ps) {
- return $p->code;
- }
- // Si une boucle DOCUMENTS{doublons} est presente dans le squelette,
- // ou si in INCLURE contient {doublons}
- // on insere une fonction de remplissage du tableau des doublons
- // dans les filtres propre() ou typo()
- // (qui traitent les raccourcis <docXX> 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 . ')';
- }
- // La protection des champs par |safehtml est assuree par les extensions
- // dans la declaration des traitements des champs sensibles
- // Remplacer enfin le placeholder %s par le vrai code de la balise
- return str_replace('%s', $p->code, $ps);
- }
- //
- // Appliquer les filtres a un champ [(#CHAMP|filtre1|filtre2)]
- // retourne un code php compile exprimant ce champ filtre et securise
- // - une etoile => pas de processeurs standards
- // - deux etoiles => pas de securite non plus !
- //
- // http://code.spip.net/@applique_filtres
- function applique_filtres($p) {
- // Traitements standards (cf. supra)
- if ($p->etoile == '') {
- $code = champs_traitements($p);
- } else {
- $code = $p->code;
- }
- // Appliquer les filtres perso
- 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'])) {
- $code = "invalideur_session(\$Cache, $code)";
- }
- $code = sandbox_composer_interdire_scripts($code, $p);
- return $code;
- }
- // Cf. function pipeline dans ecrire/inc_utils.php
- // http://code.spip.net/@compose_filtres
- function compose_filtres(&$p, $code) {
- $image_miette = false;
- 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
- $code = "filtrer('image_graver', $code)";
- $image_miette = false;
- }
- // recuperer les arguments du filtre,
- // a separer par "," ou ":" dans le cas du filtre "?{a,b}"
- if ($fonc !== '?') {
- $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] : "");
- }
- }
- $arglist = compose_filtres_args($p, $filtre, $sep);
- $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;
- }
- }
- }
- // ramasser les images intermediaires inutiles et graver l'image finale
- 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) {
- switch (true) {
- case in_array($fonc, $GLOBALS['table_criteres_infixes']):
- return "($code $fonc $arg)";
- case ($fonc == 'and') or ($fonc == 'et'):
- return "((($code) AND ($arg)) ?' ' :'')";
- case ($fonc == 'or') or ($fonc == 'ou'):
- return "((($code) OR ($arg)) ?' ' :'')";
- 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'):
- return "(($code) ?'' :' ')";
- case ($fonc == 'yes') or ($fonc == 'oui'):
- return "(($code) ?' ' :'')";
- }
- return '';
- }
- // 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);
- }
- return $arglist;
- }
- /**
- * 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);
- }
- //
- // Rechercher dans la pile des boucles actives celle ayant un critere
- // comportant un certain $motif, et construire alors une reference
- // a l'environnement de cette boucle, qu'on indexe avec $champ.
- // Sert a referencer une cellule non declaree dans la table et pourtant la.
- // Par exemple pour la balise #POINTS on produit $Pile[$SP-n]['points']
- // si la n-ieme boucle a un critere "recherche", car on sait qu'il a produit
- // "SELECT XXXX AS points"
- //
- // 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) {
- if ($critere->op == $motif) {
- $p->code = '$Pile[$SP' . (($n == 0) ? "" : "-$n") .
- "]['$champ']";
- $b = '';
- break 2;
- }
- }
- $n++;
- $b = $p->boucles[$b]->id_parent;
- }
- // si on est hors d'une boucle de {recherche}, cette balise est vide
- if (!$p->code) {
- $p->code = "''";
- }
- $p->interdire_scripts = false;
- return $p;
- }