/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
- * Copyright (c) 2001-2012 *
+ * Copyright (c) 2001-2017 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
/**
* Définition des {criteres} d'une boucle
*
- * @package SPIP\Compilateur\Criteres
-**/
+ * @package SPIP\Core\Compilateur\Criteres
+ **/
-if (!defined('_ECRIRE_INC_VERSION')) return;
+if (!defined('_ECRIRE_INC_VERSION')) {
+ return;
+}
/**
* Une Regexp repérant une chaine produite par le compilateur,
* souvent utilisée pour faire de la concaténation lors de la compilation
* plutôt qu'à l'exécution, i.e. pour remplacer 'x'.'y' par 'xy'
-**/
+ **/
define('_CODE_QUOTE', ",^(\n//[^\n]*\n)? *'(.*)' *$,");
-
/**
* Compile le critère {racine}
*
* c'est à dire ayant id_parent=0
*
* @link http://www.spip.net/@racine
- *
- * @param string $idb
- * Identifiant de la boucle
- * @param array $boucles
- * AST du squelette
- * @param array $crit
- * Paramètres du critère dans cette boucle
- * @return
- * AST complété de la gestion du critère
-**/
-function critere_racine_dist($idb, &$boucles, $crit){
- global $exceptions_des_tables;
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_racine_dist($idb, &$boucles, $crit) {
+
$not = $crit->not;
$boucle = &$boucles[$idb];
- $id_parent = isset($exceptions_des_tables[$boucle->id_table]['id_parent']) ?
- $exceptions_des_tables[$boucle->id_table]['id_parent'] :
+ $id_parent = isset($GLOBALS['exceptions_des_tables'][$boucle->id_table]['id_parent']) ?
+ $GLOBALS['exceptions_des_tables'][$boucle->id_table]['id_parent'] :
'id_parent';
- $c = array("'='", "'$boucle->id_table."."$id_parent'", 0);
+ $c = array("'='", "'$boucle->id_table." . "$id_parent'", 0);
$boucle->where[] = ($crit->not ? array("'NOT'", $c) : $c);
}
* Compile le critère {exclus}
*
* Exclut du résultat l’élément dans lequel on se trouve déjà
- *
+ *
* @link http://www.spip.net/@exclus
*
- * @param string $idb
- * Identifiant de la boucle
- * @param array $boucles
- * AST du squelette
- * @param array $crit
- * Paramètres du critère dans cette boucle
- * @return
- * AST complété de la gestion du critère
-**/
-function critere_exclus_dist($idb, &$boucles, $crit){
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_exclus_dist($idb, &$boucles, $crit) {
$not = $crit->not;
$boucle = &$boucles[$idb];
$id = $boucle->primary;
- if ($not OR !$id)
- return (array('zbug_critere_inconnu', array('critere' => $not.$crit->op)));
+ if ($not or !$id) {
+ return (array('zbug_critere_inconnu', array('critere' => $not . $crit->op)));
+ }
$arg = kwote(calculer_argument_precedent($idb, $id, $boucles));
- $boucle->where[] = array("'!='", "'$boucle->id_table."."$id'", $arg);
+ $boucle->where[] = array("'!='", "'$boucle->id_table." . "$id'", $arg);
}
* dans un précédent critère {doublon} sur une boucle de même table.
*
* Il est possible de spécifier un nom au doublon tel que {doublons sommaire}
- *
+ *
* @link http://www.spip.net/@doublons
- *
- * @param string $idb
- * Identifiant de la boucle
- * @param array $boucles
- * AST du squelette
- * @param array $crit
- * Paramètres du critère dans cette boucle
- * @return
- * AST complété de la gestion du critère
-**/
-function critere_doublons_dist($idb, &$boucles, $crit){
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_doublons_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$primary = $boucle->primary;
// la table nécessite une clé primaire, non composée
- if (!$primary OR strpos($primary, ',')){
+ if (!$primary or strpos($primary, ',')) {
return (array('zbug_doublon_sur_table_sans_cle_primaire'));
}
$not = ($crit->not ? '' : 'NOT');
// le doublon s'applique sur un type de boucle (article)
- $nom = "'" . $boucle->type_requete. "'";
+ $nom = "'" . $boucle->type_requete . "'";
// compléter le nom avec un nom précisé {doublons nom}
// on obtient $nom = "'article' . 'nom'"
// $doublons et son index, ici $nom
// debut du code "sql_in('articles.id_article', "
- $debut_in = "sql_in('".$boucle->id_table.'.'.$primary."', ";
+ $debut_in = "sql_in('" . $boucle->id_table . '.' . $primary . "', ";
// lecture des données du doublon "$doublons[$doublon_index[] = "
// Attention : boucle->doublons désigne une variable qu'on affecte
- $debut_doub = '$doublons[' . (!$not ? '' : ($boucle->doublons."[]= "));
+ $debut_doub = '$doublons[' . (!$not ? '' : ($boucle->doublons . "[]= "));
// le debut complet du code des doublons
$debut_doub = $debut_in . $debut_doub;
// si on trouve un autre critère doublon,
// on fusionne pour avoir un seul IN, et on s'en va !
foreach ($boucle->where as $k => $w) {
- if (strpos($w[0], $debut_doub)===0) {
+ if (strpos($w[0], $debut_doub) === 0) {
// fusionner le sql_in (du where)
- $boucle->where[$k][0] = $debut_doub . $fin_doub.' . '.substr($w[0], strlen($debut_in));
+ $boucle->where[$k][0] = $debut_doub . $fin_doub . ' . ' . substr($w[0], strlen($debut_in));
// fusionner l'initialisation (du hash) pour faire plus joli
$x = strpos($boucle->hash, $init_comment);
$len = strlen($init_comment);
$boucle->hash =
substr($boucle->hash, 0, $x + $len) . $init_code . substr($boucle->hash, $x + $len);
+
return;
}
}
// mettre l'ensemble dans un tableau pour que ce ne soit pas vu comme une constante
- $boucle->where[] = array($debut_doub . $fin_doub.", '".$not."')");
+ $boucle->where[] = array($debut_doub . $fin_doub . ", '" . $not . "')");
// déclarer le doublon s'il n'existe pas encore
$boucle->hash .= $init_comment . $init_code;
* les éléments dans la langue en cours. Certaines boucles
* tel que articles et rubriques restreignent par défaut sur la langue
* en cours.
- *
+ *
* Sans définir de valeur au critère, celui-ci utilise 'oui' comme
* valeur par défaut.
- *
- * @param string $idb
- * Identifiant de la boucle
- * @param array $boucles
- * AST du squelette
- * @param array $crit
- * Paramètres du critère dans cette boucle
- * @return
- * AST complété de la gestion du critère
-**/
-function critere_lang_select_dist($idb, &$boucles, $crit){
- if (!isset($crit->param[1][0]) OR !($param = $crit->param[1][0]->texte)) $param = 'oui';
- if ($crit->not) $param = ($param=='oui') ? 'non' : 'oui';
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_lang_select_dist($idb, &$boucles, $crit) {
+ if (!isset($crit->param[1][0]) or !($param = $crit->param[1][0]->texte)) {
+ $param = 'oui';
+ }
+ if ($crit->not) {
+ $param = ($param == 'oui') ? 'non' : 'oui';
+ }
$boucle = &$boucles[$idb];
$boucle->lang_select = $param;
}
-// {debut_xxx}
-// http://www.spip.net/@debut_
-// http://doc.spip.org/@critere_debut_dist
-function critere_debut_dist($idb, &$boucles, $crit){
+
+/**
+ * Compile le critère {debut_xxx}
+ *
+ * Limite le nombre d'éléments affichés.
+ *
+ * Ce critère permet de faire commencer la limitation des résultats
+ * par une variable passée dans l’URL et commençant par 'debut_' tel que
+ * {debut_page,10}. Le second paramètre est le nombre de résultats à
+ * afficher.
+ *
+ * Note : il est plus simple d'utiliser le critère pagination.
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_debut_dist($idb, &$boucles, $crit) {
list($un, $deux) = $crit->param;
$un = $un[0]->texte;
$deux = $deux[0]->texte;
- if ($deux){
- $boucles[$idb]->limit = 'intval($Pile[0]["debut'.
- $un.
- '"]) . ",'.
- $deux.
- '"';
- } else calculer_critere_DEFAUT_dist($idb, $boucles, $crit);
+ if ($deux) {
+ $boucles[$idb]->limit = 'intval($Pile[0]["debut' .
+ $un .
+ '"]) . ",' .
+ $deux .
+ '"';
+ } else {
+ calculer_critere_DEFAUT_dist($idb, $boucles, $crit);
+ }
}
-// {pagination}
-// {pagination 20}
-// {pagination #ENV{pages,5}} etc
-// {pagination 20 #ENV{truc,chose}} pour utiliser la variable debut_#ENV{truc,chose}
-// http://www.spip.net/@pagination
-// http://doc.spip.org/@critere_pagination_dist
-function critere_pagination_dist($idb, &$boucles, $crit){
+
+/**
+ * Compile le critère `pagination` qui demande à paginer une boucle.
+ *
+ * Demande à paginer la boucle pour n'afficher qu'une partie des résultats,
+ * et gère l'affichage de la partie de page demandée par debut_xx dans
+ * dans l'environnement du squelette.
+ *
+ * Le premier paramètre indique le nombre d'éléments par page, le second,
+ * rarement utilisé permet de définir le nom de la variable désignant la
+ * page demandée (`debut_xx`), qui par défaut utilise l'identifiant de la boucle.
+ *
+ * @critere
+ * @see balise_PAGINATION_dist()
+ * @link http://www.spip.net/3367 Le système de pagination
+ * @link http://www.spip.net/4867 Le critère pagination
+ * @example
+ * ```
+ * {pagination}
+ * {pagination 20}
+ * {pagination #ENV{pages,5}} etc
+ * {pagination 20 #ENV{truc,chose}} pour utiliser la variable debut_#ENV{truc,chose}
+ * ```
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_pagination_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
// definition de la taille de la page
$pas = !isset($crit->param[0][0]) ? "''"
: calculer_liste(array($crit->param[0][0]), array(), $boucles, $boucle->id_parent);
- if (!preg_match(_CODE_QUOTE, $pas, $r)){
+ if (!preg_match(_CODE_QUOTE, $pas, $r)) {
$pas = "((\$a = intval($pas)) ? \$a : 10)";
} else {
$r = intval($r[2]);
$pas = strval($r ? $r : 10);
}
- $type = !isset($crit->param[0][1]) ? "'$idb'"
- : calculer_liste(array($crit->param[0][1]), array(), $boucles, $boucle->id_parent);
- $debut = ($type[0]!=="'") ? "'debut'.$type"
- : ("'debut".substr($type, 1));
+ // Calcul du nommage de la pagination si il existe.
+ // La nouvelle syntaxe {pagination 20, nom} est prise en compte et privilégiée mais on reste
+ // compatible avec l'ancienne car certains cas fonctionnent correctement
+ $type = "'$idb'";
+ // Calcul d'un nommage spécifique de la pagination si précisé.
+ // Syntaxe {pagination 20, nom}
+ if (isset($crit->param[0][1])) {
+ $type = calculer_liste(array($crit->param[0][1]), array(), $boucles, $boucle->id_parent);
+ } // Ancienne syntaxe {pagination 20 nom} pour compatibilité
+ elseif (isset($crit->param[1][0])) {
+ $type = calculer_liste(array($crit->param[1][0]), array(), $boucles, $boucle->id_parent);
+ }
+
+ $debut = ($type[0] !== "'") ? "'debut'.$type" : ("'debut" . substr($type, 1));
$boucle->modificateur['debut_nom'] = $type;
$partie =
// tester si le numero de page demande est de la forme '@yyy'
- 'isset($Pile[0]['.$debut.']) ? $Pile[0]['.$debut.'] : _request('.$debut.");\n"
- ."\tif(substr(\$debut_boucle,0,1)=='@'){\n"
- ."\t\t".'$debut_boucle = $Pile[0]['.$debut.'] = quete_debut_pagination(\''.$boucle->primary.'\',$Pile[0][\'@'.$boucle->primary.'\'] = substr($debut_boucle,1),'.$pas.',$iter);'."\n"
- ."\t\t".'$iter->seek(0);'."\n"
- ."\t}\n"
- ."\t".'$debut_boucle = intval($debut_boucle)';
+ 'isset($Pile[0][' . $debut . ']) ? $Pile[0][' . $debut . '] : _request(' . $debut . ");\n"
+ . "\tif(substr(\$debut_boucle,0,1)=='@'){\n"
+ . "\t\t" . '$debut_boucle = $Pile[0][' . $debut . '] = quete_debut_pagination(\'' . $boucle->primary . '\',$Pile[0][\'@' . $boucle->primary . '\'] = substr($debut_boucle,1),' . $pas . ',$iter);' . "\n"
+ . "\t\t" . '$iter->seek(0);' . "\n"
+ . "\t}\n"
+ . "\t" . '$debut_boucle = intval($debut_boucle)';
$boucle->hash .= '
- $command[\'pagination\'] = array((isset($Pile[0]['.$debut.']) ? $Pile[0]['.$debut.'] : null), ' . $pas . ');';
+ $command[\'pagination\'] = array((isset($Pile[0][' . $debut . ']) ? $Pile[0][' . $debut . '] : null), ' . $pas . ');';
$boucle->total_parties = $pas;
calculer_parties($boucles, $idb, $partie, 'p+');
// ajouter la cle primaire dans le select pour pouvoir gerer la pagination referencee par @id
// sauf si pas de primaire, ou si primaire composee
// dans ce cas, on ne sait pas gerer une pagination indirecte
- $t = $boucle->id_table.'.'.$boucle->primary;
+ $t = $boucle->id_table . '.' . $boucle->primary;
if ($boucle->primary
- AND !preg_match('/[,\s]/', $boucle->primary)
- AND !in_array($t, $boucle->select)
- )
+ and !preg_match('/[,\s]/', $boucle->primary)
+ and !in_array($t, $boucle->select)
+ ) {
$boucle->select[] = $t;
+ }
}
-// {recherche} ou {recherche susan}
-// http://www.spip.net/@recherche
-// http://doc.spip.org/@critere_recherche_dist
-function critere_recherche_dist($idb, &$boucles, $crit){
+/**
+ * Compile le critère `recherche` qui permet de sélectionner des résultats
+ * d'une recherche.
+ *
+ * Le texte cherché est pris dans le premier paramètre `{recherche xx}`
+ * ou à défaut dans la clé `recherche` de l'environnement du squelette.
+ *
+ * @critere
+ * @link http://www.spip.net/3878
+ * @see inc_prepare_recherche_dist()
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_recherche_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
- if (isset($crit->param[0]))
+ if (!$boucle->primary or strpos($boucle->primary, ',')) {
+ erreur_squelette(_T('zbug_critere_sur_table_sans_cle_primaire', array('critere' => 'recherche')), $boucle);
+
+ return;
+ }
+
+ if (isset($crit->param[0])) {
$quoi = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
- else
+ } else {
$quoi = '(isset($Pile[0]["recherche"])?$Pile[0]["recherche"]:(isset($GLOBALS["recherche"])?$GLOBALS["recherche"]:""))';
+ }
$_modificateur = var_export($boucle->modificateur, true);
$boucle->hash .= '
// RECHERCHE'
- .($crit->cond ? '
- if (!strlen('.$quoi.')){
+ . ($crit->cond ? '
+ if (!strlen(' . $quoi . ')){
list($rech_select, $rech_where) = array("0 as points","");
- } else' : '').'
+ } else' : '') . '
{
$prepare_recherche = charger_fonction(\'prepare_recherche\', \'inc\');
- list($rech_select, $rech_where) = $prepare_recherche('.$quoi.', "'.$boucle->id_table.'", "'.$crit->cond.'","'.$boucle->sql_serveur.'",'.$_modificateur.',"'.$boucle->primary.'");
+ list($rech_select, $rech_where) = $prepare_recherche(' . $quoi . ', "' . $boucle->id_table . '", "' . $crit->cond . '","' . $boucle->sql_serveur . '",' . $_modificateur . ',"' . $boucle->primary . '");
}
';
- $t = $boucle->id_table.'.'.$boucle->primary;
- if (!in_array($t, $boucles[$idb]->select))
- $boucle->select[] = $t; # pour postgres, neuneu ici
+
+ $t = $boucle->id_table . '.' . $boucle->primary;
+ if (!in_array($t, $boucles[$idb]->select)) {
+ $boucle->select[] = $t;
+ } # pour postgres, neuneu ici
// jointure uniquement sur le serveur principal
// (on ne peut joindre une table d'un serveur distant avec la table des resultats du serveur principal)
- if (!$boucle->sql_serveur){
- $boucle->join['resultats'] = array("'".$boucle->id_table."'", "'id'", "'".$boucle->primary."'");
+ if (!$boucle->sql_serveur) {
+ $boucle->join['resultats'] = array("'" . $boucle->id_table . "'", "'id'", "'" . $boucle->primary . "'");
$boucle->from['resultats'] = 'spip_resultats';
}
$boucle->select[] = '$rech_select';
$boucle->where[] = '$rech_where?$rech_where:\'\'';
}
-// {traduction}
-// http://www.spip.net/@traduction
-// (id_trad>0 AND id_trad=id_trad(precedent))
-// OR id_article=id_article(precedent)
-// http://doc.spip.org/@critere_traduction_dist
-function critere_traduction_dist($idb, &$boucles, $crit){
+/**
+ * Compile le critère `traduction`
+ *
+ * Sélectionne toutes les traductions de l'élément courant (la boucle englobante)
+ * en différentes langues (y compris l'élément englobant)
+ *
+ * Équivalent à `(id_trad>0 AND id_trad=id_trad(precedent)) OR id_xx=id_xx(precedent)`
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_traduction_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$prim = $boucle->primary;
$table = $boucle->id_table;
$arg = kwote(calculer_argument_precedent($idb, 'id_trad', $boucles));
$dprim = kwote(calculer_argument_precedent($idb, $prim, $boucles));
$boucle->where[] =
- array("'OR'",
- array("'AND'",
- array("'='", "'$table.id_trad'", 0),
- array("'='", "'$table.$prim'", $dprim)
- ),
- array("'AND'",
- array("'>'", "'$table.id_trad'", 0),
- array("'='", "'$table.id_trad'", $arg)
- )
+ array(
+ "'OR'",
+ array(
+ "'AND'",
+ array("'='", "'$table.id_trad'", 0),
+ array("'='", "'$table.$prim'", $dprim)
+ ),
+ array(
+ "'AND'",
+ array("'>'", "'$table.id_trad'", 0),
+ array("'='", "'$table.id_trad'", $arg)
+ )
);
}
-// {origine_traduction}
-// (id_trad>0 AND id_article=id_trad) OR (id_trad=0)
-// http://www.spip.net/@origine_traduction
-// http://doc.spip.org/@critere_origine_traduction_dist
-function critere_origine_traduction_dist($idb, &$boucles, $crit){
+
+/**
+ * Compile le critère {origine_traduction}
+ *
+ * Sélectionne les éléments qui servent de base à des versions traduites
+ * (par exemple les articles "originaux" sur une boucle articles)
+ *
+ * Équivalent à (id_trad>0 AND id_xx=id_trad) OR (id_trad=0)
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_origine_traduction_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$prim = $boucle->primary;
$table = $boucle->id_table;
$c =
- array("'OR'",
- array("'='", "'$table."."id_trad'", "'$table.$prim'"),
- array("'='", "'$table.id_trad'", "'0'")
+ array(
+ "'OR'",
+ array("'='", "'$table." . "id_trad'", "'$table.$prim'"),
+ array("'='", "'$table.id_trad'", "'0'")
);
$boucle->where[] = ($crit->not ? array("'NOT'", $c) : $c);
}
-// {meme_parent}
-// http://www.spip.net/@meme_parent
-// http://doc.spip.org/@critere_meme_parent_dist
-function critere_meme_parent_dist($idb, &$boucles, $crit){
- global $exceptions_des_tables;
+
+/**
+ * Compile le critère {meme_parent}
+ *
+ * Sélectionne les éléments ayant le même parent que la boucle parente,
+ * c'est à dire les frères et sœurs.
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_meme_parent_dist($idb, &$boucles, $crit) {
+
$boucle = &$boucles[$idb];
$arg = kwote(calculer_argument_precedent($idb, 'id_parent', $boucles));
- $id_parent = isset($exceptions_des_tables[$boucle->id_table]['id_parent']) ?
- $exceptions_des_tables[$boucle->id_table]['id_parent'] :
+ $id_parent = isset($GLOBALS['exceptions_des_tables'][$boucle->id_table]['id_parent']) ?
+ $GLOBALS['exceptions_des_tables'][$boucle->id_table]['id_parent'] :
'id_parent';
- $mparent = $boucle->id_table.'.'.$id_parent;
+ $mparent = $boucle->id_table . '.' . $id_parent;
- if ($boucle->type_requete=='rubriques' OR isset($exceptions_des_tables[$boucle->id_table]['id_parent'])){
+ if ($boucle->type_requete == 'rubriques' or isset($GLOBALS['exceptions_des_tables'][$boucle->id_table]['id_parent'])) {
$boucle->where[] = array("'='", "'$mparent'", $arg);
- }
- // le cas FORUMS est gere dans le plugin forum, dans la fonction critere_FORUMS_meme_parent_dist()
+ } // le cas FORUMS est gere dans le plugin forum, dans la fonction critere_FORUMS_meme_parent_dist()
else {
- return (array('zbug_critere_inconnu', array('critere' => $crit->op.' '.$boucle->type_requete)));
+ return (array('zbug_critere_inconnu', array('critere' => $crit->op . ' ' . $boucle->type_requete)));
}
}
/**
- * Sélectionne dans une boucle les éléments appartenant à une branche d'une rubrique
+ * Compile le critère `branche` qui sélectionne dans une boucle les
+ * éléments appartenant à une branche d'une rubrique.
+ *
+ * Cherche l'identifiant de la rubrique en premier paramètre du critère `{branche XX}`
+ * s'il est renseigné, sinon, sans paramètre (`{branche}` tout court) dans les
+ * boucles parentes. On calcule avec lui la liste des identifiants
+ * de rubrique de toute la branche.
+ *
+ * La boucle qui possède ce critère cherche une liaison possible avec
+ * la colonne `id_rubrique`, et tentera de trouver une jointure avec une autre
+ * table si c'est nécessaire pour l'obtenir.
*
- * Calcule une branche d'une rubrique et conditionne la boucle avec.
- * Cherche l'identifiant de la rubrique dans les boucles parentes ou par jointure
- * et calcule la liste des identifiants de rubrique de toute la branche
+ * Ce critère peut être rendu optionnel avec `{branche ?}` en remarquant
+ * cependant que le test s'effectue sur la présence d'un champ 'id_rubrique'
+ * sinon d'une valeur 'id_rubrique' dans l'environnement (et non 'branche'
+ * donc).
*
* @link http://www.spip.net/@branche
- *
- * @param string $idb
- * Identifiant de la boucle
- * @param array $boucles
- * AST du squelette
- * @param array $crit
- * Paramètres du critère dans cette boucle
- * @return
- * AST complété de la condition where au niveau de la boucle,
- * restreignant celle ci aux rubriques de la branche
-**/
-function critere_branche_dist($idb, &$boucles, $crit){
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_branche_dist($idb, &$boucles, $crit) {
$not = $crit->not;
$boucle = &$boucles[$idb];
- $arg = calculer_argument_precedent($idb, 'id_rubrique', $boucles);
+ // prendre en priorite un identifiant en parametre {branche XX}
+ if (isset($crit->param[0])) {
+ $arg = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
+ // sinon on le prend chez une boucle parente
+ } else {
+ $arg = kwote(calculer_argument_precedent($idb, 'id_rubrique', $boucles), $boucle->sql_serveur, 'int NOT NULL');
+ }
//Trouver une jointure
$champ = "id_rubrique";
$desc = $boucle->show;
//Seulement si necessaire
- if (!array_key_exists($champ, $desc['field'])){
+ if (!array_key_exists($champ, $desc['field'])) {
$cle = trouver_jointure_champ($champ, $boucle);
$trouver_table = charger_fonction("trouver_table", "base");
$desc = $trouver_table($boucle->from[$cle]);
- if (count(trouver_champs_decomposes($champ, $desc))>1){
+ if (count(trouver_champs_decomposes($champ, $desc)) > 1) {
$decompose = decompose_champ_id_objet($champ);
$champ = array_shift($decompose);
- $boucle->where[] = array("'='", _q($cle.".".reset($decompose)), '"'.sql_quote(end($decompose)).'"');
+ $boucle->where[] = array("'='", _q($cle . "." . reset($decompose)), '"' . sql_quote(end($decompose)) . '"');
}
+ } else {
+ $cle = $boucle->id_table;
}
- else $cle = $boucle->id_table;
- $c = "sql_in('$cle".".$champ', calcul_branche_in($arg)"
- .($not ? ", 'NOT'" : '').")";
+ $c = "sql_in('$cle" . ".$champ', calcul_branche_in($arg)"
+ . ($not ? ", 'NOT'" : '') . ")";
$boucle->where[] = !$crit->cond ? $c :
- ("($arg ? $c : ".($not ? "'0=1'" : "'1=1'").')');
+ ("($arg ? $c : " . ($not ? "'0=1'" : "'1=1'") . ')');
}
-// {logo} liste les objets qui ont un logo
-// http://doc.spip.org/@critere_logo_dist
-function critere_logo_dist($idb, &$boucles, $crit){
+/**
+ * Compile le critère `logo` qui liste les objets qui ont un logo
+ *
+ * @uses lister_objets_avec_logos()
+ * Pour obtenir les éléments qui ont un logo
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_logo_dist($idb, &$boucles, $crit) {
$not = $crit->not;
$boucle = &$boucles[$idb];
- $c = "sql_in('".
- $boucle->id_table.'.'.$boucle->primary
- ."', lister_objets_avec_logos('".$boucle->primary."'), '')";
+ $c = "sql_in('" .
+ $boucle->id_table . '.' . $boucle->primary
+ . "', lister_objets_avec_logos('" . $boucle->primary . "'), '')";
- if ($crit->cond) $c = "($arg ? $c : 1)";
+ if ($crit->cond) {
+ $c = "($arg ? $c : 1)";
+ }
- if ($not)
+ if ($not) {
$boucle->where[] = array("'NOT'", $c);
- else
+ } else {
$boucle->where[] = $c;
+ }
}
-// c'est la commande SQL "GROUP BY"
-// par exemple <boucle(articles){fusion lang}>
-// http://doc.spip.org/@critere_fusion_dist
-function critere_fusion_dist($idb, &$boucles, $crit){
- if ($t = isset($crit->param[0])){
+
+/**
+ * Compile le critère `fusion` qui regroupe les éléments selon une colonne.
+ *
+ * C'est la commande SQL «GROUP BY»
+ *
+ * @critere
+ * @link http://www.spip.net/5166
+ * @example
+ * ```
+ * <BOUCLE_a(articles){fusion lang}>
+ * ```
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_fusion_dist($idb, &$boucles, $crit) {
+ if ($t = isset($crit->param[0])) {
$t = $crit->param[0];
- if ($t[0]->type=='texte'){
+ if ($t[0]->type == 'texte') {
$t = $t[0]->texte;
- if (preg_match("/^(.*)\.(.*)$/", $t, $r)){
+ if (preg_match("/^(.*)\.(.*)$/", $t, $r)) {
$t = table_objet_sql($r[1]);
$t = array_search($t, $boucles[$idb]->from);
- if ($t) $t .= '.'.$r[2];
+ if ($t) {
+ $t .= '.' . $r[2];
+ }
}
} else {
$t = '".'
- .calculer_critere_arg_dynamique($idb, $boucles, $t)
- .'."';
+ . calculer_critere_arg_dynamique($idb, $boucles, $t)
+ . '."';
}
}
- if ($t){
+ if ($t) {
$boucles[$idb]->group[] = $t;
- if (!in_array($t, $boucles[$idb]->select))
+ if (!in_array($t, $boucles[$idb]->select)) {
$boucles[$idb]->select[] = $t;
- } else
- return (array('zbug_critere_inconnu', array('critere' => $crit->op.' ?')));
+ }
+ } else {
+ return (array('zbug_critere_inconnu', array('critere' => $crit->op . ' ?')));
+ }
}
-// c'est la commande SQL "COLLATE"
-// qui peut etre appliquee sur les order by, group by, where like ...
-// http://doc.spip.org/@critere_collecte_dist
-function critere_collecte_dist($idb, &$boucles, $crit){
- if (isset($crit->param[0])){
+/**
+ * Compile le critère `{collecte}` qui permet de spécifier l'interclassement
+ * à utiliser pour les tris de la boucle.
+ *
+ * Cela permet avec le critère `{par}` de trier un texte selon
+ * l'interclassement indiqué. L'instruction s'appliquera sur les critères `{par}`
+ * qui succèdent ce critère, ainsi qu'au critère `{par}` précédent
+ * si aucun interclassement ne lui est déjà appliqué.
+ *
+ * Techniquement, c'est la commande SQL "COLLATE" qui utilisée.
+ * (elle peut être appliquée sur les order by, group by, where, like ...)
+ *
+ * @example
+ * - `{par titre}{collecte utf8_spanish_ci}` ou `{collecte utf8_spanish_ci}{par titre}`
+ * - `{par titre}{par surtitre}{collecte utf8_spanish_ci}` :
+ * Seul 'surtitre' (`par` précédent) utilisera l'interclassement
+ * - `{collecte utf8_spanish_ci}{par titre}{par surtitre}` :
+ * 'titre' et 'surtitre' utiliseront l'interclassement (tous les `par` suivants)
+ *
+ * @note
+ * Piège sur une éventuelle écriture peu probable :
+ * `{par a}{collecte c1}{par b}{collecte c2}` : le tri `{par b}`
+ * utiliserait l'interclassement c1 (et non c2 qui ne s'applique pas
+ * au `par` précédent s'il a déjà un interclassement demandé).
+ *
+ * @critere
+ * @link http://www.spip.net/4028
+ * @see critere_par_dist() Le critère `{par}`
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ */
+function critere_collecte_dist($idb, &$boucles, $crit) {
+ if (isset($crit->param[0])) {
$_coll = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
$boucle = $boucles[$idb];
$boucle->modificateur['collate'] = "($_coll ?' COLLATE '.$_coll:'')";
$n = count($boucle->order);
- if ($n && (strpos($boucle->order[$n-1], 'COLLATE')===false))
- $boucle->order[$n-1] .= " . ".$boucle->modificateur['collate'];
- } else
- return (array('zbug_critere_inconnu', array('critere' => $crit->op." ".count($boucles[$idb]->order))));
+ if ($n && (strpos($boucle->order[$n - 1], 'COLLATE') === false)) {
+ // l'instruction COLLATE doit être placée avant ASC ou DESC
+ // notamment lors de l'utilisation `{!par xxx}{collate yyy}`
+ if (
+ (false !== $i = strpos($boucle->order[$n - 1], 'ASC'))
+ OR (false !== $i = strpos($boucle->order[$n - 1], 'DESC'))
+ ) {
+ $boucle->order[$n - 1] = substr_replace($boucle->order[$n - 1], "' . " . $boucle->modificateur['collate'] . " . ' ", $i, 0);
+ } else {
+ $boucle->order[$n - 1] .= " . " . $boucle->modificateur['collate'];
+ }
+ }
+ } else {
+ return (array('zbug_critere_inconnu', array('critere' => $crit->op . " " . count($boucles[$idb]->order))));
+ }
}
-// http://doc.spip.org/@calculer_critere_arg_dynamique
-function calculer_critere_arg_dynamique($idb, &$boucles, $crit, $suffix = ''){
+// http://code.spip.net/@calculer_critere_arg_dynamique
+function calculer_critere_arg_dynamique($idb, &$boucles, $crit, $suffix = '') {
$boucle = $boucles[$idb];
- $alt = "('".$boucle->id_table.'.\' . $x'.$suffix.')';
- $var = '$champs_'.$idb;
- $desc = (strpos($boucle->in, "static $var =")!==false);
- if (!$desc){
+ $alt = "('" . $boucle->id_table . '.\' . $x' . $suffix . ')';
+ $var = '$champs_' . $idb;
+ $desc = (strpos($boucle->in, "static $var =") !== false);
+ if (!$desc) {
$desc = $boucle->show['field'];
$desc = implode(',', array_map('_q', array_keys($desc)));
- $boucles[$idb]->in .= "\n\tstatic $var = array(".$desc.");";
+ $boucles[$idb]->in .= "\n\tstatic $var = array(" . $desc . ");";
+ }
+ if ($desc) {
+ $alt = "(in_array(\$x, $var) ? $alt :(\$x$suffix))";
}
- if ($desc) $alt = "(in_array(\$x, $var) ? $alt :(\$x$suffix))";
$arg = calculer_liste($crit, array(), $boucles, $boucle->id_parent);
+
return "((\$x = preg_replace(\"/\\W/\",'', $arg)) ? $alt : '')";
}
-// Tri : {par xxxx}
-// http://www.spip.net/@par
-// http://doc.spip.org/@critere_par_dist
-function critere_par_dist($idb, &$boucles, $crit){
+/**
+ * Compile le critère `{par}` qui permet d'ordonner les résultats d'une boucle
+ *
+ * Demande à trier la boucle selon certains champs (en SQL, la commande `ORDER BY`).
+ * Si plusieurs tris sont demandés (plusieurs fois le critère `{par x}{par y}` dans une boucle ou plusieurs champs
+ * séparés par des virgules dans le critère `{par x, y, z}`), ils seront appliqués dans l'ordre.
+ *
+ * Quelques particularités :
+ * - `{par hasard}` : trie par hasard
+ * - `{par num titre}` : trie par numéro de titre
+ * - `{par multi titre}` : trie par la langue extraite d'une balise polyglotte `<multi>` sur le champ titre
+ * - `{!par date}` : trie par date inverse en utilisant le champ date principal déclaré pour la table (si c'est un objet éditorial).
+ * - `{!par points}` : trie par pertinence de résultat de recherche (avec le critère `{recherche}`)
+ * - `{par FUNCTION_SQL(n)}` : trie en utilisant une fonction SQL (peut dépendre du moteur SQL utilisé).
+ * Exemple : `{par SUBSTRING_INDEX(titre, ".", -1)}` (tri ~ alphabétique en ignorant les numéros de titres
+ * (exemple erroné car faux dès qu'un titre possède un point.)).
+ * - `{par table.champ}` : trie en effectuant une jointure sur la table indiquée.
+ * - `{par #BALISE}` : trie sur la valeur retournée par la balise (doit être un champ de la table, ou 'hasard').
+ *
+ * @example
+ * - `{par titre}`
+ * - `{!par date}`
+ * - `{par num titre, multi titre, hasard}`
+ *
+ * @critere
+ * @link http://www.spip.net/5531
+ * @see critere_tri_dist() Le critère `{tri ...}`
+ * @see critere_inverse_dist() Le critère `{inverse}`
+ *
+ * @uses critere_parinverse()
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ */
+function critere_par_dist($idb, &$boucles, $crit) {
return critere_parinverse($idb, $boucles, $crit);
}
-// http://doc.spip.org/@critere_parinverse
-function critere_parinverse($idb, &$boucles, $crit, $sens = ''){
- global $exceptions_des_jointures;
+/**
+ * Calculs pour le critère `{par}` ou `{inverse}` pour ordonner les résultats d'une boucle
+ *
+ * Les expressions intermédiaires `{par expr champ}` sont calculées dans des fonctions
+ * `calculer_critere_par_expression_{expr}()` notamment `{par num champ}` ou `{par multi champ}`.
+ *
+ * @see critere_par_dist() Le critère `{par}` pour des exemples
+ *
+ * @uses calculer_critere_arg_dynamique() pour le calcul de `{par #ENV{tri}}`
+ * @uses calculer_critere_par_hasard() pour le calcul de `{par hasard}`
+ * @uses calculer_critere_par_champ()
+ * @see calculer_critere_par_expression_num() pour le calcul de `{par num x}`
+ * @see calculer_critere_par_expression_multi() pour le calcul de `{par multi x}`
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ */
+function critere_parinverse($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
- if ($crit->not) $sens = $sens ? "" : " . ' DESC'";
- $collecte = (isset($boucle->modificateur['collecte'])) ? " . ".$boucle->modificateur['collecte'] : "";
- foreach ($crit->param as $tri){
+ $sens = $collecte = '';
+ if ($crit->not) {
+ $sens = " . ' DESC'";
+ }
+ if (isset($boucle->modificateur['collate'])) {
+ $collecte = ' . ' . $boucle->modificateur['collate'];
+ }
- $order = $fct = ""; // en cas de fonction SQL
- // tris specifies dynamiquement
- if ($tri[0]->type!='texte'){
+ // Pour chaque paramètre du critère
+ foreach ($crit->param as $tri) {
+ $order = $fct = '';
+ // tris specifiés dynamiquement {par #ENV{tri}}
+ if ($tri[0]->type != 'texte') {
// calculer le order dynamique qui verifie les champs
$order = calculer_critere_arg_dynamique($idb, $boucles, $tri, $sens);
- // et si ce n'est fait, ajouter un champ 'hasard'
- // pour supporter 'hasard' comme tri dynamique
- $par = "rand()";
- $parha = $par." AS hasard";
- if (!in_array($parha, $boucle->select))
- $boucle->select[] = $parha;
- } else {
+ // ajouter 'hasard' comme possibilité de tri dynamique
+ calculer_critere_par_hasard($idb, $boucles, $crit);
+ }
+ // tris textuels {par titre}
+ else {
$par = array_shift($tri);
$par = $par->texte;
- // par multi champ
- if (preg_match(",^multi[\s]*(.*)$,", $par, $m)){
- $champ = trim($m[1]);
- // par multi L1.champ
- if (strpos($champ, '.')) {
- $cle = '';
- // par multi champ (champ sur une autre table)
- } elseif (!array_key_exists($champ, $boucle->show['field'])){
- $cle = trouver_jointure_champ($champ, $boucle);
- // par multi champ (champ dans la table en cours)
+
+ // tris de la forme {par expression champ} tel que {par num titre} ou {par multi titre}
+ if (preg_match(",^(\w+)[\s]+(.*)$,", $par, $m)) {
+ $expression = trim($m[1]);
+ $champ = trim($m[2]);
+ if (function_exists($f = 'calculer_critere_par_expression_' . $expression)) {
+ $order = $f($idb, $boucles, $crit, $tri, $champ);
} else {
- $cle = $boucle->id_table;
+ return array('zbug_critere_inconnu', array('critere' => $crit->op . " $par"));
}
- if ($cle) { $cle .= '.'; }
- $texte = $cle.$champ;
- $boucle->select[] = "\".sql_multi('".$texte."', \$GLOBALS['spip_lang']).\"";
- $order = "'multi'";
- // par num champ(, suite)
- } else if (preg_match(",^num (.*)$,m", $par, $m)) {
- $champ = trim($m[1]);
- // par num L1.champ
- if (strpos($champ, '.')) {
- $cle = '';
- // par num champ (champ sur une autre table)
- } elseif (!array_key_exists($champ, $boucle->show['field'])){
- $cle = trouver_jointure_champ($champ, $boucle);
- // par num champ (champ dans la table en cours)
- } else {
- $cle = $boucle->id_table;
+
+ // tris de la forme {par champ} ou {par FONCTION(champ)}
+ } elseif (preg_match(",^" . CHAMP_SQL_PLUS_FONC . '$,is', $par, $match)) {
+ // {par FONCTION(champ)}
+ if (count($match) > 2) {
+ $par = substr($match[2], 1, -1);
+ $fct = $match[1];
}
- if ($cle) { $cle .= '.'; }
- $texte = '0+'. $cle . $champ;
- $suite = calculer_liste($tri, array(), $boucles, $boucle->id_parent);
- if ($suite!=="''")
- $texte = "\" . ((\$x = $suite) ? ('$texte' . \$x) : '0')"." . \"";
- $as = 'num'.($boucle->order ? count($boucle->order) : "");
- $boucle->select[] = $texte." AS $as";
- $order = "'$as'";
- } else {
- if (!preg_match(",^".CHAMP_SQL_PLUS_FONC.'$,is', $par, $match)){
- return (array('zbug_critere_inconnu', array('critere' => $crit->op." $par")));
+ // quelques cas spécifiques {par hasard}, {par date}
+ if ($par == 'hasard') {
+ $order = calculer_critere_par_hasard($idb, $boucles, $crit);
+ } elseif ($par == 'date' and !empty($boucle->show['date'])) {
+ $order = "'" . $boucle->id_table . "." . $boucle->show['date'] . "'";
} else {
- if (count($match)>2){
- $par = substr($match[2], 1, -1);
- $fct = $match[1];
- }
- // par hasard
- if ($par=='hasard'){
- $par = "rand()";
- $boucle->select[] = $par." AS alea";
- $order = "'alea'";
- }
- // par titre_mot ou type_mot voire d'autres
- else if (isset($exceptions_des_jointures[$par])){
- list($table, $champ) = $exceptions_des_jointures[$par];
- $order = critere_par_joint($table, $champ, $boucle, $idb);
- if (!$order)
- return (array('zbug_critere_inconnu', array('critere' => $crit->op." $par")));
- }
- else if ($par=='date'
- AND $desc = $boucle->show
- AND $desc['date']
- ){
- $m = $desc['date'];
- $order = "'".$boucle->id_table.".".$m."'";
- }
- // par champ. Verifier qu'ils sont presents.
- elseif (preg_match("/^([^,]*)\.(.*)$/", $par, $r)) {
- // cas du tri sur champ de jointure explicite
- $t = array_search($r[1], $boucle->from);
- if (!$t){
- $t = trouver_jointure_champ($r[2], $boucle, array($r[1]));
- }
- if (!$t){
- return (array('zbug_critere_inconnu', array('critere' => $crit->op." $par")));
- } else $order = "'".$t.'.'.$r[2]."'";
- } else {
- $desc = $boucle->show;
- if ($desc['field'][$par])
- $par = $boucle->id_table.".".$par;
- // sinon tant pis, ca doit etre un champ synthetise (cf points)
- $order = "'$par'";
- }
+ // cas général {par champ}, {par table.champ}, ...
+ $order = calculer_critere_par_champ($idb, $boucles, $crit, $par);
}
}
+
+ // on ne sait pas traiter…
+ else {
+ return array('zbug_critere_inconnu', array('critere' => $crit->op . " $par"));
+ }
+
+ // En cas d'erreur de squelette retournée par une fonction
+ if (is_array($order)) {
+ return $order;
+ }
}
- if (preg_match('/^\'([^"]*)\'$/', $order, $m)){
+
+ if (preg_match('/^\'([^"]*)\'$/', $order, $m)) {
$t = $m[1];
- if (strpos($t, '.') AND !in_array($t, $boucle->select)){
+ if (strpos($t, '.') and !in_array($t, $boucle->select)) {
$boucle->select[] = $t;
}
- } else $sens = '';
+ } else {
+ $sens = '';
+ }
- if ($fct){
- if (preg_match("/^\s*'(.*)'\s*$/", $order, $r))
- $order = "'$fct(".$r[1].")'";
- else $order = "'$fct(' . $order . ')'";
+ if ($fct) {
+ if (preg_match("/^\s*'(.*)'\s*$/", $order, $r)) {
+ $order = "'$fct(" . $r[1] . ")'";
+ } else {
+ $order = "'$fct(' . $order . ')'";
+ }
+ }
+ $t = $order . $collecte . $sens;
+ if (preg_match("/^(.*)'\s*\.\s*'([^']*')$/", $t, $r)) {
+ $t = $r[1] . $r[2];
}
- $t = $order.$collecte.$sens;
- if (preg_match("/^(.*)'\s*\.\s*'([^']*')$/", $t, $r))
- $t = $r[1].$r[2];
+
$boucle->order[] = $t;
}
}
-// http://doc.spip.org/@critere_par_joint
-function critere_par_joint($table, $champ, &$boucle, $idb){
- $t = array_search($table, $boucle->from);
- if (!$t) $t = trouver_jointure_champ($champ, $boucle);
- return !$t ? '' : ("'".$t.'.'.$champ."'");
+/**
+ * Calculs pour le critère `{par hasard}`
+ *
+ * Ajoute le générateur d'aléatoire au SELECT de la boucle.
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return string Clause pour le Order by
+ */
+function calculer_critere_par_hasard($idb, &$boucles, $crit) {
+ $boucle = &$boucles[$idb];
+ // Si ce n'est fait, ajouter un champ 'hasard' dans le select
+ $parha = "rand() AS hasard";
+ if (!in_array($parha, $boucle->select)) {
+ $boucle->select[] = $parha;
+ }
+ return "'hasard'";
}
-// {inverse}
-// http://www.spip.net/@inverse
+/**
+ * Calculs pour le critère `{par num champ}` qui extrait le numéro préfixant un texte
+ *
+ * Tri par numéro de texte (tel que "10. titre"). Le numéro calculé est ajouté au SELECT
+ * de la boucle. L'écriture `{par num #ENV{tri}}` est aussi prise en compte.
+ *
+ * @note
+ * Les textes sans numéro valent 0 et sont donc placés avant les titres ayant des numéros.
+ * Utiliser `{par sinum champ, num champ}` pour avoir le comportement inverse.
+ *
+ * @see calculer_critere_par_expression_sinum() pour le critère `{par sinum champ}`
+ * @uses calculer_critere_par_champ()
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @param array $tri Paramètre en cours du critère
+ * @param string $champ Texte suivant l'expression ('titre' dans {par num titre})
+ * @return string Clause pour le Order by
+ */
+function calculer_critere_par_expression_num($idb, &$boucles, $crit, $tri, $champ) {
+ $_champ = calculer_critere_par_champ($idb, $boucles, $crit, $champ, true);
+ if (is_array($_champ)) {
+ return array('zbug_critere_inconnu', array('critere' => $crit->op . " num $champ"));
+ }
+ $boucle = &$boucles[$idb];
+ $texte = '0+' . $_champ;
+ $suite = calculer_liste($tri, array(), $boucles, $boucle->id_parent);
+ if ($suite !== "''") {
+ $texte = "\" . ((\$x = $suite) ? ('$texte' . \$x) : '0')" . " . \"";
+ }
+ $as = 'num' . ($boucle->order ? count($boucle->order) : "");
+ $boucle->select[] = $texte . " AS $as";
+ $order = "'$as'";
+ return $order;
+}
-// http://doc.spip.org/@critere_inverse_dist
-function critere_inverse_dist($idb, &$boucles, $crit){
+/**
+ * Calculs pour le critère `{par sinum champ}` qui ordonne les champs avec numéros en premier
+ *
+ * Ajoute au SELECT la valeur 'sinum' qui vaut 0 si le champ a un numéro, 1 s'il n'en a pas.
+ * Ainsi `{par sinum titre, num titre, titre}` mettra les éléments sans numéro en fin de liste,
+ * contrairement à `{par num titre, titre}` seulement.
+ *
+ * @see calculer_critere_par_expression_num() pour le critère `{par num champ}`
+ * @uses calculer_critere_par_champ()
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @param array $tri Paramètre en cours du critère
+ * @param string $champ Texte suivant l'expression ('titre' dans {par sinum titre})
+ * @return string Clause pour le Order by
+ */
+function calculer_critere_par_expression_sinum($idb, &$boucles, $crit, $tri, $champ) {
+ $_champ = calculer_critere_par_champ($idb, $boucles, $crit, $champ, true);
+ if (is_array($_champ)) {
+ return array('zbug_critere_inconnu', array('critere' => $crit->op . " sinum $champ"));
+ }
+ $boucle = &$boucles[$idb];
+ $texte = '0+' . $_champ;
+ $suite = calculer_liste($tri, array(), $boucles, $boucle->id_parent);
+ if ($suite !== "''") {
+ $texte = "\" . ((\$x = $suite) ? ('$texte' . \$x) : '0')" . " . \"";
+ }
+ $as = 'sinum' . ($boucle->order ? count($boucle->order) : "");
+ $boucle->select[] = 'CASE (' . $texte . ') WHEN 0 THEN 1 ELSE 0 END AS ' . $as;
+ $order = "'$as'";
+ return $order;
+}
+
+
+/**
+ * Calculs pour le critère `{par multi champ}` qui extrait la langue en cours dans les textes
+ * ayant des balises `<multi>` (polyglottes)
+ *
+ * Ajoute le calcul du texte multi extrait dans le SELECT de la boucle.
+ * Il ne peut y avoir qu'un seul critère de tri `multi` par boucle.
+ *
+ * @uses calculer_critere_par_champ()
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @param array $tri Paramètre en cours du critère
+ * @param string $champ Texte suivant l'expression ('titre' dans {par multi titre})
+ * @return string Clause pour le Order by
+ */
+function calculer_critere_par_expression_multi($idb, &$boucles, $crit, $tri, $champ) {
+ $_champ = calculer_critere_par_champ($idb, $boucles, $crit, $champ, true);
+ if (is_array($_champ)) {
+ return array('zbug_critere_inconnu', array('critere' => $crit->op . " multi $champ"));
+ }
+ $boucle = &$boucles[$idb];
+ $boucle->select[] = "\".sql_multi('" . $_champ . "', \$GLOBALS['spip_lang']).\"";
+ $order = "'multi'";
+ return $order;
+}
+
+/**
+ * Retourne le champ de tri demandé en ajoutant éventuellement les jointures nécessaires à la boucle.
+ *
+ * - si le champ existe dans la table, on l'utilise
+ * - si c'est une exception de jointure, on l'utilise (et crée la jointure au besoin)
+ * - si c'est un champ dont la jointure est déjà présente on la réutilise
+ * - si c'est un champ dont la jointure n'est pas présente, on la crée.
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @param string $par Nom du tri à analyser ('champ' ou 'table.champ')
+ * @param bool $raw Retourne le champ pour le compilateur ("'alias.champ'") ou brut ('alias.champ')
+ * @return array|string
+ */
+function calculer_critere_par_champ($idb, &$boucles, $crit, $par, $raw = false) {
+ $boucle = &$boucles[$idb];
+
+ // le champ existe dans la table, pas de souci (le plus commun)
+ if (isset($desc['field'][$par])) {
+ $par = $boucle->id_table . "." . $par;
+ }
+ // le champ est peut être une jointure
+ else {
+ $table = $table_alias = false; // toutes les tables de jointure possibles
+ $champ = $par;
+
+ // le champ demandé est une exception de jointure {par titre_mot}
+ if (isset($GLOBALS['exceptions_des_jointures'][$par])) {
+ list($table, $champ) = $GLOBALS['exceptions_des_jointures'][$par];
+ } // la table de jointure est explicitement indiquée {par truc.muche}
+ elseif (preg_match("/^([^,]*)\.(.*)$/", $par, $r)) {
+ list(, $table, $champ) = $r;
+ $table_alias = $table; // c'est peut-être un alias de table {par L1.titre}
+ $table = table_objet_sql($table);
+ }
+
+ // Si on connait la table d'arrivée, on la demande donc explicitement
+ // Sinon on cherche le champ dans les tables possibles de jointures
+ // Si la table est déjà dans le from, on la réutilise.
+ if ($infos = chercher_champ_dans_tables($champ, $boucle->from, $boucle->sql_serveur, $table)) {
+ $par = $infos['alias'] . "." . $champ;
+ } elseif (
+ $boucle->jointures_explicites
+ and $alias = trouver_jointure_champ($champ, $boucle, explode(' ', $boucle->jointures_explicites), false, $table)
+ ) {
+ $par = $alias . "." . $champ;
+ } elseif ($alias = trouver_jointure_champ($champ, $boucle, $boucle->jointures, false, $table)) {
+ $par = $alias . "." . $champ;
+ // en spécifiant directement l'alias {par L2.titre} (situation hasardeuse tout de même)
+ } elseif (
+ $table_alias
+ and isset($boucle->from[$table_alias])
+ and $infos = chercher_champ_dans_tables($champ, $boucle->from, $boucle->sql_serveur, $boucle->from[$table_alias])
+ ) {
+ $par = $infos['alias'] . "." . $champ;
+ } elseif ($table) {
+ // On avait table + champ, mais on ne les a pas trouvés
+ return array('zbug_critere_inconnu', array('critere' => $crit->op . " $par"));
+ } else {
+ // Sinon tant pis, ca doit etre un champ synthetise (cf points)
+ }
+ }
+
+ return $raw ? $par : "'$par'";
+}
+
+/**
+ * Retourne un champ de tri en créant une jointure
+ * si la table n'est pas présente dans le from de la boucle.
+ *
+ * @deprecated
+ * @param string $table Table du champ désiré
+ * @param string $champ Champ désiré
+ * @param Boucle $boucle Boucle en cours de compilation
+ * @return string Champ pour le compilateur si trouvé, tel que "'alias.champ'", sinon vide.
+ */
+function critere_par_joint($table, $champ, &$boucle) {
+ $t = array_search($table, $boucle->from);
+ if (!$t) {
+ $t = trouver_jointure_champ($champ, $boucle);
+ }
+ return !$t ? '' : ("'" . $t . '.' . $champ . "'");
+}
+
+/**
+ * Compile le critère `{inverse}` qui inverse l'ordre utilisé par le précédent critère `{par}`
+ *
+ * Accèpte un paramètre pour déterminer le sens : `{inverse #X}` utilisera un tri croissant (ASC)
+ * si la valeur retournée par `#X` est considérée vrai (`true`),
+ * le sens contraire (DESC) sinon.
+ *
+ * @example
+ * - `{par date}{inverse}`, équivalent à `{!par date}`
+ * - `{par date}{inverse #ENV{sens}}` utilise la valeur d'environnement sens pour déterminer le sens.
+ *
+ * @critere
+ * @see critere_par_dist() Le critère `{par}`
+ * @link http://www.spip.net/5530
+ * @uses critere_parinverse()
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ */
+function critere_inverse_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
// Classement par ordre inverse
- if ($crit->not)
+ if ($crit->not) {
critere_parinverse($idb, $boucles, $crit);
- else
- {
+ } else {
$order = "' DESC'";
// Classement par ordre inverse fonction eventuelle de #ENV{...}
- if (isset($crit->param[0])){
+ if (isset($crit->param[0])) {
$critere = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
$order = "(($critere)?' DESC':'')";
}
$n = count($boucle->order);
- if (!$n){
- if (isset($boucle->default_order[0]))
+ if (!$n) {
+ if (isset($boucle->default_order[0])) {
$boucle->default_order[0] .= ' . " DESC"';
- else
+ } else {
$boucle->default_order[] = ' DESC';
+ }
} else {
- $t = $boucle->order[$n-1]." . $order";
- if (preg_match("/^(.*)'\s*\.\s*'([^']*')$/", $t, $r))
- $t = $r[1].$r[2];
- $boucle->order[$n-1] = $t;
+ $t = $boucle->order[$n - 1] . " . $order";
+ if (preg_match("/^(.*)'\s*\.\s*'([^']*')$/", $t, $r)) {
+ $t = $r[1] . $r[2];
+ }
+ $boucle->order[$n - 1] = $t;
}
}
}
-// http://doc.spip.org/@critere_agenda_dist
-function critere_agenda_dist($idb, &$boucles, $crit){
+// http://code.spip.net/@critere_agenda_dist
+function critere_agenda_dist($idb, &$boucles, $crit) {
$params = $crit->param;
- if (count($params)<1)
- return (array('zbug_critere_inconnu', array('critere' => $crit->op." ?")));
-
- $parent = $boucles[$idb]->id_parent;
+ if (count($params) < 1) {
+ return array('zbug_critere_inconnu', array('critere' => $crit->op . " ?"));
+ }
- // les valeurs $date et $type doivent etre connus a la compilation
- // autrement dit ne pas etre des champs
+ $boucle = &$boucles[$idb];
+ $parent = $boucle->id_parent;
+ $fields = $boucle->show['field'];
$date = array_shift($params);
- $date = $date[0]->texte;
-
$type = array_shift($params);
- $type = $type[0]->texte;
+ // la valeur $type doit etre connue a la compilation
+ // donc etre forcement reduite a un litteral unique dans le source
+ $type = is_object($type[0]) ? $type[0]->texte : null;
+
+ // La valeur date doit designer un champ de la table SQL.
+ // Si c'est un litteral unique dans le source, verifier a la compil,
+ // sinon synthetiser le test de verif pour execution ulterieure
+ // On prendra arbitrairement le premier champ si test negatif.
+ if ((count($date) == 1) and ($date[0]->type == 'texte')) {
+ $date = $date[0]->texte;
+ if (!isset($fields[$date])) {
+ return array('zbug_critere_inconnu', array('critere' => $crit->op . " " . $date));
+ }
+ } else {
+ $a = calculer_liste($date, array(), $boucles, $parent);
+ $noms = array_keys($fields);
+ $defaut = $noms[0];
+ $noms = join(" ", $noms);
+ # bien laisser 2 espaces avant $nom pour que strpos<>0
+ $cond = "(\$a=strval($a))AND\nstrpos(\" $noms \",\" \$a \")";
+ $date = "'.(($cond)\n?\$a:\"$defaut\").'";
+ }
$annee = $params ? array_shift($params) : "";
- $annee = "\n".'sprintf("%04d", ($x = '.
- calculer_liste($annee, array(), $boucles, $parent).
- ') ? $x : date("Y"))';
+ $annee = "\n" . 'sprintf("%04d", ($x = ' .
+ calculer_liste($annee, array(), $boucles, $parent) .
+ ') ? $x : date("Y"))';
$mois = $params ? array_shift($params) : "";
- $mois = "\n".'sprintf("%02d", ($x = '.
- calculer_liste($mois, array(), $boucles, $parent).
- ') ? $x : date("m"))';
+ $mois = "\n" . 'sprintf("%02d", ($x = ' .
+ calculer_liste($mois, array(), $boucles, $parent) .
+ ') ? $x : date("m"))';
$jour = $params ? array_shift($params) : "";
- $jour = "\n".'sprintf("%02d", ($x = '.
- calculer_liste($jour, array(), $boucles, $parent).
- ') ? $x : date("d"))';
+ $jour = "\n" . 'sprintf("%02d", ($x = ' .
+ calculer_liste($jour, array(), $boucles, $parent) .
+ ') ? $x : date("d"))';
$annee2 = $params ? array_shift($params) : "";
- $annee2 = "\n".'sprintf("%04d", ($x = '.
- calculer_liste($annee2, array(), $boucles, $parent).
- ') ? $x : date("Y"))';
+ $annee2 = "\n" . 'sprintf("%04d", ($x = ' .
+ calculer_liste($annee2, array(), $boucles, $parent) .
+ ') ? $x : date("Y"))';
$mois2 = $params ? array_shift($params) : "";
- $mois2 = "\n".'sprintf("%02d", ($x = '.
- calculer_liste($mois2, array(), $boucles, $parent).
- ') ? $x : date("m"))';
+ $mois2 = "\n" . 'sprintf("%02d", ($x = ' .
+ calculer_liste($mois2, array(), $boucles, $parent) .
+ ') ? $x : date("m"))';
$jour2 = $params ? array_shift($params) : "";
- $jour2 = "\n".'sprintf("%02d", ($x = '.
- calculer_liste($jour2, array(), $boucles, $parent).
- ') ? $x : date("d"))';
-
- $boucle = &$boucles[$idb];
- $date = $boucle->id_table.".$date";
-
- $quote_end = ",'".$boucle->sql_serveur."','text'";
- if ($type=='jour')
- $boucle->where[] = array("'='", "'DATE_FORMAT($date, \'%Y%m%d\')'",
- ("sql_quote($annee . $mois . $jour$quote_end)"));
- elseif ($type=='mois')
- $boucle->where[] = array("'='", "'DATE_FORMAT($date, \'%Y%m\')'",
- ("sql_quote($annee . $mois$quote_end)"));
- elseif ($type=='semaine')
- $boucle->where[] = array("'AND'",
- array("'>='",
- "'DATE_FORMAT($date, \'%Y%m%d\')'",
- ("date_debut_semaine($annee, $mois, $jour)")),
- array("'<='",
- "'DATE_FORMAT($date, \'%Y%m%d\')'",
- ("date_fin_semaine($annee, $mois, $jour)")));
- elseif (count($crit->param)>2)
- $boucle->where[] = array("'AND'",
- array("'>='",
- "'DATE_FORMAT($date, \'%Y%m%d\')'",
- ("sql_quote($annee . $mois . $jour$quote_end)")),
- array("'<='", "'DATE_FORMAT($date, \'%Y%m%d\')'", ("sql_quote($annee2 . $mois2 . $jour2$quote_end)")));
+ $jour2 = "\n" . 'sprintf("%02d", ($x = ' .
+ calculer_liste($jour2, array(), $boucles, $parent) .
+ ') ? $x : date("d"))';
+
+ $date = $boucle->id_table . ".$date";
+
+ $quote_end = ",'" . $boucle->sql_serveur . "','text'";
+ if ($type == 'jour') {
+ $boucle->where[] = array(
+ "'='",
+ "'DATE_FORMAT($date, \'%Y%m%d\')'",
+ ("sql_quote($annee . $mois . $jour$quote_end)")
+ );
+ } elseif ($type == 'mois') {
+ $boucle->where[] = array(
+ "'='",
+ "'DATE_FORMAT($date, \'%Y%m\')'",
+ ("sql_quote($annee . $mois$quote_end)")
+ );
+ } elseif ($type == 'semaine') {
+ $boucle->where[] = array(
+ "'AND'",
+ array(
+ "'>='",
+ "'DATE_FORMAT($date, \'%Y%m%d\')'",
+ ("date_debut_semaine($annee, $mois, $jour)")
+ ),
+ array(
+ "'<='",
+ "'DATE_FORMAT($date, \'%Y%m%d\')'",
+ ("date_fin_semaine($annee, $mois, $jour)")
+ )
+ );
+ } elseif (count($crit->param) > 2) {
+ $boucle->where[] = array(
+ "'AND'",
+ array(
+ "'>='",
+ "'DATE_FORMAT($date, \'%Y%m%d\')'",
+ ("sql_quote($annee . $mois . $jour$quote_end)")
+ ),
+ array("'<='", "'DATE_FORMAT($date, \'%Y%m%d\')'", ("sql_quote($annee2 . $mois2 . $jour2$quote_end)"))
+ );
+ }
// sinon on prend tout
}
-// http://doc.spip.org/@calculer_critere_parties
-function calculer_critere_parties($idb, &$boucles, $crit){
+
+/**
+ * Compile les critères {i,j} et {i/j}
+ *
+ * Le critère {i,j} limite l'affiche de la boucle en commançant l'itération
+ * au i-ème élément, et pour j nombre d'éléments.
+ * Le critère {n-i,j} limite en commençant au n moins i-ème élément de boucle
+ * Le critère {i,n-j} limite en terminant au n moins j-ème élément de boucle.
+ *
+ * Le critère {i/j} affiche une part d'éléments de la boucle.
+ * Commence à i*n/j élément et boucle n/j éléments. {2/4} affiche le second
+ * quart des éléments d'une boucle.
+ *
+ * Traduit si possible (absence de n dans {i,j}) la demande en une
+ * expression LIMIT du gestionnaire SQL
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function calculer_critere_parties($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$a1 = $crit->param[0];
$a2 = $crit->param[1];
list($a11, $a12) = calculer_critere_parties_aux($idb, $boucles, $a1);
list($a21, $a22) = calculer_critere_parties_aux($idb, $boucles, $a2);
- if (($op==',') && (is_numeric($a11) && (is_numeric($a21)))){
- $boucle->limit = $a11.','.$a21;
- }
- else {
- $boucle->total_parties = ($a21!='n') ? $a21 : $a22;
- $partie = ($a11!='n') ? $a11 : $a12;
- $mode = (($op=='/') ? '/' :
- (($a11=='n') ? '-' : '+').(($a21=='n') ? '-' : '+'));
+ if (($op == ',') && (is_numeric($a11) && (is_numeric($a21)))) {
+ $boucle->limit = $a11 . ',' . $a21;
+ } else {
+ // 3 dans {1/3}, {2,3} ou {1,n-3}
+ $boucle->total_parties = ($a21 != 'n') ? $a21 : $a22;
+ // 2 dans {2/3}, {2,5}, {n-2,1}
+ $partie = ($a11 != 'n') ? $a11 : $a12;
+ $mode = (($op == '/') ? '/' :
+ (($a11 == 'n') ? '-' : '+') . (($a21 == 'n') ? '-' : '+'));
// cas simple {0,#ENV{truc}} compilons le en LIMIT :
- if ($a11!=='n' AND $a21!=='n' AND $mode=="++" AND $op==','){
+ if ($a11 !== 'n' and $a21 !== 'n' and $mode == "++" and $op == ',') {
$boucle->limit =
- (is_numeric($a11)?"'$a11'":$a11)
- .".','."
- .(is_numeric($a21)?"'$a21'":$a21);
- }
- else
+ (is_numeric($a11) ? "'$a11'" : $a11)
+ . ".','."
+ . (is_numeric($a21) ? "'$a21'" : $a21);
+ } else {
calculer_parties($boucles, $idb, $partie, $mode);
+ }
}
}
-//
-// Code specifique aux criteres {pagination}, {1,n} {n/m} etc
-//
-
-function calculer_parties(&$boucles, $id_boucle, $debut, $mode){
+/**
+ * Compile certains critères {i,j} et {i/j}
+ *
+ * Calcule une expression déterminant $debut_boucle et $fin_boucle (le
+ * début et la fin des éléments de la boucle qui doivent être affichés)
+ * et les déclare dans la propriété «mode_partie» de la boucle, qui se
+ * charge également de déplacer le pointeur de boucle sur le premier
+ * élément à afficher.
+ *
+ * Place dans la propriété partie un test vérifiant que l'élément de
+ * boucle en cours de lecture appartient bien à la plage autorisée.
+ * Trop tôt, passe à l'élément suivant, trop tard, sort de l'itération de boucle.
+ *
+ * @param array $boucles AST du squelette
+ * @param string $id_boucle Identifiant de la boucle
+ * @param string $debut Valeur ou code pour trouver le début (i dans {i,j})
+ * @param string $mode
+ * Mode (++, p+, +- ...) : 2 signes début & fin
+ * - le signe - indique
+ * -- qu'il faut soustraire debut du total {n-3,x}. 3 étant $debut
+ * -- qu'il faut raccourcir la fin {x,n-3} de 3 elements. 3 étant $total_parties
+ * - le signe p indique une pagination
+ * @return void
+ **/
+function calculer_parties(&$boucles, $id_boucle, $debut, $mode) {
$total_parties = $boucles[$id_boucle]->total_parties;
preg_match(",([+-/p])([+-/])?,", $mode, $regs);
list(, $op1, $op2) = array_pad($regs, 3, null);
$nombre_boucle = "\$Numrows['$id_boucle']['total']";
// {1/3}
- if ($op1=='/'){
- $pmoins1 = is_numeric($debut) ? ($debut-1) : "($debut-1)";
+ if ($op1 == '/') {
+ $pmoins1 = is_numeric($debut) ? ($debut - 1) : "($debut-1)";
$totpos = is_numeric($total_parties) ? ($total_parties) :
"($total_parties ? $total_parties : 1)";
$fin = "ceil(($nombre_boucle * $debut )/$totpos) - 1";
$debut = !$pmoins1 ? 0 : "ceil(($nombre_boucle * $pmoins1)/$totpos);";
} else {
// cas {n-1,x}
- if ($op1=='-') $debut = "$nombre_boucle - $debut;";
+ if ($op1 == '-') {
+ $debut = "$nombre_boucle - $debut;";
+ }
// cas {x,n-1}
- if ($op2=='-'){
- $fin = '$debut_boucle + '.$nombre_boucle.' - '
- .(is_numeric($total_parties) ? ($total_parties+1) :
- ($total_parties.' - 1'));
+ if ($op2 == '-') {
+ $fin = '$debut_boucle + ' . $nombre_boucle . ' - '
+ . (is_numeric($total_parties) ? ($total_parties + 1) :
+ ($total_parties . ' - 1'));
} else {
// {x,1} ou {pagination}
$fin = '$debut_boucle'
- .(is_numeric($total_parties) ?
- (($total_parties==1) ? "" : (' + '.($total_parties-1))) :
- ('+'.$total_parties.' - 1'));
+ . (is_numeric($total_parties) ?
+ (($total_parties == 1) ? "" : (' + ' . ($total_parties - 1))) :
+ ('+' . $total_parties . ' - 1'));
}
// {pagination}, gerer le debut_xx=-1 pour tout voir
- if ($op1=='p'){
+ if ($op1 == 'p') {
$debut .= ";\n \$debut_boucle = ((\$tout=(\$debut_boucle == -1))?0:(\$debut_boucle))";
$debut .= ";\n \$debut_boucle = max(0,min(\$debut_boucle,floor(($nombre_boucle-1)/($total_parties))*($total_parties)))";
$fin = "(\$tout ? $nombre_boucle : $fin)";
// Utiliser min pour rabattre $fin_boucle sur total_boucle.
$boucles[$id_boucle]->mode_partie = "\n\t"
- .'$debut_boucle = '.$debut.";\n "
- .'$fin_boucle = min('.$fin.", \$Numrows['$id_boucle']['total'] - 1);\n "
- .'$Numrows[\''.$id_boucle."']['grand_total'] = \$Numrows['$id_boucle']['total'];\n "
- .'$Numrows[\''.$id_boucle.'\']["total"] = max(0,$fin_boucle - $debut_boucle + 1);'
- ."\n\tif (\$debut_boucle>0 AND \$debut_boucle < \$Numrows['$id_boucle']['grand_total'] AND \$iter->seek(\$debut_boucle,'continue'))\n\t\t\$Numrows['$id_boucle']['compteur_boucle'] = \$debut_boucle;\n\t";
+ . '$debut_boucle = ' . $debut . ";\n "
+ . "\$debut_boucle = intval(\$debut_boucle);\n "
+ . '$fin_boucle = min(' . $fin . ", \$Numrows['$id_boucle']['total'] - 1);\n "
+ . '$Numrows[\'' . $id_boucle . "']['grand_total'] = \$Numrows['$id_boucle']['total'];\n "
+ . '$Numrows[\'' . $id_boucle . '\']["total"] = max(0,$fin_boucle - $debut_boucle + 1);'
+ . "\n\tif (\$debut_boucle>0"
+ . " AND \$debut_boucle < \$Numrows['$id_boucle']['grand_total']"
+ . " AND \$iter->seek(\$debut_boucle,'continue'))"
+ . "\n\t\t\$Numrows['$id_boucle']['compteur_boucle'] = \$debut_boucle;\n\t";
$boucles[$id_boucle]->partie = "
if (\$Numrows['$id_boucle']['compteur_boucle'] <= \$debut_boucle) continue;
if (\$Numrows['$id_boucle']['compteur_boucle']-1 > \$fin_boucle) break;";
}
-// http://doc.spip.org/@calculer_critere_parties_aux
-function calculer_critere_parties_aux($idb, &$boucles, $param){
- if ($param[0]->type!='texte'){
+/**
+ * Analyse un des éléments des critères {a,b} ou {a/b}
+ *
+ * Pour l'élément demandé (a ou b) retrouve la valeur de l'élément,
+ * et de combien il est soustrait si c'est le cas comme dans {a-3,b}
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param array $param Paramètre à analyser (soit a, soit b dans {a,b} ou {a/b})
+ * @return array Valeur de l'élément (peut être une expression PHP), Nombre soustrait
+ **/
+function calculer_critere_parties_aux($idb, &$boucles, $param) {
+ if ($param[0]->type != 'texte') {
$a1 = calculer_liste(array($param[0]), array('id_mere' => $idb), $boucles, $boucles[$idb]->id_parent);
- preg_match(',^ *(-([0-9]+))? *$,', $param[1]->texte, $m);
- return array("intval($a1)", ($m[2] ? $m[2] : 0));
+ if (isset($param[1]->texte)) {
+ preg_match(',^ *(-([0-9]+))? *$,', $param[1]->texte, $m);
+
+ return array("intval($a1)", ((isset($m[2]) and $m[2]) ? $m[2] : 0));
+ } else {
+ return array("intval($a1)", 0);
+ }
} else {
preg_match(',^ *(([0-9]+)|n) *(- *([0-9]+)? *)?$,', $param[0]->texte, $m);
$a1 = $m[1];
- if (!@$m[3])
+ if (empty($m[3])) {
return array($a1, 0);
- elseif ($m[4])
+ } elseif (!empty($m[4])) {
return array($a1, $m[4]);
- else return array($a1,
- calculer_liste(array($param[1]), array(), $boucles[$idb]->id_parent, $boucles));
+ } else {
+ return array($a1, calculer_liste(array($param[1]), array(), $boucles, $boucles[$idb]->id_parent));
+ }
}
}
/**
* Compile les critères d'une boucle
- *
+ *
* Cette fonction d'aiguillage cherche des fonctions spécifiques déclarées
* pour chaque critère demandé, dans l'ordre ci-dessous :
- *
+ *
* - critere_{serveur}_{table}_{critere}, sinon avec _dist
* - critere_{serveur}_{critere}, sinon avec _dist
* - critere_{table}_{critere}, sinon avec _dist
* - critere_{critere}, sinon avec _dist
- * - critere_defaut, sinon avec _dist
+ * - calculer_critere_defaut, sinon avec _dist
*
* Émet une erreur de squelette si un critère retourne une erreur.
- *
+ *
* @param string $idb
* Identifiant de la boucle
* @param array $boucles
* @return string|array
* string : Chaine vide sans erreur
* array : Erreur sur un des critères
-**/
-function calculer_criteres($idb, &$boucles){
+ **/
+function calculer_criteres($idb, &$boucles) {
$msg = '';
$boucle = $boucles[$idb];
$table = strtoupper($boucle->type_requete);
$defaut = charger_fonction('DEFAUT', 'calculer_critere');
// s'il y avait une erreur de syntaxe, propager cette info
- if (!is_array($boucle->criteres)) return array();
+ if (!is_array($boucle->criteres)) {
+ return array();
+ }
- foreach ($boucle->criteres as $crit){
+ foreach ($boucle->criteres as $crit) {
$critere = $crit->op;
// critere personnalise ?
if (
- (!$serveur OR
- ((!function_exists($f = "critere_".$serveur."_".$table."_".$critere))
- AND (!function_exists($f = $f."_dist"))
- AND (!function_exists($f = "critere_".$serveur."_".$critere))
- AND (!function_exists($f = $f."_dist"))
- )
+ (!$serveur or
+ ((!function_exists($f = "critere_" . $serveur . "_" . $table . "_" . $critere))
+ and (!function_exists($f = $f . "_dist"))
+ and (!function_exists($f = "critere_" . $serveur . "_" . $critere))
+ and (!function_exists($f = $f . "_dist"))
+ )
)
- AND (!function_exists($f = "critere_".$table."_".$critere))
- AND (!function_exists($f = $f."_dist"))
- AND (!function_exists($f = "critere_".$critere))
- AND (!function_exists($f = $f."_dist"))
- ){
- // fonction critere standard
+ and (!function_exists($f = "critere_" . $table . "_" . $critere))
+ and (!function_exists($f = $f . "_dist"))
+ and (!function_exists($f = "critere_" . $critere))
+ and (!function_exists($f = $f . "_dist"))
+ ) {
+ // fonction critere standard
$f = $defaut;
}
// compile le critere
$res = $f($idb, $boucles, $crit);
// Gestion centralisee des erreurs pour pouvoir propager
- if (is_array($res)){
+ if (is_array($res)) {
$msg = $res;
erreur_squelette($msg, $boucle);
}
}
+
return $msg;
}
/**
+ * Désemberlificote les guillements et échappe (ou fera échapper) le contenu...
+ *
* Madeleine de Proust, revision MIT-1958 sqq, revision CERN-1989
* hum, c'est kwoi cette fonxion ? on va dire qu'elle desemberlificote les guillemets...
*
- * http://doc.spip.org/@kwote
+ * http://code.spip.net/@kwote
*
- * @param string $lisp
- * @param string $serveur
- * @param string $type
- * @return string
+ * @param string $lisp Code compilé
+ * @param string $serveur Connecteur de bdd utilisé
+ * @param string $type Type d'échappement (char, int...)
+ * @return string Code compilé rééchappé
*/
-function kwote($lisp, $serveur='', $type=''){
- if (preg_match(_CODE_QUOTE, $lisp, $r))
- return $r[1]."\"".sql_quote(str_replace(array("\\'", "\\\\"), array("'", "\\"), $r[2]),$serveur,$type)."\"";
- else
- return "sql_quote($lisp)";
+function kwote($lisp, $serveur = '', $type = '') {
+ if (preg_match(_CODE_QUOTE, $lisp, $r)) {
+ return $r[1] . "\"" . sql_quote(str_replace(array("\\'", "\\\\"), array("'", "\\"), $r[2]), $serveur, $type) . "\"";
+ } else {
+ return "sql_quote($lisp, '$serveur', '" . str_replace("'", "\\'", $type) . "')";
+ }
}
-// Si on a une liste de valeurs dans #ENV{x}, utiliser la double etoile
-// pour faire par exemple {id_article IN #ENV**{liste_articles}}
-// http://doc.spip.org/@critere_IN_dist
-function critere_IN_dist($idb, &$boucles, $crit){
+
+/**
+ * Compile un critère possédant l'opérateur IN : {xx IN yy}
+ *
+ * Permet de restreindre un champ sur une liste de valeurs tel que
+ * {id_article IN 3,4} {id_article IN #LISTE{3,4}}
+ *
+ * Si on a une liste de valeurs dans #ENV{x}, utiliser la double etoile
+ * pour faire par exemple {id_article IN #ENV**{liste_articles}}
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function critere_IN_dist($idb, &$boucles, $crit) {
$r = calculer_critere_infixe($idb, $boucles, $crit);
- if (!$r){
- return (array('zbug_critere_inconnu', array('critere' => $crit->op." ?")));
+ if (!$r) {
+ return (array('zbug_critere_inconnu', array('critere' => $crit->op . " ?")));
}
list($arg, $op, $val, $col, $where_complement) = $r;
// inserer la condition; exemple: {id_mot ?IN (66, 62, 64)}
$where = $in;
- if ($crit->cond){
+ if ($crit->cond) {
$pred = calculer_argument_precedent($idb, $col, $boucles);
$where = array("'?'", $pred, $where, "''");
if ($where_complement) // condition annexe du type "AND (objet='article')"
+ {
$where_complement = array("'?'", $pred, $where_complement, "''");
+ }
}
- if ($crit->exclus)
- if (!preg_match(",^L[0-9]+[.],", $arg))
+ if ($crit->exclus) {
+ if (!preg_match(",^L[0-9]+[.],", $arg)) {
$where = array("'NOT'", $where);
- else
+ } else
// un not sur un critere de jointure se traduit comme un NOT IN avec une sous requete
// c'est une sous requete identique a la requete principale sous la forme (SELF,$select,$where) avec $select et $where qui surchargent
- $where = array("'NOT'", array("'IN'", "'".$boucles[$idb]->id_table.".".$boucles[$idb]->primary."'", array("'SELF'", "'".$boucles[$idb]->id_table.".".$boucles[$idb]->primary."'", $where)));
+ {
+ $where = array(
+ "'NOT'",
+ array(
+ "'IN'",
+ "'" . $boucles[$idb]->id_table . "." . $boucles[$idb]->primary . "'",
+ array("'SELF'", "'" . $boucles[$idb]->id_table . "." . $boucles[$idb]->primary . "'", $where)
+ )
+ );
+ }
+ }
$boucles[$idb]->where[] = $where;
if ($where_complement) // condition annexe du type "AND (objet='article')"
+ {
$boucles[$idb]->where[] = $where_complement;
+ }
}
-// http://doc.spip.org/@critere_IN_cas
-function critere_IN_cas($idb, &$boucles, $crit2, $arg, $op, $val, $col){
+// http://code.spip.net/@critere_IN_cas
+function critere_IN_cas($idb, &$boucles, $crit2, $arg, $op, $val, $col) {
static $num = array();
$descr = $boucles[$idb]->descr;
$cpt = &$num[$descr['nom']][$descr['gram']][$idb];
- $var = '$in'.$cpt++;
+ $var = '$in' . $cpt++;
$x = "\n\t$var = array();";
- foreach ($val as $k => $v){
- if (preg_match(",^(\n//.*\n)?'(.*)'$,", $v, $r)){
+ foreach ($val as $k => $v) {
+ if (preg_match(",^(\n//.*\n)?'(.*)'$,", $v, $r)) {
// optimiser le traitement des constantes
- if (is_numeric($r[2]))
- $x .= "\n\t$var"."[]= $r[2];";
- else
- $x .= "\n\t$var"."[]= ".sql_quote($r[2]).";";
+ if (is_numeric($r[2])) {
+ $x .= "\n\t$var" . "[]= $r[2];";
+ } else {
+ $x .= "\n\t$var" . "[]= " . sql_quote($r[2]) . ";";
+ }
} else {
// Pour permettre de passer des tableaux de valeurs
// on repere l'utilisation brute de #ENV**{X},
// c'est-a-dire sa traduction en ($PILE[0][X]).
// et on deballe mais en rajoutant l'anti XSS
- $x .= "\n\tif (!(is_array(\$a = ($v))))\n\t\t$var"."[]= \$a;\n\telse $var = array_merge($var, \$a);";
+ $x .= "\n\tif (!(is_array(\$a = ($v))))\n\t\t$var" . "[]= \$a;\n\telse $var = array_merge($var, \$a);";
}
}
$boucles[$idb]->in .= $x;
- // inserer le tri par defaut selon les ordres du IN ...
- // avec une ecriture de type FIELD qui degrade les performances (du meme ordre qu'un rexgexp)
+ // inserer le tri par defaut selon les ordres du IN ...
+ // avec une ecriture de type FIELD qui degrade les performances (du meme ordre qu'un regexp)
// et que l'on limite donc strictement aux cas necessaires :
// si ce n'est pas un !IN, et si il n'y a pas d'autre order dans la boucle
- if (!$crit2){
+ if (!$crit2) {
$boucles[$idb]->default_order[] = "((!sql_quote($var) OR sql_quote($var)===\"''\") ? 0 : ('FIELD($arg,' . sql_quote($var) . ')'))";
}
- return "sql_in('$arg',sql_quote($var)".($crit2=='NOT' ? ",'NOT'" : "").")";
+ return "sql_in('$arg',sql_quote($var)" . ($crit2 == 'NOT' ? ",'NOT'" : "") . ")";
}
/**
- * {where}
- * tout simplement, pour faire le pont entre php et squelettes
+ * Compile le critère {where}
*
- * @param <type> $idb
- * @param <type> $boucles
- * @param <type> $crit
+ * Ajoute une contrainte sql WHERE, tout simplement pour faire le pont
+ * entre php et squelettes, en utilisant la syntaxe attendue par
+ * la propriété $where d'une Boucle.
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
*/
-function critere_where_dist($idb, &$boucles, $crit){
+function critere_where_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
- if (isset($crit->param[0]))
+ if (isset($crit->param[0])) {
$_where = calculer_liste($crit->param[0], array(), $boucles, $boucle->id_parent);
- else
+ } else {
$_where = '@$Pile[0]["where"]';
+ }
- if ($crit->cond)
+ if ($crit->cond) {
$_where = "(($_where) ? ($_where) : '')";
+ }
- if ($crit->not)
+ if ($crit->not) {
$_where = "array('NOT',$_where)";
+ }
$boucle->where[] = $_where;
}
/**
- * Un critere pour gerer un champ de tri qui peut etre modifie dynamiquement
- * par la balise #TRI
- *
- * {tri [champ_par_defaut][,sens_par_defaut][,nom_variable]}
- * champ_par_defaut : un champ de la table sql
- * sens_par_defaut : -1 ou inverse pour decroissant, 1 ou direct pour croissant
- * peut etre un tableau pour preciser des sens par defaut associes a chaque champ
- * exemple : array('titre'=>1,'date'=>-1) pour trier par defaut
- * les titre croissant et les dates decroissantes
- * dans ce cas, quand un champ est utilise pour le tri et n'est pas present dans le tableau
- * c'est la premiere valeur qui est utilisee
- * nom_variable : nom de la variable utilisee (par defaut tri_nomboucle)
- *
- * {tri titre}
- * {tri titre,inverse}
- * {tri titre,-1}
- * {tri titre,-1,truc}
- *
- * le critere {tri} s'utilise conjointement avec la balise #TRI dans la meme boucle
- * pour generer les liens qui permettent de changer le critere de tri et le sens du tri
- *
- * Exemple d'utilisation
- *
- * <B_articles>
- * <p>#TRI{titre,'Trier par titre'} | #TRI{date,'Trier par date'}</p>
- * <ul>
- * <BOUCLE_articles(ARTICLES){tri titre}>
- * <li>#TITRE - [(#DATE|affdate_jourcourt)]</li>
- * </BOUCLE_articles>
- * </ul>
- * </B_articles>
- *
- * NB :
- * contraitement a {par ...} {tri} ne peut prendre qu'un seul champ,
- * mais il peut etre complete avec {par ...} pour indiquer des criteres secondaires
- *
- * ex :
- * {tri num titre}{par titre} permet de faire un tri sur le rang (modifiable dynamiquement)
- * avec un second critere sur le titre en cas d'egalite des rang
- *
- * @param unknown_type $idb
- * @param unknown_type $boucles
- * @param unknown_type $crit
+ * Compile le critère `{tri}` permettant le tri dynamique d'un champ
+ *
+ * Le critère `{tri}` gère un champ de tri qui peut être modifié dynamiquement par la balise `#TRI`.
+ * Il s'utilise donc conjointement avec la balise `#TRI` dans la même boucle
+ * pour génerér les liens qui permettent de changer le critère de tri et le sens du tri
+ *
+ * @syntaxe `{tri [champ_par_defaut][,sens_par_defaut][,nom_variable]}`
+ *
+ * - champ_par_defaut : un champ de la table sql
+ * - sens_par_defaut : -1 ou inverse pour décroissant, 1 ou direct pour croissant
+ * peut être un tableau pour préciser des sens par défaut associés à chaque champ
+ * exemple : `array('titre' => 1, 'date' => -1)` pour trier par défaut
+ * les titre croissants et les dates décroissantes
+ * dans ce cas, quand un champ est utilisé pour le tri et n'est pas présent dans le tableau
+ * c'est la première valeur qui est utilisée
+ * - nom_variable : nom de la variable utilisée (par defaut `tri_{nomboucle}`)
+ *
+ * {tri titre}
+ * {tri titre,inverse}
+ * {tri titre,-1}
+ * {tri titre,-1,truc}
+ *
+ * Exemple d'utilisation :
+ *
+ * <B_articles>
+ * <p>#TRI{titre,'Trier par titre'} | #TRI{date,'Trier par date'}</p>
+ * <ul>
+ * <BOUCLE_articles(ARTICLES){tri titre}>
+ * <li>#TITRE - [(#DATE|affdate_jourcourt)]</li>
+ * </BOUCLE_articles>
+ * </ul>
+ * </B_articles>
+ *
+ * @note
+ * Contraitement à `{par ...}`, `{tri}` ne peut prendre qu'un seul champ,
+ * mais il peut être complété avec `{par ...}` pour indiquer des criteres secondaires
+ *
+ * Exemble :
+ * `{tri num titre}{par titre}` permet de faire un tri sur le rang (modifiable dynamiquement)
+ * avec un second critère sur le titre en cas d'égalité des rangs
+ *
+ * @link http://www.spip.net/5429
+ * @see critere_par_dist() Le critère `{par ...}`
+ * @see balise_TRI_dist() La balise `#TRI`
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
*/
-function critere_tri_dist($idb, &$boucles, $crit){
+function critere_tri_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
// definition du champ par defaut
$_variable = !isset($crit->param[2][0]) ? "'$idb'"
: calculer_liste(array($crit->param[2][0]), array(), $boucles, $boucle->id_parent);
- $_tri = "((\$t=(isset(\$Pile[0]['tri'.$_variable]))?\$Pile[0]['tri'.$_variable]:$_champ_defaut)?tri_protege_champ(\$t):'')";
+ $_tri = "((\$t=(isset(\$Pile[0]['tri'.$_variable]))?\$Pile[0]['tri'.$_variable]:((strncmp($_variable,'session',7)==0 AND session_get('tri'.$_variable))?session_get('tri'.$_variable):$_champ_defaut))?tri_protege_champ(\$t):'')";
$_sens_defaut = "(is_array(\$s=$_sens_defaut)?(isset(\$s[\$st=$_tri])?\$s[\$st]:reset(\$s)):\$s)";
- $_sens = "((intval(\$t=(isset(\$Pile[0]['sens'.$_variable]))?\$Pile[0]['sens'.$_variable]:$_sens_defaut)==-1 OR \$t=='inverse')?-1:1)";
+ $_sens = "((intval(\$t=(isset(\$Pile[0]['sens'.$_variable]))?\$Pile[0]['sens'.$_variable]:((strncmp($_variable,'session',7)==0 AND session_get('sens'.$_variable))?session_get('sens'.$_variable):$_sens_defaut))==-1 OR \$t=='inverse')?-1:1)";
$boucle->modificateur['tri_champ'] = $_tri;
$boucle->modificateur['tri_sens'] = $_sens;
# Criteres de comparaison
-// http://doc.spip.org/@calculer_critere_DEFAUT
-function calculer_critere_DEFAUT_dist($idb, &$boucles, $crit){
+/**
+ * Compile un critère non déclaré explicitement
+ *
+ * Compile les critères non déclarés, ainsi que les parties de boucles
+ * avec les critères {0,1} ou {1/2}
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return void
+ **/
+function calculer_critere_DEFAUT_dist($idb, &$boucles, $crit) {
// double cas particulier {0,1} et {1/2} repere a l'analyse lexicale
- if (($crit->op==",") OR ($crit->op=='/'))
+ if (($crit->op == ",") or ($crit->op == '/')) {
return calculer_critere_parties($idb, $boucles, $crit);
+ }
$r = calculer_critere_infixe($idb, $boucles, $crit);
- if (!$r){
- # // on produit une erreur seulement si le critere n'a pas de '?'
- # if (!$crit->cond) {
- return (array('zbug_critere_inconnu', array('critere' => $crit->op)));
- # }
- } else calculer_critere_DEFAUT_args($idb, $boucles, $crit, $r);
+ if (!$r) {
+ # // on produit une erreur seulement si le critere n'a pas de '?'
+ # if (!$crit->cond) {
+ return (array('zbug_critere_inconnu', array('critere' => $crit->op)));
+ # }
+ } else {
+ calculer_critere_DEFAUT_args($idb, $boucles, $crit, $r);
+ }
}
-function calculer_critere_DEFAUT_args($idb, &$boucles, $crit, $args){
+
+/**
+ * Compile un critère non déclaré explicitement, dont on reçoit une analyse
+ *
+ * Ajoute en fonction des arguments trouvés par calculer_critere_infixe()
+ * les conditions WHERE à appliquer sur la boucle.
+ *
+ * @see calculer_critere_infixe()
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @param array $args Description du critère
+ * Cf. retour de calculer_critere_infixe()
+ * @return void
+ **/
+function calculer_critere_DEFAUT_args($idb, &$boucles, $crit, $args) {
list($arg, $op, $val, $col, $where_complement) = $args;
$where = array("'$op'", "'$arg'", $val[0]);
// inserer la negation (cf !...)
- if ($crit->not) $where = array("'NOT'", $where);
- if ($crit->exclus)
- if (!preg_match(",^L[0-9]+[.],", $arg))
+ if ($crit->not) {
+ $where = array("'NOT'", $where);
+ }
+ if ($crit->exclus) {
+ if (!preg_match(",^L[0-9]+[.],", $arg)) {
$where = array("'NOT'", $where);
- else
+ } else
// un not sur un critere de jointure se traduit comme un NOT IN avec une sous requete
// c'est une sous requete identique a la requete principale sous la forme (SELF,$select,$where) avec $select et $where qui surchargent
- $where = array("'NOT'", array("'IN'", "'".$boucles[$idb]->id_table.".".$boucles[$idb]->primary."'", array("'SELF'", "'".$boucles[$idb]->id_table.".".$boucles[$idb]->primary."'", $where)));
+ {
+ $where = array(
+ "'NOT'",
+ array(
+ "'IN'",
+ "'" . $boucles[$idb]->id_table . "." . $boucles[$idb]->primary . "'",
+ array("'SELF'", "'" . $boucles[$idb]->id_table . "." . $boucles[$idb]->primary . "'", $where)
+ )
+ );
+ }
+ }
// inserer la condition (cf {lang?})
// traiter a part la date, elle est mise d'office par SPIP,
- if ($crit->cond){
+ if ($crit->cond) {
$pred = calculer_argument_precedent($idb, $col, $boucles);
- if ($col=="date" OR $col=="date_redac"){
- if ($pred=="\$Pile[0]['".$col."']"){
+ if ($col == "date" or $col == "date_redac") {
+ if ($pred == "\$Pile[0]['" . $col . "']") {
$pred = "(\$Pile[0]['{$col}_default']?'':$pred)";
}
}
- if ($op=='=' AND !$crit->not)
- $where = array("'?'", "(is_array($pred))",
- critere_IN_cas($idb, $boucles, 'COND', $arg, $op, array($pred), $col),
- $where);
+ if ($op == '=' and !$crit->not) {
+ $where = array(
+ "'?'",
+ "(is_array($pred))",
+ critere_IN_cas($idb, $boucles, 'COND', $arg, $op, array($pred), $col),
+ $where
+ );
+ }
$where = array("'?'", "!(is_array($pred)?count($pred):strlen($pred))", "''", $where);
if ($where_complement) // condition annexe du type "AND (objet='article')"
+ {
$where_complement = array("'?'", "!(is_array($pred)?count($pred):strlen($pred))", "''", $where_complement);
+ }
}
$boucles[$idb]->where[] = $where;
if ($where_complement) // condition annexe du type "AND (objet='article')"
+ {
$boucles[$idb]->where[] = $where_complement;
+ }
}
-// http://doc.spip.org/@calculer_critere_infixe
-function calculer_critere_infixe($idb, &$boucles, $crit){
- global $table_criteres_infixes;
- global $exceptions_des_jointures, $exceptions_des_tables;
+/**
+ * Décrit un critère non déclaré explicitement
+ *
+ * Décrit un critère non déclaré comme {id_article} {id_article>3} en
+ * retournant un tableau de l'analyse si la colonne (ou l'alias) existe vraiment.
+ *
+ * Ajoute au passage pour chaque colonne utilisée (alias et colonne véritable)
+ * un modificateur['criteres'][colonne].
+ *
+ * S'occupe de rechercher des exceptions, tel que
+ * - les id_parent, id_enfant, id_secteur,
+ * - des colonnes avec des exceptions déclarées,
+ * - des critères de date (jour_relatif, ...),
+ * - des critères sur tables jointes explicites (mots.titre),
+ * - des critères sur tables de jointure non explicite (id_mot sur une boucle articles...)
+ *
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return array|string
+ * Liste si on trouve le champ :
+ * - string $arg
+ * Opérande avant l'opérateur : souvent la colonne d'application du critère, parfois un calcul
+ * plus complexe dans le cas des dates.
+ * - string $op
+ * L'opérateur utilisé, tel que '='
+ * - string[] $val
+ * Liste de codes PHP obtenant les valeurs des comparaisons (ex: id_article sur la boucle parente)
+ * Souvent (toujours ?) un tableau d'un seul élément.
+ * - $col_alias
+ * - $where_complement
+ *
+ * Chaîne vide si on ne trouve pas le champ...
+ **/
+function calculer_critere_infixe($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$type = $boucle->type_requete;
$where_complement = false;
// Cas particulier : id_enfant => utiliser la colonne id_objet
- if ($col=='id_enfant')
+ if ($col == 'id_enfant') {
$col = $boucle->primary;
+ }
// Cas particulier : id_parent => verifier les exceptions de tables
- if (in_array($col,array('id_parent','id_secteur'))
- AND isset($exceptions_des_tables[$table][$col]))
- $col = $exceptions_des_tables[$table][$col];
-
- // et possibilite de gerer un critere secteur sur des tables de plugins (ie forums)
- else if (($col=='id_secteur') AND ($critere_secteur = charger_fonction("critere_secteur_$type", "public", true))){
- $table = $critere_secteur($idb, $boucles, $val, $crit);
- }
+ if ((in_array($col, array('id_parent', 'id_secteur')) and isset($GLOBALS['exceptions_des_tables'][$table][$col]))
+ or (isset($GLOBALS['exceptions_des_tables'][$table][$col]) and is_string($GLOBALS['exceptions_des_tables'][$table][$col]))
+ ) {
+ $col = $GLOBALS['exceptions_des_tables'][$table][$col];
+ } // et possibilite de gerer un critere secteur sur des tables de plugins (ie forums)
+ else {
+ if (($col == 'id_secteur') and ($critere_secteur = charger_fonction("critere_secteur_$type", "public", true))) {
+ $table = $critere_secteur($idb, $boucles, $val, $crit);
+ }
// cas id_article=xx qui se mappe en id_objet=xx AND objet=article
// sauf si exception declaree : sauter cette etape
- else if (
- !isset($exceptions_des_jointures[table_objet_sql($table)][$col])
- AND !isset($exceptions_des_jointures[$col])
- AND count(trouver_champs_decomposes($col, $desc))>1
- ){
- $e = decompose_champ_id_objet($col);
- $col = array_shift($e);
- $where_complement = primary_doublee($e, $table);
- }
- // Cas particulier : expressions de date
- else if ($c = calculer_critere_infixe_date($idb, $boucles, $col)){
- list($col,$col_vraie) = $c;
- $table = '';
- }
- else if (preg_match('/^(.*)\.(.*)$/', $col, $r)){
- list(, $table, $col) = $r;
- $col_alias = $col;
-
- $trouver_table = charger_fonction('trouver_table','base');
- if ($desc = $trouver_table($table, $boucle->sql_serveur)
- AND isset($desc['field'][$col])
- AND $cle = array_search($desc['table'],$boucle->from))
- $table = $cle;
else {
- $table = trouver_jointure_champ($col, $boucle, array($table), ($crit->cond OR $op!='='));
+ if (
+ !isset($GLOBALS['exceptions_des_jointures'][table_objet_sql($table)][$col])
+ and !isset($GLOBALS['exceptions_des_jointures'][$col])
+ and count(trouver_champs_decomposes($col, $desc)) > 1
+ ) {
+ $e = decompose_champ_id_objet($col);
+ $col = array_shift($e);
+ $where_complement = primary_doublee($e, $table);
+ } // Cas particulier : expressions de date
+ else {
+ if ($c = calculer_critere_infixe_date($idb, $boucles, $col)) {
+ list($col, $col_vraie) = $c;
+ $table = '';
+ } // table explicitée {mots.titre}
+ else {
+ if (preg_match('/^(.*)\.(.*)$/', $col, $r)) {
+ list(, $table, $col) = $r;
+ $col_alias = $col;
+
+ $trouver_table = charger_fonction('trouver_table', 'base');
+ if ($desc = $trouver_table($table, $boucle->sql_serveur)
+ and isset($desc['field'][$col])
+ and $cle = array_search($desc['table'], $boucle->from)
+ ) {
+ $table = $cle;
+ } else {
+ $table = trouver_jointure_champ($col, $boucle, array($table), ($crit->cond or $op != '='));
+ }
+ #$table = calculer_critere_externe_init($boucle, array($table), $col, $desc, ($crit->cond OR $op!='='), true);
+ if (!$table) {
+ return '';
+ }
+ }
+ // si le champ n'est pas trouvé dans la table,
+ // on cherche si une jointure peut l'obtenir
+ elseif (@!array_key_exists($col, $desc['field'])) {
+ // Champ joker * des iterateurs DATA qui accepte tout
+ if (@array_key_exists('*', $desc['field'])) {
+ $desc['field'][$col_vraie ? $col_vraie : $col] = ''; // on veut pas de cast INT par defaut car le type peut etre n'importe quoi dans les boucles DATA
+ }
+ else {
+ $r = calculer_critere_infixe_externe($boucle, $crit, $op, $desc, $col, $col_alias, $table);
+ if (!$r) {
+ return '';
+ }
+ list($col, $col_alias, $table, $where_complement, $desc) = $r;
+ }
+ }
+ }
+ }
}
- #$table = calculer_critere_externe_init($boucle, array($table), $col, $desc, ($crit->cond OR $op!='='), true);
- if (!$table) return '';
- }
- elseif (@!array_key_exists($col, $desc['field'])
- // Champ joker * des iterateurs DATA qui accepte tout
- AND @!array_key_exists('*', $desc['field'])
- ) {
- $r = calculer_critere_infixe_externe($boucle, $crit, $op, $desc, $col, $col_alias, $table);
- if (!$r) return '';
- list($col, $col_alias, $table, $where_complement, $desc) = $r;
}
- $col_vraie = ($col_vraie?$col_vraie:$col);
+ $col_vraie = ($col_vraie ? $col_vraie : $col);
// Dans tous les cas,
- // virer les guillemets eventuels autour d'un int (qui sont refuses par certains SQL) et passer dans sql_quote avec le type si connu
+ // virer les guillemets eventuels autour d'un int (qui sont refuses par certains SQL)
+ // et passer dans sql_quote avec le type si connu
// et int sinon si la valeur est numerique
// sinon introduire le vrai type du champ si connu dans le sql_quote (ou int NOT NULL sinon)
// Ne pas utiliser intval, PHP tronquant les Bigint de SQL
- if ($op=='=' OR in_array($op, $table_criteres_infixes)){
+ if ($op == '=' or in_array($op, $GLOBALS['table_criteres_infixes'])) {
// defaire le quote des int et les passer dans sql_quote avec le bon type de champ si on le connait, int sinon
// prendre en compte le debug ou la valeur arrive avec un commentaire PHP en debut
- if (preg_match(",^\\A(\s*//.*?$\s*)?\"'(-?\d+)'\"\\z,ms", $val[0], $r))
- $val[0] = $r[1].'"'.sql_quote($r[2],$boucle->sql_serveur,(isset($desc['field'][$col_vraie])?$desc['field'][$col_vraie]:'int NOT NULL')).'"';
+ if (preg_match(",^\\A(\s*//.*?$\s*)?\"'(-?\d+)'\"\\z,ms", $val[0], $r)) {
+ $val[0] = $r[1] . '"' . sql_quote($r[2], $boucle->sql_serveur,
+ (isset($desc['field'][$col_vraie]) ? $desc['field'][$col_vraie] : 'int NOT NULL')) . '"';
+ }
// sinon expliciter les
// sql_quote(truc) en sql_quote(truc,'',type)
// sql_quote(truc,serveur) en sql_quote(truc,serveur,type)
+ // sql_quote(truc,serveur,'') en sql_quote(truc,serveur,type)
// sans toucher aux
// sql_quote(truc,'','varchar(10) DEFAULT \'oui\' COLLATE NOCASE')
// sql_quote(truc,'','varchar')
elseif (preg_match('/\Asql_quote[(](.*?)(,[^)]*?)?(,[^)]*(?:\(\d+\)[^)]*)?)?[)]\s*\z/ms', $val[0], $r)
// si pas deja un type
- AND (!isset($r[3]) OR !$r[3])) {
+ and (!isset($r[3]) or !$r[3] or !trim($r[3],", '"))
+ ) {
$r = $r[1]
- .((isset($r[2]) AND $r[2]) ? $r[2] : ",''")
- .",'".(isset($desc['field'][$col_vraie])?addslashes($desc['field'][$col_vraie]):'int NOT NULL')."'";
+ . ((isset($r[2]) and $r[2]) ? $r[2] : ",''")
+ . ",'" . (isset($desc['field'][$col_vraie]) ? addslashes($desc['field'][$col_vraie]) : 'int NOT NULL') . "'";
$val[0] = "sql_quote($r)";
}
}
- // Indicateur pour permettre aux fonctionx boucle_X de modifier
+ // Indicateur pour permettre aux fonctionx boucle_X de modifier
// leurs requetes par defaut, notamment le champ statut
// Ne pas confondre champs de la table principale et des jointures
- if ($table===$boucle->id_table){
+ if ($table === $boucle->id_table) {
$boucles[$idb]->modificateur['criteres'][$col_vraie] = true;
- if ($col_alias!=$col_vraie)
+ if ($col_alias != $col_vraie) {
$boucles[$idb]->modificateur['criteres'][$col_alias] = true;
+ }
}
// ajout pour le cas special d'une condition sur le champ statut:
// de mettre ses propres criteres de statut
// http://www.spip.net/@statut (a documenter)
// garde pour compatibilite avec code des plugins anterieurs, mais redondant avec la ligne precedente
- if ($col=='statut') $boucles[$idb]->statut = true;
+ if ($col == 'statut') {
+ $boucles[$idb]->statut = true;
+ }
// inserer le nom de la table SQL devant le nom du champ
- if ($table){
- if ($col[0]=="`")
- $arg = "$table.".substr($col, 1, -1);
- else $arg = "$table.$col";
- } else $arg = $col;
+ if ($table) {
+ if ($col[0] == "`") {
+ $arg = "$table." . substr($col, 1, -1);
+ } else {
+ $arg = "$table.$col";
+ }
+ } else {
+ $arg = $col;
+ }
// inserer la fonction SQL
- if ($fct) $arg = "$fct($arg$args_sql)";
+ if ($fct) {
+ $arg = "$fct($arg$args_sql)";
+ }
return array($arg, $op, $val, $col_alias, $where_complement);
}
-function calculer_critere_infixe_externe($boucle, $crit, $op, $desc, $col, $col_alias, $table){
- global $exceptions_des_jointures;
+
+/**
+ * Décrit un critère non déclaré explicitement, sur un champ externe à la table
+ *
+ * Décrit un critère non déclaré comme {id_article} {id_article>3} qui correspond
+ * à un champ non présent dans la table, et donc à retrouver par jointure si possible.
+ *
+ * @param Boucle $boucle Description de la boucle
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @param string $op L'opérateur utilisé, tel que '='
+ * @param array $desc Description de la table
+ * @param string $col Nom de la colonne à trouver (la véritable)
+ * @param string $col_alias Alias de la colonne éventuel utilisé dans le critère ex: id_enfant
+ * @param string $table Nom de la table SQL de la boucle
+ * @return array|string
+ * Liste si jointure possible :
+ * - string $col
+ * - string $col_alias
+ * - string $table
+ * - array $where
+ * - array $desc
+ *
+ * Chaîne vide si on ne trouve pas le champ par jointure...
+ **/
+function calculer_critere_infixe_externe($boucle, $crit, $op, $desc, $col, $col_alias, $table) {
+
$where = '';
$calculer_critere_externe = 'calculer_critere_externe_init';
- // gestion par les plugins des jointures tordues
+ // gestion par les plugins des jointures tordues
// pas automatiques mais necessaires
$table_sql = table_objet_sql($table);
- if (isset($exceptions_des_jointures[$table_sql])
- AND is_array($exceptions_des_jointures[$table_sql])
- AND
- (
- isset($exceptions_des_jointures[$table_sql][$col])
- OR
- isset($exceptions_des_jointures[$table_sql][''])
- )
- ){
- $t = $exceptions_des_jointures[$table_sql];
+ if (isset($GLOBALS['exceptions_des_jointures'][$table_sql])
+ and is_array($GLOBALS['exceptions_des_jointures'][$table_sql])
+ and
+ (
+ isset($GLOBALS['exceptions_des_jointures'][$table_sql][$col])
+ or
+ isset($GLOBALS['exceptions_des_jointures'][$table_sql][''])
+ )
+ ) {
+ $t = $GLOBALS['exceptions_des_jointures'][$table_sql];
$index = isset($t[$col])
? $t[$col] : (isset($t['']) ? $t[''] : array());
- if (count($index)==3)
+ if (count($index) == 3) {
list($t, $col, $calculer_critere_externe) = $index;
- elseif (count($index)==2) {
+ } elseif (count($index) == 2) {
list($t, $col) = $t[$col];
- }
- elseif (count($index)==1) {
+ } elseif (count($index) == 1) {
list($calculer_critere_externe) = $index;
$t = $table;
- }
- else
- $t = ''; // jointure non declaree. La trouver.
- }
- elseif (isset($exceptions_des_jointures[$col]))
- list($t, $col) = $exceptions_des_jointures[$col];
- else
- $t = ''; // jointure non declaree. La trouver.
+ } else {
+ $t = '';
+ } // jointure non declaree. La trouver.
+ } elseif (isset($GLOBALS['exceptions_des_jointures'][$col])) {
+ list($t, $col) = $GLOBALS['exceptions_des_jointures'][$col];
+ } else {
+ $t = '';
+ } // jointure non declaree. La trouver.
// ici on construit le from pour fournir $col en piochant dans les jointures
// <BOUCLE_(DOCUMENTS documents_liens){id_mot}>
// alors que <BOUCLE_(DOCUMENTS){id_mot}> produit la meme chose que <BOUCLE_(DOCUMENTS mots_liens){id_mot}>
$table = "";
- if ($boucle->jointures_explicites){
+ if ($boucle->jointures_explicites) {
$jointures_explicites = explode(' ', $boucle->jointures_explicites);
- $table = $calculer_critere_externe($boucle, $jointures_explicites, $col, $desc, ($crit->cond OR $op!='='), $t);
+ $table = $calculer_critere_externe($boucle, $jointures_explicites, $col, $desc, ($crit->cond or $op != '='), $t);
}
// et sinon on cherche parmi toutes les jointures declarees
if (!$table) {
- $table = $calculer_critere_externe($boucle, $boucle->jointures, $col, $desc, ($crit->cond OR $op!='='), $t);
+ $table = $calculer_critere_externe($boucle, $boucle->jointures, $col, $desc, ($crit->cond or $op != '='), $t);
}
- if (!$table) return '';
+ if (!$table) {
+ return '';
+ }
// il ne reste plus qu'a trouver le champ dans les from
- list($nom, $desc) = trouver_champ_exterieur($col, $boucle->from, $boucle);
+ list($nom, $desc, $cle) = trouver_champ_exterieur($col, $boucle->from, $boucle);
- if (count(trouver_champs_decomposes($col, $desc))>1){
+ if (count($cle) > 1 or reset($cle) !== $col) {
$col_alias = $col; // id_article devient juste le nom d'origine
- $e = decompose_champ_id_objet($col);
- $col = array_shift($e);
- $where = primary_doublee($e, $table);
+ if (count($cle) > 1 and reset($cle) == 'id_objet') {
+ $e = decompose_champ_id_objet($col);
+ $col = array_shift($e);
+ $where = primary_doublee($e, $table);
+ } else {
+ $col = reset($cle);
+ }
}
return array($col, $col_alias, $table, $where, $desc);
}
-// Ne pas appliquer sql_quote lors de la compilation,
-// car on ne connait pas le serveur SQL, donc s'il faut \' ou ''
-// http://doc.spip.org/@primary_doublee
-function primary_doublee($decompose, $table){
+/**
+ * Calcule une condition WHERE entre un nom du champ et une valeur
+ *
+ * Ne pas appliquer sql_quote lors de la compilation,
+ * car on ne connait pas le serveur SQL
+ *
+ * @todo Ce nom de fonction n'est pas très clair ?
+ *
+ * @param array $decompose Liste nom du champ, code PHP pour obtenir la valeur
+ * @param string $table Nom de la table
+ * @return string[]
+ * Liste de 3 éléments pour une description where du compilateur :
+ * - operateur (=),
+ * - table.champ,
+ * - valeur
+ **/
+function primary_doublee($decompose, $table) {
$e1 = reset($decompose);
- $e2 = "sql_quote('".end($decompose)."')";
- return array("'='", "'$table.".$e1."'", $e2);
+ $e2 = "sql_quote('" . end($decompose) . "')";
+
+ return array("'='", "'$table." . $e1 . "'", $e2);
}
/**
- * Champ hors table, ca ne peut etre qu'une jointure.
- * On cherche la table du champ et on regarde si elle est deja jointe
+ * Champ hors table, ça ne peut être qu'une jointure.
+ *
+ * On cherche la table du champ et on regarde si elle est déjà jointe
* Si oui et qu'on y cherche un champ nouveau, pas de jointure supplementaire
* Exemple: criteres {titre_mot=...}{type_mot=...}
* Dans les 2 autres cas ==> jointure
* (Exemple: criteres {type_mot=...}{type_mot=...} donne 2 jointures
* pour selectioner ce qui a exactement ces 2 mots-cles.
*
- * http://doc.spip.org/@calculer_critere_externe_init
- *
- * @param $boucle
- * @param $joints
- * @param $col
- * @param $desc
- * @param $cond
+ * @param Boucle $boucle
+ * Description de la boucle
+ * @param array $joints
+ * Liste de jointures possibles (ex: $boucle->jointures ou $boucle->jointures_explicites)
+ * @param string $col
+ * Colonne cible de la jointure
+ * @param array $desc
+ * Description de la table
+ * @param bool $cond
+ * Flag pour savoir si le critère est conditionnel ou non
* @param bool|string $checkarrivee
- * @return mixed|string
+ * string : nom de la table jointe où on veut trouver le champ.
+ * n'a normalement pas d'appel sans $checkarrivee.
+ * @return string
+ * Alias de la table de jointure (Lx)
+ * Vide sinon.
*/
-function calculer_critere_externe_init(&$boucle, $joints, $col, $desc, $cond, $checkarrivee = false){
+function calculer_critere_externe_init(&$boucle, $joints, $col, $desc, $cond, $checkarrivee = false) {
// si on demande un truc du genre spip_mots
// avec aussi spip_mots_liens dans les jointures dispo
// et qu'on est la
// il faut privilegier la jointure directe en 2 etapes spip_mots_liens, spip_mots
if ($checkarrivee
- AND is_string($checkarrivee)
- AND $a = table_objet($checkarrivee)
- AND in_array($a.'_liens', $joints)
- ){
+ and is_string($checkarrivee)
+ and $a = table_objet($checkarrivee)
+ and in_array($a . '_liens', $joints)
+ ) {
if ($res = calculer_lien_externe_init($boucle, $joints, $col, $desc, $cond, $checkarrivee)) {
return $res;
}
}
- foreach ($joints as $joint){
- if ($arrivee = trouver_champ_exterieur($col, array($joint), $boucle, $checkarrivee)){
+ foreach ($joints as $joint) {
+ if ($arrivee = trouver_champ_exterieur($col, array($joint), $boucle, $checkarrivee)) {
+ // alias de table dans le from
$t = array_search($arrivee[0], $boucle->from);
- // transformer eventuellement id_xx en (id_objet,objet)
- $cols = trouver_champs_decomposes($col, $arrivee[1]);
- if ($t){
+ // recuperer la cle id_xx eventuellement decomposee en (id_objet,objet)
+ $cols = $arrivee[2];
+ // mais on ignore la 3eme cle si presente qui correspond alors au point de depart
+ if (count($cols) > 2) {
+ array_pop($cols);
+ }
+ if ($t) {
+ // la table est déjà dans le FROM, on vérifie si le champ est utilisé.
$joindre = false;
- foreach ($cols as $col){
- $c = '/\b'.$t.".$col".'\b/';
- if (trouver_champ($c, $boucle->where)) $joindre = true;
- else {
+ foreach ($cols as $col) {
+ $c = '/\b' . $t . ".$col" . '\b/';
+ if (trouver_champ($c, $boucle->where)) {
+ $joindre = true;
+ } else {
// mais ca peut etre dans le FIELD pour le Having
- $c = "/FIELD.$t".".$col,/";
- if (trouver_champ($c, $boucle->select)) $joindre = true;
+ $c = "/FIELD.$t" . ".$col,/";
+ if (trouver_champ($c, $boucle->select)) {
+ $joindre = true;
+ }
}
}
- if (!$joindre) return $t;
+ if (!$joindre) {
+ return $t;
+ }
}
+ array_pop($arrivee);
if ($res = calculer_jointure($boucle, array($boucle->id_table, $desc), $arrivee, $cols, $cond, 1)) {
return $res;
}
}
}
+
return '';
}
/**
- * Generer directement une jointure via une table de lien spip_xxx_liens
- * pour un critere {id_xxx}
- * @param $boucle
- * @param $joints
- * @param $col
- * @param $desc
- * @param $cond
- * @param bool $checkarrivee
+ * Générer directement une jointure via une table de lien spip_xxx_liens
+ * pour un critère {id_xxx}
+ *
+ * @todo $checkarrivee doit être obligatoire ici ?
+ *
+ * @param Boucle $boucle
+ * Description de la boucle
+ * @param array $joints
+ * Liste de jointures possibles (ex: $boucle->jointures ou $boucle->jointures_explicites)
+ * @param string $col
+ * Colonne cible de la jointure
+ * @param array $desc
+ * Description de la table
+ * @param bool $cond
+ * Flag pour savoir si le critère est conditionnel ou non
+ * @param bool|string $checkarrivee
+ * string : nom de la table jointe où on veut trouver le champ.
+ * n'a normalement pas d'appel sans $checkarrivee.
* @return string
+ * Alias de la table de jointure (Lx)
*/
-function calculer_lien_externe_init(&$boucle, $joints, $col, $desc, $cond, $checkarrivee = false){
+function calculer_lien_externe_init(&$boucle, $joints, $col, $desc, $cond, $checkarrivee = false) {
$primary_arrivee = id_table_objet($checkarrivee);
- $intermediaire = trouver_champ_exterieur($primary_arrivee, $joints, $boucle, $checkarrivee."_liens");
+ // [FIXME] $checkarrivee peut-il arriver avec false ????
+ $intermediaire = trouver_champ_exterieur($primary_arrivee, $joints, $boucle, $checkarrivee . "_liens");
$arrivee = trouver_champ_exterieur($col, $joints, $boucle, $checkarrivee);
- if (!$intermediaire OR !$arrivee) return '';
+ if (!$intermediaire or !$arrivee) {
+ return '';
+ }
+ array_pop($intermediaire); // enlever la cle en 3eme argument
+ array_pop($arrivee); // enlever la cle en 3eme argument
$res = fabrique_jointures($boucle,
- array(
- array($boucle->id_table, $intermediaire, array(id_table_objet($desc['table_objet']), 'id_objet', 'objet', $desc['type'])),
- array(reset($intermediaire), $arrivee, $primary_arrivee)
- )
- , $cond, $desc, $boucle->id_table, array($col));
+ array(
+ array(
+ $boucle->id_table,
+ $intermediaire,
+ array(id_table_objet($desc['table_objet']), 'id_objet', 'objet', $desc['type'])
+ ),
+ array(reset($intermediaire), $arrivee, $primary_arrivee)
+ ), $cond, $desc, $boucle->id_table, array($col));
+
return $res;
}
-// http://doc.spip.org/@trouver_champ
-function trouver_champ($champ, $where){
- if (!is_array($where))
+/**
+ * Recherche la présence d'un champ dans une valeur de tableau
+ *
+ * @param string $champ
+ * Expression régulière pour trouver un champ donné.
+ * Exemple : /\barticles.titre\b/
+ * @param array $where
+ * Tableau de valeurs dans lesquels chercher le champ.
+ * @return bool
+ * true si le champ est trouvé quelque part dans $where
+ * false sinon.
+ **/
+function trouver_champ($champ, $where) {
+ if (!is_array($where)) {
return preg_match($champ, $where);
- else {
- foreach ($where as $clause){
- if (trouver_champ($champ, $clause)) return true;
+ } else {
+ foreach ($where as $clause) {
+ if (trouver_champ($champ, $clause)) {
+ return true;
+ }
}
+
return false;
}
}
-// determine l'operateur et les operandes
-
-// http://doc.spip.org/@calculer_critere_infixe_ops
-function calculer_critere_infixe_ops($idb, &$boucles, $crit){
+/**
+ * Détermine l'operateur et les opérandes d'un critère non déclaré
+ *
+ * Lorsque l'opérateur n'est pas explicite comme sur {id_article>0} c'est
+ * l'opérateur '=' qui est utilisé.
+ *
+ * Traite les cas particuliers id_parent, id_enfant, date, lang
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
+ * @return array
+ * Liste :
+ * - string $fct Nom d'une fonction SQL sur le champ ou vide (ex: SUM)
+ * - string $col Nom de la colonne SQL utilisée
+ * - string $op Opérateur
+ * - string[] $val
+ * Liste de codes PHP obtenant les valeurs des comparaisons (ex: id_article sur la boucle parente)
+ * Souvent un tableau d'un seul élément.
+ * - string $args_sql Suite des arguments du critère. ?
+ **/
+function calculer_critere_infixe_ops($idb, &$boucles, $crit) {
// cas d'une valeur comparee a elle-meme ou son referent
- if (count($crit->param)==0){
+ if (count($crit->param) == 0) {
$op = '=';
$col = $val = $crit->op;
- if (preg_match('/^(.*)\.(.*)$/', $col, $r)) $val = $r[2];
+ if (preg_match('/^(.*)\.(.*)$/', $col, $r)) {
+ $val = $r[2];
+ }
// Cas special {lang} : aller chercher $GLOBALS['spip_lang']
- if ($val=='lang')
+ if ($val == 'lang') {
$val = array(kwote('$GLOBALS[\'spip_lang\']'));
- else {
+ } else {
$defaut = null;
- if ($val=='id_parent') {
+ if ($val == 'id_parent') {
// Si id_parent, comparer l'id_parent avec l'id_objet
// de la boucle superieure.... faudrait verifier qu'il existe
// pour eviter l'erreur SQL
$val = $boucles[$idb]->primary;
// mais si pas de boucle superieure, prendre id_parent dans l'env
- $defaut = "\$Pile[0]['id_parent']";
- }
- elseif ($val=='id_enfant'){
+ $defaut = "@\$Pile[0]['id_parent']";
+ } elseif ($val == 'id_enfant') {
// Si id_enfant, comparer l'id_objet avec l'id_parent
// de la boucle superieure
$val = 'id_parent';
- }
- elseif ($crit->cond AND ($col=="date" OR $col=="date_redac")){
+ } elseif ($crit->cond and ($col == "date" or $col == "date_redac")) {
// un critere conditionnel sur date est traite a part
// car la date est mise d'office par SPIP,
- $defaut = "(\$Pile[0]['{$col}_default']?'':\$Pile[0]['".$col."'])";
+ $defaut = "(\$Pile[0]['{$col}_default']?'':\$Pile[0]['" . $col . "'])";
}
$val = calculer_argument_precedent($idb, $val, $boucles, $defaut);
// le phraseur impose que le premier param soit du texte
$params = $crit->param;
$op = $crit->op;
- if ($op=='==') $op = 'REGEXP';
+ if ($op == '==') {
+ $op = 'REGEXP';
+ }
$col = array_shift($params);
$col = $col[0]->texte;
// Dans le cas {x=='#DATE'} etc, defaire le travail du phraseur,
// celui ne sachant pas ce qu'est un critere infixe
// et a fortiori son 2e operande qu'entoure " ou '
- if (count($params)==1
- AND count($params[0]==3)
- AND $params[0][0]->type=='texte'
- AND @$params[0][2]->type=='texte'
- AND ($p = $params[0][0]->texte)==$params[0][2]->texte
- AND (($p=="'") OR ($p=='"'))
- AND $params[0][1]->type=='champ'
- ){
- $val[] = "$p\\$p#".$params[0][1]->nom_champ."\\$p$p";
- } else
- foreach ((($op!='IN') ? $params : calculer_vieux_in($params)) as $p){
+ if (count($params) == 1
+ and count($params[0]) == 3
+ and $params[0][0]->type == 'texte'
+ and $params[0][2]->type == 'texte'
+ and ($p = $params[0][0]->texte) == $params[0][2]->texte
+ and (($p == "'") or ($p == '"'))
+ and $params[0][1]->type == 'champ'
+ ) {
+ $val[] = "$p\\$p#" . $params[0][1]->nom_champ . "\\$p$p";
+ } else {
+ foreach ((($op != 'IN') ? $params : calculer_vieux_in($params)) as $p) {
$a = calculer_liste($p, $desc, $boucles, $parent);
- if (strcasecmp($op,'IN')==0) $val[] = $a;
- else $val[] = kwote($a, $boucles[$idb]->sql_serveur, 'char'); // toujours quoter en char ici
+ if (strcasecmp($op, 'IN') == 0) {
+ $val[] = $a;
+ } else {
+ $val[] = kwote($a, $boucles[$idb]->sql_serveur, 'char');
+ } // toujours quoter en char ici
}
+ }
}
$fct = $args_sql = '';
// fonction SQL ?
- if (preg_match('/^(.*)'.SQL_ARGS.'$/', $col, $m)){
+ // chercher FONCTION(champ) tel que CONCAT(titre,descriptif)
+ if (preg_match('/^(.*)' . SQL_ARGS . '$/', $col, $m)) {
$fct = $m[1];
preg_match('/^\(([^,]*)(.*)\)$/', $m[2], $a);
$col = $a[1];
- if (preg_match('/^(\S*)(\s+AS\s+.*)$/i', $col, $m)){
+ if (preg_match('/^(\S*)(\s+AS\s+.*)$/i', $col, $m)) {
$col = $m[1];
$args_sql = $m[2];
}
$args_sql .= $a[2];
- ;
}
return array($fct, $col, $op, $val, $args_sql);
// compatibilite ancienne version
-// http://doc.spip.org/@calculer_vieux_in
-function calculer_vieux_in($params){
+// http://code.spip.net/@calculer_vieux_in
+function calculer_vieux_in($params) {
$deb = $params[0][0];
- $k = count($params)-1;
+ $k = count($params) - 1;
$last = $params[$k];
- $j = count($last)-1;
+ $j = count($last) - 1;
$last = $last[$j];
$n = isset($last->texte) ? strlen($last->texte) : 0;
- if (!((isset($deb->texte[0]) AND $deb->texte[0]=='(')
- && (isset($last->texte[$n-1]) AND $last->texte[$n-1]==')')))
+ if (!((isset($deb->texte[0]) and $deb->texte[0] == '(')
+ && (isset($last->texte[$n - 1]) and $last->texte[$n - 1] == ')'))
+ ) {
return $params;
+ }
$params[0][0]->texte = substr($deb->texte, 1);
// attention, on peut avoir k=0,j=0 ==> recalculer
$last = $params[$k][$j];
$n = strlen($last->texte);
- $params[$k][$j]->texte = substr($last->texte, 0, $n-1);
+ $params[$k][$j]->texte = substr($last->texte, 0, $n - 1);
$newp = array();
- foreach ($params as $v){
- if ($v[0]->type!='texte')
+ foreach ($params as $v) {
+ if ($v[0]->type != 'texte') {
$newp[] = $v;
- else {
- foreach (explode(',', $v[0]->texte) as $x){
+ } else {
+ foreach (explode(',', $v[0]->texte) as $x) {
$t = new Texte;
$t->texte = $x;
$newp[] = array($t);
}
}
}
+
return $newp;
}
-// http://doc.spip.org/@calculer_critere_infixe_date
-function calculer_critere_infixe_date($idb, &$boucles, $col){
- if (!preg_match(",^((age|jour|mois|annee)_relatif|date|mois|annee|jour|heure|age)(_[a-z]+)?$,", $col, $regs)) return '';
+/**
+ * Calcule les cas particuliers de critères de date
+ *
+ * Lorsque la colonne correspond à un critère de date, tel que
+ * jour, jour_relatif, jour_x, age, age_relatif, age_x...
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param string $col Nom du champ demandé
+ * @return string|array
+ * chaine vide si ne correspond pas à une date,
+ * sinon liste
+ * - expression SQL de calcul de la date,
+ * - nom de la colonne de date (si le calcul n'est pas relatif)
+ **/
+function calculer_critere_infixe_date($idb, &$boucles, $col) {
+ if (!preg_match(",^((age|jour|mois|annee)_relatif|date|mois|annee|jour|heure|age)(_[a-z]+)?$,", $col, $regs)) {
+ return '';
+ }
+
$boucle = $boucles[$idb];
$table = $boucle->show;
+
// si c'est une colonne de la table, ne rien faire
- if(isset($table['field'][$col])) return '';
+ if (isset($table['field'][$col])) {
+ return '';
+ }
+
+ if (!$table['date'] && !isset($GLOBALS['table_date'][$table['id_table']])) {
+ return '';
+ }
+ $pred = $date_orig = isset($GLOBALS['table_date'][$table['id_table']]) ? $GLOBALS['table_date'][$table['id_table']] : $table['date'];
- if (!$table['date'] && !isset($GLOBALS['table_date'][$table['id_table']])) return '';
- $pred = $date_orig = isset($GLOBALS['table_date'][$table['id_table']])? $GLOBALS['table_date'][$table['id_table']] : $table['date'];
$col = $regs[1];
- if (isset($regs[3]) AND $suite = $regs[3]){
+ if (isset($regs[3]) and $suite = $regs[3]) {
# Recherche de l'existence du champ date_xxxx,
# si oui choisir ce champ, sinon choisir xxxx
- if (isset($table['field']["date$suite"]))
- $date_orig = 'date'.$suite;
- else
+ if (isset($table['field']["date$suite"])) {
+ $date_orig = 'date' . $suite;
+ } else {
$date_orig = substr($suite, 1);
+ }
$pred = $date_orig;
+ } else {
+ if (isset($regs[2]) and $rel = $regs[2]) {
+ $pred = 'date';
+ }
}
- else
- if (isset($regs[2]) AND $rel = $regs[2]) $pred = 'date';
- $date_compare = "\"' . normaliser_date(".
- calculer_argument_precedent($idb, $pred, $boucles).
- ") . '\"";
+ $date_compare = "\"' . normaliser_date(" .
+ calculer_argument_precedent($idb, $pred, $boucles) .
+ ") . '\"";
$col_vraie = $date_orig;
- $date_orig = $boucle->id_table.'.'.$date_orig;
+ $date_orig = $boucle->id_table . '.' . $date_orig;
switch ($col) {
case 'date':
$col_vraie = "";// comparer a un int (par defaut)
break;
case 'jour_relatif':
- $col = "(TO_DAYS(".$date_compare.")-TO_DAYS(".$date_orig."))";
+ $col = "(TO_DAYS(" . $date_compare . ")-TO_DAYS(" . $date_orig . "))";
$col_vraie = "";// comparer a un int (par defaut)
break;
case 'mois_relatif':
- $col = "MONTH(".$date_compare.")-MONTH(".
- $date_orig.")+12*(YEAR(".$date_compare.
- ")-YEAR(".$date_orig."))";
+ $col = "MONTH(" . $date_compare . ")-MONTH(" .
+ $date_orig . ")+12*(YEAR(" . $date_compare .
+ ")-YEAR(" . $date_orig . "))";
$col_vraie = "";// comparer a un int (par defaut)
break;
case 'annee_relatif':
- $col = "YEAR(".$date_compare.")-YEAR(".
- $date_orig.")";
+ $col = "YEAR(" . $date_compare . ")-YEAR(" .
+ $date_orig . ")";
$col_vraie = "";// comparer a un int (par defaut)
break;
}
- return array($col,$col_vraie);
+
+ return array($col, $col_vraie);
}
-// http://doc.spip.org/@calculer_param_date
-function calculer_param_date($date_compare, $date_orig){
- if (preg_match(",'\" *\.(.*)\. *\"',", $date_compare, $r)){
+/**
+ * Calcule l'expression SQL permettant de trouver un nombre de jours écoulés.
+ *
+ * Le calcul SQL retournera un nombre de jours écoulés entre la date comparée
+ * et la colonne SQL indiquée
+ *
+ * @param string $date_compare
+ * Code PHP permettant d'obtenir le timestamp référent.
+ * C'est à partir de lui que l'on compte les jours
+ * @param string $date_orig
+ * Nom de la colonne SQL qui possède la date
+ * @return string
+ * Expression SQL calculant le nombre de jours écoulé entre une valeur
+ * de colonne SQL et une date.
+ **/
+function calculer_param_date($date_compare, $date_orig) {
+ if (preg_match(",'\" *\.(.*)\. *\"',", $date_compare, $r)) {
$init = "'\" . (\$x = $r[1]) . \"'";
$date_compare = '\'$x\'';
- }
- else
+ } else {
$init = $date_compare;
+ }
return
- "LEAST((UNIX_TIMESTAMP(".
- $init.
- ")-UNIX_TIMESTAMP(".
- $date_orig.
- "))/86400,\n\tTO_DAYS(".
- $date_compare.
- ")-TO_DAYS(".
- $date_orig.
- "),\n\tDAYOFMONTH(".
- $date_compare.
- ")-DAYOFMONTH(".
- $date_orig.
- ")+30.4368*(MONTH(".
- $date_compare.
- ")-MONTH(".
- $date_orig.
- "))+365.2422*(YEAR(".
- $date_compare.
- ")-YEAR(".
- $date_orig.
- ")))";
+ // optimisation : mais prevoir le support SQLite avant
+ "TIMESTAMPDIFF(HOUR,$date_orig,$init)/24";
}
/**
- * (DATA){source mode, "xxxxxx", arg, arg, arg}
- * @param string $idb
- * @param object $boucles
- * @param object $crit
+ * Compile le critère {source} d'une boucle DATA
+ *
+ * Permet de déclarer le mode d'obtention des données dans une boucle
+ * DATA (premier argument) et les données (la suite).
+ *
+ * @example
+ * (DATA){source mode, "xxxxxx", arg, arg, arg}
+ * (DATA){source tableau, #LISTE{un,deux,trois}}
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_DATA_source_dist($idb, &$boucles, $crit){
+function critere_DATA_source_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$args = array();
- foreach ($crit->param as &$param)
+ foreach ($crit->param as &$param) {
array_push($args,
- calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent));
+ calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent));
+ }
$boucle->hash .= '
- $command[\'sourcemode\'] = '.array_shift($args).";\n";
+ $command[\'sourcemode\'] = ' . array_shift($args) . ";\n";
$boucle->hash .= '
- $command[\'source\'] = array('.join(', ', $args).");\n";
+ $command[\'source\'] = array(' . join(', ', $args) . ");\n";
}
/**
- * (DATA){datasource "xxxxxx", mode} <= deprecated
- * @param string $idb
- * @param object $boucles
- * @param object $crit
+ * Compile le critère {datasource} d'une boucle DATA
+ *
+ * Permet de déclarer le mode d'obtention des données dans une boucle DATA
+ *
+ * @deprecated Utiliser directement le critère {source}
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_DATA_datasource_dist($idb, &$boucles, $crit){
+function critere_DATA_datasource_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$boucle->hash .= '
- $command[\'source\'] = array('.calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent).');
- $command[\'sourcemode\'] = '.calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent).';';
+ $command[\'source\'] = array(' . calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent) . ');
+ $command[\'sourcemode\'] = ' . calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent) . ';';
}
/**
- * (DATA){datacache}
- * @param string $idb
- * @param object $boucles
- * @param object $crit
+ * Compile le critère {datacache} d'une boucle DATA
+ *
+ * Permet de transmettre une durée de cache (time to live) utilisée
+ * pour certaines sources d'obtention des données (par exemple RSS),
+ * indiquant alors au bout de combien de temps la donnée est à réobtenir.
+ *
+ * La durée par défaut est 1 journée.
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_DATA_datacache_dist($idb, &$boucles, $crit){
+function critere_DATA_datacache_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$boucle->hash .= '
- $command[\'datacache\'] = '.calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent).';';
+ $command[\'datacache\'] = ' . calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent) . ';';
}
/**
- * Pour passer des arguments a un iterateur non-spip
- * (php:xxxIterator){args argument1, argument2, argument3}
+ * Compile le critère {args} d'une boucle PHP
*
- * @param string $idb
- * @param object $boucles
- * @param object $crit
+ * Permet de passer des arguments à un iterateur non-spip
+ * (PHP:xxxIterator){args argument1, argument2, argument3}
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_php_args_dist($idb, &$boucles, $crit){
+function critere_php_args_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$boucle->hash .= '$command[\'args\']=array();';
- foreach ($crit->param as $param){
+ foreach ($crit->param as $param) {
$boucle->hash .= '
- $command[\'args\'][] = '.calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent).';';
+ $command[\'args\'][] = ' . calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent) . ';';
}
}
/**
- * Passer une liste de donnees a l'iterateur DATA
- * (DATA){liste X1, X2, X3}
+ * Compile le critère {liste} d'une boucle DATA
*
- * @param string $idb
- * @param object $boucles
- * @param object $crit
+ * Passe une liste de données à l'itérateur DATA
+ *
+ * @example
+ * (DATA){liste X1, X2, X3}
+ * équivalent à (DATA){source tableau,#LISTE{X1, X2, X3}}
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_DATA_liste_dist($idb, &$boucles, $crit){
+function critere_DATA_liste_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
- $boucle->hash .= "\n\t".'$command[\'liste\'] = array();'."\n";
- foreach ($crit->param as $param){
- $boucle->hash .= "\t".'$command[\'liste\'][] = '.calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent).";\n";
+ $boucle->hash .= "\n\t" . '$command[\'liste\'] = array();' . "\n";
+ foreach ($crit->param as $param) {
+ $boucle->hash .= "\t" . '$command[\'liste\'][] = ' . calculer_liste($param, array(), $boucles,
+ $boucles[$idb]->id_parent) . ";\n";
}
}
/**
- * Passer un enum min max a l'iterateur DATA
- * (DATA){enum Xmin, Xmax}
+ * Compile le critère {enum} d'une boucle DATA
*
- * @param string $idb
- * @param object $boucles
- * @param object $crit
+ * Passe les valeurs de début et de fin d'une énumération, qui seront
+ * vues comme une liste d'autant d'éléments à parcourir pour aller du
+ * début à la fin.
+ *
+ * Cela utilisera la fonction range() de PHP.
+ *
+ * @example
+ * (DATA){enum Xdebut, Xfin}
+ * (DATA){enum a,z}
+ * (DATA){enum z,a}
+ * (DATA){enum 1.0,9.2}
+ *
+ * @link http://php.net/manual/fr/function.range.php
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_DATA_enum_dist($idb, &$boucles, $crit){
+function critere_DATA_enum_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
- $boucle->hash .= "\n\t".'$command[\'enum\'] = array();'."\n";
- foreach ($crit->param as $param){
- $boucle->hash .= "\t".'$command[\'enum\'][] = '.calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent).";\n";
+ $boucle->hash .= "\n\t" . '$command[\'enum\'] = array();' . "\n";
+ foreach ($crit->param as $param) {
+ $boucle->hash .= "\t" . '$command[\'enum\'][] = ' . calculer_liste($param, array(), $boucles,
+ $boucles[$idb]->id_parent) . ";\n";
}
}
/**
- * Extraire un chemin d'un tableau de donnees
+ * Compile le critère {datapath} d'une boucle DATA
+ *
+ * Extrait un chemin d'un tableau de données
+ *
* (DATA){datapath query.results}
*
- * @param string $idb
- * @param object $boucles
- * @param object $crit
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_DATA_datapath_dist($idb, &$boucles, $crit){
+function critere_DATA_datapath_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
- foreach ($crit->param as $param){
+ foreach ($crit->param as $param) {
$boucle->hash .= '
- $command[\'datapath\'][] = '.calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent).';';
+ $command[\'datapath\'][] = ' . calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent) . ';';
}
}
/**
- * le critere {si ...} applicable a toutes les boucles
+ * Compile le critère {si}
*
- * @param string $idb
- * @param object $boucles
- * @param object $crit
+ * Le critère {si condition} est applicable à toutes les boucles et conditionne
+ * l'exécution de la boucle au résultat de la condition. La partie alternative
+ * de la boucle est alors affichée si une condition n'est pas remplie (comme
+ * lorsque la boucle ne ramène pas de résultat).
+ * La différence étant que si la boucle devait réaliser une requête SQL
+ * (par exemple une boucle ARTICLES), celle ci n'est pas réalisée si la
+ * condition n'est pas remplie.
+ *
+ * Les valeurs de la condition sont forcément extérieures à cette boucle
+ * (sinon il faudrait l'exécuter pour connaître le résultat, qui doit tester
+ * si on exécute la boucle !)
+ *
+ * Si plusieurs critères {si} sont présents, ils sont cumulés :
+ * si une seule des conditions n'est pas vérifiée, la boucle n'est pas exécutée.
+ *
+ * @example
+ * {si #ENV{exec}|=={article}}
+ * {si (#_contenu:GRAND_TOTAL|>{10})}
+ * {si #AUTORISER{voir,articles}}
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_si_dist($idb, &$boucles, $crit){
+function critere_si_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
// il faut initialiser 1 fois le tableau a chaque appel de la boucle
// (par exemple lorsque notre boucle est appelee dans une autre boucle)
// mais ne pas l'initialiser n fois si il y a n criteres {si } dans la boucle !
$boucle->hash .= "\n\tif (!isset(\$si_init)) { \$command['si'] = array(); \$si_init = true; }\n";
- if ($crit->param){
- foreach ($crit->param as $param){
+ if ($crit->param) {
+ foreach ($crit->param as $param) {
$boucle->hash .= "\t\$command['si'][] = "
- . calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent) . ";\n";
+ . calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent) . ";\n";
}
// interdire {si 0} aussi !
} else {
}
/**
+ * Compile le critère {tableau} d'une boucle POUR
+ *
* {tableau #XX} pour compatibilite ascendante boucle POUR
- * ... preferer la notation {datasource #XX,table}
+ * ... préférer la notation (DATA){source tableau,#XX}
*
- * @param string $idb
- * @param object $boucles
- * @param object $crit
+ * @deprecated Utiliser une boucle (DATA){source tableau,#XX}
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_POUR_tableau_dist($idb, &$boucles, $crit){
+function critere_POUR_tableau_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$boucle->hash .= '
- $command[\'source\'] = array('.calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent).');
+ $command[\'source\'] = array(' . calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent) . ');
$command[\'sourcemode\'] = \'table\';';
}
/**
- * Trouver toutes les objets qui ont des enfants (les noeuds de l'arbre)
+ * Compile le critère {noeud}
+ *
+ * Trouver tous les objets qui ont des enfants (les noeuds de l'arbre)
* {noeud}
* {!noeud} retourne les feuilles
*
* @global array $exceptions_des_tables
- * @param string $idb
- * @param array $boucles
- * @param Object $crit
+ *
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_noeud_dist($idb, &$boucles, $crit){
- global $exceptions_des_tables;
+function critere_noeud_dist($idb, &$boucles, $crit) {
+
$not = $crit->not;
$boucle = &$boucles[$idb];
$primary = $boucle->primary;
- if (!$primary OR strpos($primary, ',')){
- erreur_squelette(_T('zbug_doublon_sur_table_sans_cle_primaire'), "BOUCLE$idb");
+ if (!$primary or strpos($primary, ',')) {
+ erreur_squelette(_T('zbug_doublon_sur_table_sans_cle_primaire'), $boucle);
+
return;
}
$table = $boucle->type_requete;
$table_sql = table_objet_sql(objet_type($table));
- $id_parent = isset($exceptions_des_tables[$boucle->id_table]['id_parent']) ?
- $exceptions_des_tables[$boucle->id_table]['id_parent'] :
+ $id_parent = isset($GLOBALS['exceptions_des_tables'][$boucle->id_table]['id_parent']) ?
+ $GLOBALS['exceptions_des_tables'][$boucle->id_table]['id_parent'] :
'id_parent';
$in = "IN";
- $where = array("'IN'", "'$boucle->id_table."."$primary'", "'('.sql_get_select('$id_parent', '$table_sql').')'");
- if ($not)
+ $where = array("'IN'", "'$boucle->id_table." . "$primary'", "'('.sql_get_select('$id_parent', '$table_sql').')'");
+ if ($not) {
$where = array("'NOT'", $where);
+ }
$boucle->where[] = $where;
}
/**
- * Trouver toutes les objets qui n'ont pas d'enfants (les feuilles de l'arbre)
+ * Compile le critère {feuille}
+ *
+ * Trouver tous les objets qui n'ont pas d'enfants (les feuilles de l'arbre)
* {feuille}
* {!feuille} retourne les noeuds
*
* @global array $exceptions_des_tables
- * @param string $idb
- * @param array $boucles
- * @param Object $crit
+ * @param string $idb Identifiant de la boucle
+ * @param array $boucles AST du squelette
+ * @param Critere $crit Paramètres du critère dans cette boucle
*/
-function critere_feuille_dist($idb, &$boucles, $crit){
+function critere_feuille_dist($idb, &$boucles, $crit) {
$not = $crit->not;
$crit->not = $not ? false : true;
critere_noeud_dist($idb, $boucles, $crit);
$crit->not = $not;
}
-
-?>