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) . '\']'; } $idb_origine = $idb; $nom_champ_origine = $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; } // renseigner la boucle source de ce champ pour les traitements $boucles[$idb_origine]->index_champ[$nom_champ_origine] = $idb; $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); // si le champ a ete trouve dans une boucle parente sa source est renseignee ici if (!empty($p->boucles[$idb]->index_champ[$p->nom_champ])) { $idb = $p->boucles[$idb]->index_champ[$p->nom_champ]; } // 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; // bien prendre en compte les alias de boucles (hierarchie => rubrique, syndication => syncdic, etc.) if ($type_requete and isset($GLOBALS['table_des_tables'][$type_requete])) { $type_alias = $type_requete; $type_requete = $GLOBALS['table_des_tables'][$type_requete]; } else { $type_alias = 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 une boucle utilisant un alias ("hierarchie") elseif ($type_alias and isset($ps[$type_alias])) { $ps = $ps[$type_alias]; } // ou pour indifféremment 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 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; }