3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2014 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
14 // Definition des {criteres} d'une boucle
17 // Une Regexp reperant une chaine produite par le compilateur,
18 // souvent utilisee pour faire de la concatenation lors de la compilation
19 // plutot qu'a l'execution, i.e. pour remplacer 'x'.'y' par 'xy'
21 define('_CODE_QUOTE', ",^(\n//[^\n]*\n)? *'(.*)' *$,");
23 if (!defined('_ECRIRE_INC_VERSION')) return;
26 // http://www.spip.net/@racine
27 // http://doc.spip.org/@critere_racine_dist
28 function critere_racine_dist($idb, &$boucles, $crit) {
29 global $exceptions_des_tables;
31 $boucle = &$boucles[$idb];
32 $id_parent = isset($exceptions_des_tables[$boucle->id_table
]['id_parent']) ?
33 $exceptions_des_tables[$boucle->id_table
]['id_parent'] :
37 return (array('zbug_critere_inconnu', array('critere' => $not . $crit->op
)));
39 $boucle->where
[]= array("'='", "'$boucle->id_table." . "$id_parent'", 0);
43 // http://www.spip.net/@exclus
44 // http://doc.spip.org/@critere_exclus_dist
45 function critere_exclus_dist($idb, &$boucles, $crit) {
47 $boucle = &$boucles[$idb];
48 $id = $boucle->primary
;
51 return (array('zbug_critere_inconnu', array('critere' => $not . $crit->op
)));
52 $arg = kwote(calculer_argument_precedent($idb, $id, $boucles));
53 $boucle->where
[]= array("'!='", "'$boucle->id_table." . "$id'", $arg);
56 // {doublons} ou {unique}
57 // http://www.spip.net/@doublons
58 // attention: boucle->doublons designe une variable qu'on affecte
59 // http://doc.spip.org/@critere_doublons_dist
60 function critere_doublons_dist($idb, &$boucles, $crit) {
61 $boucle = &$boucles[$idb];
62 $primary = $boucle->primary
;
63 $type = $boucle->type_requete
;
64 // Dans le cas NOT, la table du doublon peut etre indiquee
65 // si la table courante a un champ homonyme de sa cle primaire.
66 // Tres utile pour la table des forums.
67 if (isset($crit->param
[1])) {
69 $x = !$crit->not ?
'' : calculer_liste($crit->param
[1], array(), $boucles, $boucle->id_parent
);
70 # attention au commentaire "// x signes" qui precede
71 if (preg_match(",^(?:\s*//[^\n]*\n)?'([^']+)'*$,ms", $x, $m)) {
72 $x = id_table_objet($type = $m[1]);
73 if (isset($boucle->show
['field'][$x]))
74 $primary = $x; // sinon erreur declenchee ci-dessous
78 if (!$primary OR strpos($primary,',')) {
79 return (array('zbug_doublon_sur_table_sans_cle_primaire'));
81 $not = ($crit->not ?
'' : 'NOT');
82 $nom = !isset($crit->param
[0]) ?
"''" : calculer_liste($crit->param
[0], array(), $boucles, $boucles[$idb]->id_parent
);
83 // mettre un tableau pour que ce ne soit pas vu comme une constante
85 $nom = "'" . $type . "'" . ($nom == "''" ?
'' : " . $nom");
87 $debutdoub = '$doublons['
88 . (!$not ?
'' : ($boucle->doublons
. "[]= "));
92 $debin = "sql_in('" . $boucle->id_table
. '.' . $primary . "', ";
94 $suitin = $debin . $debutdoub;
96 // si autre critere doublon, fusionner pour avoir un seul In
97 foreach ($boucle->where
as $k => $w) {
98 if (strpos($w[0], $suitin) ===0) {
99 $boucle->where
[$k][0] = $debin . $debutdoub . $findoub . ' . ' . substr($w[0],strlen($debin));
103 $boucle->where
[]= array($suitin . $findoub . ", '" . $not . "')");
105 # la ligne suivante avait l'intention d'eviter une collecte deja faite
106 # mais elle fait planter une boucle a 2 critere doublons:
107 # {!doublons A}{doublons B}
108 # (de http://article.gmane.org/gmane.comp.web.spip.devel/31034)
109 # if ($crit->not) $boucle->doublons = "";
113 // http://www.spip.net/@lang_select
114 // http://doc.spip.org/@critere_lang_select_dist
115 function critere_lang_select_dist($idb, &$boucles, $crit) {
116 if (!($param = $crit->param
[1][0]->texte
)) $param = 'oui';
117 if ($crit->not
) $param = ($param=='oui') ?
'non' : 'oui';
118 $boucle = &$boucles[$idb];
119 $boucle->lang_select
= $param;
123 // http://www.spip.net/@debut_
124 // http://doc.spip.org/@critere_debut_dist
125 function critere_debut_dist($idb, &$boucles, $crit) {
126 list($un, $deux) = $crit->param
;
128 $deux = $deux[0]->texte
;
130 $boucles[$idb]->limit
= 'intval($Pile[0]["debut' .
135 } else calculer_critere_DEFAUT_dist($idb, $boucles, $crit);
140 // {pagination #ENV{pages,5}} etc
141 // {pagination 20 #ENV{truc,chose}} pour utiliser la variable debut_#ENV{truc,chose}
142 // http://www.spip.net/@pagination
143 // http://doc.spip.org/@critere_pagination_dist
144 function critere_pagination_dist($idb, &$boucles, $crit) {
146 $boucle = &$boucles[$idb];
147 // definition de la taille de la page
148 $pas = !isset($crit->param
[0][0]) ?
"''" : calculer_liste(array($crit->param
[0][0]), array(), $boucles, $boucle->id_parent
);
150 if (!preg_match(_CODE_QUOTE
, $pas, $r)) {
151 $pas = "((\$a = intval($pas)) ? \$a : 10)";
154 $pas = strval($r ?
$r : 10);
156 $type = !isset($crit->param
[0][1]) ?
"'$idb'" : calculer_liste(array($crit->param
[0][1]), array(), $boucles, $boucle->id_parent
);
157 $debut = ($type[0]!=="'") ?
"'debut'.$type"
158 : ("'debut" .substr($type,1));
160 $boucle->modificateur
['debut_nom'] = $type;
162 // tester si le numero de page demande est de la forme '@yyy'
163 'isset($Pile[0]['.$debut.']) ? $Pile[0]['.$debut.'] : _request('.$debut.");\n"
164 ."\tif(substr(\$debut_boucle,0,1)=='@'){\n"
165 ."\t\t".'$debut_boucle = $Pile[0]['. $debut.'] = quete_debut_pagination(\''.$boucle->primary
.'\',$Pile[0][\'@'.$boucle->primary
.'\'] = substr($debut_boucle,1),'.$pas.',$result,'._q($boucle->sql_serveur
).');'."\n"
166 ."\t\t".'if (!sql_seek($result,0,'._q($boucle->sql_serveur
).")){\n"
167 ."\t\t\t".'@sql_free($result,'._q($boucle->sql_serveur
).");\n"
168 ."\t\t\t".'$result = calculer_select($select, $from, $type, $where, $join, $groupby, $orderby, $limit, $having, $table, $id, $connect);'."\n"
171 ."\t".'$debut_boucle = intval($debut_boucle)';
174 $boucle->total_parties
= $pas;
175 calculer_parties($boucles, $idb, $partie, 'p+');
176 // ajouter la cle primaire dans le select pour pouvoir gerer la pagination referencee par @id
177 // sauf si pas de primaire, ou si primaire composee
178 // dans ce cas, on ne sait pas gerer une pagination indirecte
179 $t = $boucle->id_table
. '.' . $boucle->primary
;
181 AND !preg_match('/[,\s]/',$boucle->primary
)
182 AND !in_array($t, $boucle->select
))
183 $boucle->select
[]= $t;
187 // {recherche} ou {recherche susan}
188 // http://www.spip.net/@recherche
189 // http://doc.spip.org/@critere_recherche_dist
190 function critere_recherche_dist($idb, &$boucles, $crit) {
192 $boucle = &$boucles[$idb];
194 if (isset($crit->param
[0]))
195 $quoi = calculer_liste($crit->param
[0], array(), $boucles, $boucles[$idb]->id_parent
);
197 $quoi = '@$Pile[0]["recherche"]';
199 // indiquer si l'on est dans une boucle forum avec le critère {plat} ou {tout}
201 if (isset($boucle->modificateur
['tout']) OR isset($boucle->modificateur
['plat'])) {
207 $prepare_recherche = charger_fonction(\'prepare_recherche\', \'inc\');
208 list($rech_select, $rech_where) = $prepare_recherche('.$quoi.', "'.$boucle->id_table
.'", "'.$crit->cond
.'","' . $boucle->sql_serveur
. '", "'.$plat.'");
211 $t = $boucle->id_table
. '.' . $boucle->primary
;
212 if (!in_array($t, $boucles[$idb]->select
))
213 $boucle->select
[]= $t; # pour postgres, neuneu ici
214 $boucle->join
['resultats']=array("'".$boucle->id_table
."'","'id'","'".$boucle->primary
."'");
215 $boucle->from
['resultats']='spip_resultats';
216 $boucle->select
[]= '$rech_select';
217 //$boucle->where[]= "\$rech_where?'resultats.id=".$boucle->id_table.".".$boucle->primary."':''";
219 // et la recherche trouve
220 $boucle->where
[]= '$rech_where?$rech_where:\'\'';
224 // http://www.spip.net/@traduction
225 // (id_trad>0 AND id_trad=id_trad(precedent))
226 // OR id_article=id_article(precedent)
227 // http://doc.spip.org/@critere_traduction_dist
228 function critere_traduction_dist($idb, &$boucles, $crit) {
229 $boucle = &$boucles[$idb];
230 $prim = $boucle->primary
;
231 $table = $boucle->id_table
;
232 $arg = kwote(calculer_argument_precedent($idb, 'id_trad', $boucles));
233 $dprim = kwote(calculer_argument_precedent($idb, $prim, $boucles));
237 array("'='", "'$table.id_trad'", 0),
238 array("'='", "'$table.$prim'", $dprim)
241 array("'>'", "'$table.id_trad'", 0),
242 array("'='", "'$table.id_trad'", $arg)
247 // {origine_traduction}
248 // (id_trad>0 AND id_article=id_trad) OR (id_trad=0)
249 // http://www.spip.net/@origine_traduction
250 // http://doc.spip.org/@critere_origine_traduction_dist
251 function critere_origine_traduction_dist($idb, &$boucles, $crit) {
252 $boucle = &$boucles[$idb];
253 $prim = $boucle->primary
;
254 $table = $boucle->id_table
;
258 array("'='", "'$table." . "id_trad'", "'$table.$prim'"),
259 array("'='", "'$table.id_trad'", "'0'")
261 $boucle->where
[]= ($crit->not ?
array("'NOT'", $c) : $c);
265 // http://www.spip.net/@meme_parent
266 // http://doc.spip.org/@critere_meme_parent_dist
267 function critere_meme_parent_dist($idb, &$boucles, $crit) {
268 global $exceptions_des_tables;
269 $boucle = &$boucles[$idb];
270 $arg = kwote(calculer_argument_precedent($idb, 'id_parent', $boucles));
271 $id_parent = isset($exceptions_des_tables[$boucle->id_table
]['id_parent']) ?
272 $exceptions_des_tables[$boucle->id_table
]['id_parent'] :
274 $mparent = $boucle->id_table
. '.' . $id_parent;
276 if ($boucle->type_requete
== 'rubriques' OR isset($exceptions_des_tables[$boucle->id_table
]['id_parent'])) {
277 $boucle->where
[]= array("'='", "'$mparent'", $arg);
279 } else if ($boucle->type_requete
== 'forums') {
280 $boucle->where
[]= array("'='", "'$mparent'", $arg);
281 $boucle->where
[]= array("'>'", "'$mparent'", 0);
282 $boucle->modificateur
['plat'] = true;
283 } else erreur_squelette(_T('zbug_info_erreur_squelette'), "{meme_parent} BOUCLE$idb");
287 // http://www.spip.net/@branche
288 // http://doc.spip.org/@critere_branche_dist
289 function critere_branche_dist($idb, &$boucles, $crit) {
292 $boucle = &$boucles[$idb];
293 $arg = calculer_argument_precedent($idb, 'id_rubrique', $boucles);
295 //Trouver une jointure
296 $desc = $boucle->show
;
297 //Seulement si necessaire
298 if (!array_key_exists('id_rubrique', $desc['field'])) {
299 $cle = trouver_jointure_champ('id_rubrique', $boucle);
300 } else $cle = $boucle->id_table
;
302 $c = "sql_in('$cle" . ".id_rubrique', calcul_branche_in($arg)"
303 . ($not ?
", 'NOT'" : '') . ")";
304 $boucle->where
[]= !$crit->cond ?
$c :
305 ("($arg ? $c : " . ($not ?
"'0=1'" : "'1=1'") .')');
308 // {logo} liste les objets qui ont un logo
309 // http://doc.spip.org/@critere_logo_dist
310 function critere_logo_dist($idb, &$boucles, $crit) {
313 $boucle = &$boucles[$idb];
316 $boucle->id_table
. '.' . $boucle->primary
317 . "', lister_objets_avec_logos('". $boucle->primary
."'), '')";
318 if ($crit->cond
) $c = "($arg ? $c : 1)";
321 $boucle->where
[]= array("'NOT'", $c);
323 $boucle->where
[]= $c;
326 // c'est la commande SQL "GROUP BY"
327 // par exemple <boucle(articles){fusion lang}>
328 // http://doc.spip.org/@critere_fusion_dist
329 function critere_fusion_dist($idb,&$boucles, $crit) {
330 if ($t = isset($crit->param
[0])) {
331 $t = $crit->param
[0];
332 if ($t[0]->type
== 'texte') {
334 if (preg_match("/^(.*)\.(.*)$/", $t, $r)) {
335 $t = table_objet_sql($r[1]);
336 $t = array_search($t, $boucles[$idb]->from
);
337 if ($t) $t .= '.' . $r[2];
340 . calculer_critere_arg_dynamique($idb, $boucles, $t)
345 $boucles[$idb]->group
[] = $t;
346 if (!in_array($t, $boucles[$idb]->select
))
347 $boucles[$idb]->select
[] = $t;
349 return (array('zbug_critere_inconnu', array('critere' => $crit->op
. ' ?')));
352 // c'est la commande SQL "COLLATE"
353 // qui peut etre appliquee sur les order by, group by, where like ...
354 // http://doc.spip.org/@critere_collecte_dist
355 function critere_collecte_dist($idb,&$boucles, $crit) {
356 if (isset($crit->param
[0])) {
357 $_coll = calculer_liste($crit->param
[0], array(), $boucles, $boucles[$idb]->id_parent
);
358 $boucle = $boucles[$idb];
359 $boucle->modificateur
['collate'] = "($_coll ?' COLLATE '.$_coll:'')";
360 $n = count($boucle->order
);
361 if ($n && (strpos($boucle->order
[$n-1],'COLLATE')===false))
362 $boucle->order
[$n-1] .= " . " . $boucle->modificateur
['collate'];
364 return (array('zbug_critere_inconnu', array('critere' => $crit->op
. " $n")));
367 // http://doc.spip.org/@calculer_critere_arg_dynamique
368 function calculer_critere_arg_dynamique($idb, &$boucles, $crit, $suffix='')
370 $boucle = $boucles[$idb];
371 $alt = "('" . $boucle->id_table
. '.\' . $x' . $suffix . ')';
372 $var = '$champs_' . $idb;
373 $desc = (strpos($boucle->in
, "static $var =") !== false);
375 $desc = $boucle->show
['field'];
376 $desc = implode(',',array_map('_q',array_keys($desc)));
377 $boucles[$idb]->in
.= "\n\tstatic $var = array(" . $desc .");";
379 if ($desc) $alt = "(in_array(\$x, $var) ? $alt :(\$x$suffix))";
380 $arg = calculer_liste($crit, array(), $boucles, $boucle->id_parent
);
381 return "((\$x = preg_replace(\"/\\W/\",'', $arg)) ? $alt : '')";
384 // http://www.spip.net/@par
385 // http://doc.spip.org/@critere_par_dist
386 function critere_par_dist($idb, &$boucles, $crit) {
387 return critere_parinverse($idb, $boucles, $crit) ;
390 // http://doc.spip.org/@critere_parinverse
391 function critere_parinverse($idb, &$boucles, $crit, $sens='') {
392 global $exceptions_des_jointures;
393 $boucle = &$boucles[$idb];
394 if ($crit->not
) $sens = $sens ?
"" : " . ' DESC'";
395 $collecte = (isset($boucle->modificateur
['collecte']))?
" . ".$boucle->modificateur
['collecte']:"";
397 foreach ($crit->param
as $tri) {
399 $order = $fct = ""; // en cas de fonction SQL
400 // tris specifies dynamiquement
401 if ($tri[0]->type
!= 'texte') {
402 // calculer le order dynamique qui verifie les champs
403 $order = calculer_critere_arg_dynamique($idb, $boucles, $tri, $sens);
404 // et si ce n'est fait, ajouter un champ 'hasard'
405 // pour supporter 'hasard' comme tri dynamique
407 $parha = $par . " AS hasard";
408 if (!in_array($parha, $boucle->select
))
409 $boucle->select
[]= $parha;
411 $par = array_shift($tri);
414 if (preg_match(",^multi[\s]*(.*)$,",$par, $m)) {
415 $texte = $boucle->id_table
. '.' . trim($m[1]);
416 $boucle->select
[] = "\".sql_multi('".$texte."', \$GLOBALS['spip_lang']).\"" ;
418 // par num champ(, suite)
419 } else if (preg_match(",^num (.*)$,m",$par, $m)) {
420 $texte = '0+' . $boucle->id_table
. '.' . trim($m[1]);
421 $suite = calculer_liste($tri, array(), $boucles, $boucle->id_parent
);
423 $texte = "\" . ((\$x = $suite) ? ('$texte' . \$x) : '0')" . " . \"";
424 $as = 'num' .($boucle->order ?
count($boucle->order
) : "");
425 $boucle->select
[] = $texte . " AS $as";
428 if (!preg_match(",^" . CHAMP_SQL_PLUS_FONC
. '$,is', $par, $match)) {
429 return (array('zbug_critere_inconnu', array('critere' => $crit->op
. " $par")));
431 if (count($match)>2) { $par = substr($match[2],1,-1); $fct = $match[1]; }
433 if ($par == 'hasard') {
435 $boucle->select
[]= $par . " AS alea";
438 // par titre_mot ou type_mot voire d'autres
439 else if (isset($exceptions_des_jointures[$par])) {
440 list($table, $champ) = $exceptions_des_jointures[$par];
441 $order = critere_par_joint($table, $champ, $boucle, $idb);
443 return (array('zbug_critere_inconnu', array('critere' => $crit->op
. " $par")));
445 else if ($par == 'date'
446 AND isset($GLOBALS['table_date'][$boucle->type_requete
])) {
447 $m = $GLOBALS['table_date'][$boucle->type_requete
];
448 $order = "'".$boucle->id_table
."." . $m . "'";
450 // par champ. Verifier qu'ils sont presents.
451 elseif (preg_match("/^([^,]*)\.(.*)$/", $par, $r)) {
452 // cas du tri sur champ de jointure explicite
453 $t = array_search($r[1], $boucle->from
);
455 $t = trouver_champ_exterieur($r[2], array($r[1]), $boucle);
456 $t = array_search(@$t[0], $boucle->from
);
459 return (array('zbug_critere_inconnu', array('critere' => $crit->op
. " $par")));
460 } else $order = "'" . $t . '.' . $r[2] . "'";
462 $desc = $boucle->show
;
463 if ($desc['field'][$par])
464 $par = $boucle->id_table
.".".$par;
465 // sinon tant pis, ca doit etre un champ synthetise (cf points)
471 if (preg_match('/^\'([^"]*)\'$/', $order, $m)) {
473 if (strpos($t,'.') AND !in_array($t, $boucle->select
)) {
474 $boucle->select
[] = $t;
479 if (preg_match("/^\s*'(.*)'\s*$/", $order, $r))
480 $order = "'$fct(" . $r[1] . ")'";
481 else $order = "'$fct(' . $order . ')'";
483 $t = $order . $collecte . $sens;
484 if (preg_match("/^(.*)'\s*\.\s*'([^']*')$/", $t, $r))
486 $boucle->order
[] = $t;
490 // http://doc.spip.org/@critere_par_joint
491 function critere_par_joint($table, $champ, &$boucle, $idb)
493 $t = array_search($table, $boucle->from
);
494 if (!$t) $t = trouver_jointure_champ($champ, $boucle);
495 return !$t ?
'' : ("'" . $t . '.' . $champ . "'");
499 // http://www.spip.net/@inverse
501 // http://doc.spip.org/@critere_inverse_dist
502 function critere_inverse_dist($idb, &$boucles, $crit) {
504 $boucle = &$boucles[$idb];
505 // Classement par ordre inverse
507 critere_parinverse($idb, $boucles, $crit);
511 // Classement par ordre inverse fonction eventuelle de #ENV{...}
512 if (isset($crit->param
[0])){
513 $critere = calculer_liste($crit->param
[0], array(), $boucles, $boucles[$idb]->id_parent
);
514 $order = "(($critere)?' DESC':'')";
517 $n = count($boucle->order
);
519 if (isset($boucle->default_order
[0]))
520 $boucle->default_order
[0] .= ' . " DESC"';
522 $boucle->default_order
[] = ' DESC';
525 $t = $boucle->order
[$n-1] . " . $order";
526 if (preg_match("/^(.*)'\s*\.\s*'([^']*')$/", $t, $r))
528 $boucle->order
[$n-1] = $t;
533 // http://doc.spip.org/@critere_agenda_dist
534 function critere_agenda_dist($idb, &$boucles, $crit)
536 $params = $crit->param
;
538 if (count($params) < 1)
539 return array('zbug_critere_inconnu', array('critere' => $crit->op
. " ?"));
541 $boucle = &$boucles[$idb];
542 $parent = $boucle->id_parent
;
543 $fields = $boucle->show
['field'];
545 $date = array_shift($params);
546 $type = array_shift($params);
548 // la valeur $type doit etre connue a la compilation
549 // donc etre forcement reduite a un litteral unique dans le source
551 $type = $type[0]->texte
;
553 // La valeur date doit designer un champ de la table SQL.
554 // Si c'est un litteral unique dans le source, verifier a la compil,
555 // sinon synthetiser le test de verif pour execution ulterieure
556 // On prendra arbitrairement le premier champ si test negatif.
558 if ((count($date) == 1) AND ($date[0]->type
== 'texte')) {
559 $date = $date[0]->texte
;
560 if (!isset($fields[$date]))
561 return array('zbug_critere_inconnu', array('critere' => $crit->op
. " " . $date));
563 $a = calculer_liste($date, array(), $boucles, $parent);
564 $noms = array_keys($fields);
566 $noms = join(" ", $noms);
567 # bien laisser 2 espaces avant $nom pour que strpos<>0
568 $cond = "(\$a=strval($a))AND\nstrpos(\" $noms \",\" \$a \")";
569 $date = "'.(($cond)\n?\$a:\"$defaut\").'";
571 $annee = $params ?
array_shift($params) : "";
572 $annee = "\n" . 'sprintf("%04d", ($x = ' .
573 calculer_liste($annee, array(), $boucles, $parent) .
574 ') ? $x : date("Y"))';
576 $mois = $params ?
array_shift($params) : "";
577 $mois = "\n" . 'sprintf("%02d", ($x = ' .
578 calculer_liste($mois, array(), $boucles, $parent) .
579 ') ? $x : date("m"))';
581 $jour = $params ?
array_shift($params) : "";
582 $jour = "\n" . 'sprintf("%02d", ($x = ' .
583 calculer_liste($jour, array(), $boucles, $parent) .
584 ') ? $x : date("d"))';
586 $annee2 = $params ?
array_shift($params) : "";
587 $annee2 = "\n" . 'sprintf("%04d", ($x = ' .
588 calculer_liste($annee2, array(), $boucles, $parent) .
589 ') ? $x : date("Y"))';
591 $mois2 = $params ?
array_shift($params) : "";
592 $mois2 = "\n" . 'sprintf("%02d", ($x = ' .
593 calculer_liste($mois2, array(), $boucles, $parent) .
594 ') ? $x : date("m"))';
596 $jour2 = $params ?
array_shift($params) : "";
597 $jour2 = "\n" . 'sprintf("%02d", ($x = ' .
598 calculer_liste($jour2, array(), $boucles, $parent) .
599 ') ? $x : date("d"))';
601 $date = $boucle->id_table
. ".$date";
604 $boucle->where
[]= array("'='", "'DATE_FORMAT($date, \'%Y%m%d\')'",
605 ("$annee . $mois . $jour"));
606 elseif ($type == 'mois')
607 $boucle->where
[]= array("'='", "'DATE_FORMAT($date, \'%Y%m\')'",
609 elseif ($type == 'semaine')
610 $boucle->where
[]= array("'AND'",
612 "'DATE_FORMAT($date, \'%Y%m%d\')'",
613 ("date_debut_semaine($annee, $mois, $jour)")),
615 "'DATE_FORMAT($date, \'%Y%m%d\')'",
616 ("date_fin_semaine($annee, $mois, $jour)")));
617 elseif (count($crit->param
) > 2)
618 $boucle->where
[]= array("'AND'",
620 "'DATE_FORMAT($date, \'%Y%m%d\')'",
621 ("$annee . $mois . $jour")),
622 array("'<='", "'DATE_FORMAT($date, \'%Y%m%d\')'", ("$annee2 . $mois2 . $jour2")));
623 // sinon on prend tout
626 // http://doc.spip.org/@calculer_critere_parties
627 function calculer_critere_parties($idb, &$boucles, $crit) {
628 $boucle = &$boucles[$idb];
629 $a1 = $crit->param
[0];
630 $a2 = $crit->param
[1];
633 list($a11,$a12) = calculer_critere_parties_aux($idb, $boucles, $a1);
634 list($a21,$a22) = calculer_critere_parties_aux($idb, $boucles, $a2);
635 if (($op== ',')&&(is_numeric($a11) && (is_numeric($a21))))
636 $boucle->limit
= $a11 .',' . $a21;
638 $boucle->total_parties
= ($a21 != 'n') ?
$a21 : $a22;
639 $partie = ($a11 != 'n') ?
$a11 : $a12;
640 $mode = (($op == '/') ?
'/' :
641 (($a11=='n') ?
'-' : '+').(($a21=='n') ?
'-' : '+'));
642 calculer_parties($boucles, $idb, $partie, $mode);
647 // Code specifique aux criteres {pagination}, {1,n} {n/m} etc
650 function calculer_parties(&$boucles, $id_boucle, $debut, $mode) {
652 $total_parties = $boucles[$id_boucle]->total_parties
;
653 preg_match(",([+-/p])([+-/])?,", $mode, $regs);
654 list(,$op1,$op2) = $regs;
655 $nombre_boucle = "\$Numrows['$id_boucle']['total']";
658 $pmoins1 = is_numeric($debut) ?
($debut-1) : "($debut-1)";
659 $totpos = is_numeric($total_parties) ?
($total_parties) :
660 "($total_parties ? $total_parties : 1)";
661 $fin = "ceil(($nombre_boucle * $debut )/$totpos) - 1";
662 $debut = !$pmoins1 ?
0 : "ceil(($nombre_boucle * $pmoins1)/$totpos);";
665 if ($op1 == '-') $debut = "$nombre_boucle - $debut;";
669 $fin = '$debut_boucle + '.$nombre_boucle.' - '
670 . (is_numeric($total_parties) ?
($total_parties+
1) :
671 ($total_parties . ' - 1'));
673 // {x,1} ou {pagination}
674 $fin = '$debut_boucle'
675 . (is_numeric($total_parties) ?
676 (($total_parties==1) ?
"" :(' + ' . ($total_parties-1))):
677 ('+' . $total_parties . ' - 1'));
680 // {pagination}, gerer le debut_xx=-1 pour tout voir
682 $debut .= ";\n \$debut_boucle = ((\$tout=(\$debut_boucle == -1))?0:(\$debut_boucle))";
683 $debut .= ";\n \$debut_boucle = max(0,min(\$debut_boucle,floor(($nombre_boucle-1)/($total_parties))*($total_parties)))";
684 $fin = "(\$tout ? $nombre_boucle : $fin)";
689 // $debut_boucle et $fin_boucle sont les indices SQL du premier
690 // et du dernier demandes dans la boucle : 0 pour le premier,
691 // n-1 pour le dernier ; donc total_boucle = 1 + debut - fin
692 // Utiliser min pour rabattre $fin_boucle sur total_boucle.
694 $boucles[$id_boucle]->mode_partie
= "\n\t"
695 . '$debut_boucle = ' . $debut . ";\n "
696 . '$fin_boucle = min(' . $fin . ", \$Numrows['$id_boucle']['total'] - 1);\n "
697 . '$Numrows[\''.$id_boucle. "']['grand_total'] = \$Numrows['$id_boucle']['total'];\n "
698 . '$Numrows[\''.$id_boucle.'\']["total"] = max(0,$fin_boucle - $debut_boucle + 1);'
699 . "\n\tif (\$debut_boucle>0 AND \$debut_boucle < \$Numrows['$id_boucle']['grand_total'] AND sql_seek(\$result,\$debut_boucle,"._q($boucles[$id_boucle]->sql_serveur
).",'continue'))\n\t\t\$Numrows['$id_boucle']['compteur_boucle'] = \$debut_boucle;\n\t";
701 $boucles[$id_boucle]->partie
= "
702 if (\$Numrows['$id_boucle']['compteur_boucle'] <= \$debut_boucle) continue;
703 if (\$Numrows['$id_boucle']['compteur_boucle']-1 > \$fin_boucle) break;";
706 // http://doc.spip.org/@calculer_critere_parties_aux
707 function calculer_critere_parties_aux($idb, &$boucles, $param) {
708 if ($param[0]->type
!= 'texte')
710 $a1 = calculer_liste(array($param[0]), array('id_mere' => $idb), $boucles, $boucles[$idb]->id_parent
);
711 preg_match(',^ *(-([0-9]+))? *$,', $param[1]->texte
, $m);
712 return array("intval($a1)", ($m[2] ?
$m[2] : 0));
714 preg_match(',^ *(([0-9]+)|n) *(- *([0-9]+)? *)?$,', $param[0]->texte
, $m);
717 return array($a1, 0);
719 return array($a1, $m[4]);
720 else return array($a1,
721 calculer_liste(array($param[1]), array(), $boucles[$idb]->id_parent
, $boucles));
726 // La fonction d'aiguillage sur le nom du critere dans leur liste
727 // Si l'une au moins des fonctions associees retourne une erreur
728 // (i.e. un tableau), on propage l'information
729 // Sinon, ne retourne rien (affectation directe dans l'arbre)
731 // http://doc.spip.org/@calculer_criteres
732 function calculer_criteres ($idb, &$boucles)
735 $boucle = $boucles[$idb];
736 $table = strtoupper($boucle->id_table
);
737 $defaut = charger_fonction('DEFAUT', 'calculer_critere');
738 // s'il y avait une erreur de syntaxe, propager cette info
739 if (!is_array($boucle->criteres
)) return array();
740 foreach($boucle->criteres
as $crit) {
741 $critere = $crit->op
;
742 // critere personnalise ?
743 if ((!function_exists($f="critere_".$table."_".$critere))
744 AND (!function_exists($f=$f."_dist"))
745 AND (!function_exists($f="critere_".$critere))
746 AND (!function_exists($f=$f."_dist")) ) {
747 // fonction critere standard
750 // compile le critere
751 $res = $f($idb, $boucles, $crit);
753 // Gestion centralisee des erreurs pour pouvoir propager
754 if (is_array($res)) {
756 erreur_squelette($msg, $boucle);
762 // Madeleine de Proust, revision MIT-1958 sqq, revision CERN-1989
763 // hum, c'est kwoi cette fonxion ?
764 // http://doc.spip.org/@kwote
765 function kwote($lisp)
767 if (preg_match(_CODE_QUOTE
, $lisp, $r))
768 return $r[1] . "\"" . sql_quote(str_replace(array("\\'","\\\\"),array("'","\\"),$r[2])) . "\"" ;
770 return "sql_quote($lisp)";
773 // Si on a une liste de valeurs dans #ENV{x}, utiliser la double etoile
774 // pour faire par exemple {id_article IN #ENV**{liste_articles}}
775 // http://doc.spip.org/@critere_IN_dist
776 function critere_IN_dist ($idb, &$boucles, $crit)
778 $r = calculer_critere_infixe($idb, $boucles, $crit);
780 return (array('zbug_critere_inconnu', array('critere' => $crit->op
. " ?")));
782 list($arg, $op, $val, $col, $where_complement) = $r;
784 $in = critere_IN_cas($idb, $boucles, $crit->not ?
'NOT' : ($crit->exclus?
'exclus' : ''), $arg, $op, $val, $col);
785 // inserer la condition; exemple: {id_mot ?IN (66, 62, 64)}
788 $pred = calculer_argument_precedent($idb, $col, $boucles);
789 $where = array("'?'",$pred, $where,"''");
790 if ($where_complement) // condition annexe du type "AND (objet='article')"
791 $where_complement = array("'?'",$pred, $where_complement,"''");
794 if (!preg_match(",^L[0-9]+[.],",$arg))
795 $where = array("'NOT'", $where);
797 // un not sur un critere de jointure se traduit comme un NOT IN avec une sous requete
798 $where = array("'NOT'",array("'IN'","'".$boucles[$idb]->id_table
.".".$boucles[$idb]->primary
."'" ,array("'SELF'","'".$boucles[$idb]->id_table
.".".$boucles[$idb]->primary
."'",$where)));
800 $boucles[$idb]->where
[] = $where;
801 if ($where_complement) // condition annexe du type "AND (objet='article')"
802 $boucles[$idb]->where
[]= $where_complement;
805 // http://doc.spip.org/@critere_IN_cas
806 function critere_IN_cas ($idb, &$boucles, $crit2, $arg, $op, $val, $col)
808 static $num = array();
809 $descr = $boucles[$idb]->descr
;
810 $cpt = &$num[$descr['nom']][$descr['gram']][$idb];
812 $var = '$in' . $cpt++
;
813 $x= "\n\t$var = array();";
814 foreach ($val as $k => $v) {
815 if (preg_match(",^(\n//.*\n)?'(.*)'$,", $v, $r)) {
816 // optimiser le traitement des constantes
817 if (is_numeric($r[2]))
818 $x .= "\n\t$var" . "[]= $r[2];";
820 $x .= "\n\t$var" . "[]= " . sql_quote($r[2]) . ";";
822 // Pour permettre de passer des tableaux de valeurs
823 // on repere l'utilisation brute de #ENV**{X},
824 // c'est-a-dire sa traduction en ($PILE[0][X]).
825 // et on deballe mais en rajoutant l'anti XSS
826 $x .= "\n\tif (!(is_array(\$a = ($v))))\n\t\t$var" ."[]= \$a;\n\telse $var = array_merge($var, \$a);";
830 $boucles[$idb]->in
.= $x;
832 // inserer le tri par defaut selon les ordres du IN ...
833 // avec une ecriture de type FIELD qui degrade les performances (du meme ordre qu'un rexgexp)
834 // et que l'on limite donc strictement aux cas necessaires :
835 // si ce n'est pas un !IN, et si il n'y a pas d'autre order dans la boucle
837 $boucles[$idb]->default_order
[] = "((!sql_quote($var) OR sql_quote($var)===\"''\") ? 0 : ('FIELD($arg,' . sql_quote($var) . ')'))";
840 return "sql_in('$arg',sql_quote($var)".($crit2=='NOT'?
",'NOT'":"").")";
843 # Criteres de comparaison
845 // http://doc.spip.org/@calculer_critere_DEFAUT
846 function calculer_critere_DEFAUT_dist($idb, &$boucles, $crit)
848 // double cas particulier {0,1} et {1/2} repere a l'analyse lexicale
849 if (($crit->op
== ",") OR ($crit->op
== '/'))
850 return calculer_critere_parties($idb, $boucles, $crit);
852 $r = calculer_critere_infixe($idb, $boucles, $crit);
855 return (array('zbug_critere_inconnu', array('critere' => $crit->op
)));
856 } else calculer_critere_DEFAUT_args($idb, $boucles, $crit, $r);
859 function calculer_critere_DEFAUT_args($idb, &$boucles, $crit, $args)
861 list($arg, $op, $val, $col, $where_complement) = $args;
863 $where = array("'$op'", "'$arg'", $val[0]);
865 // inserer la negation (cf !...)
867 if ($crit->not
) $where = array("'NOT'", $where);
869 if (!preg_match(",^L[0-9]+[.],",$arg))
870 $where = array("'NOT'", $where);
872 // un not sur un critere de jointure se traduit comme un NOT IN avec une sous requete
873 $where = array("'NOT'",array("'IN'","'".$boucles[$idb]->id_table
.".".$boucles[$idb]->primary
."'" ,array("'SELF'","'".$boucles[$idb]->id_table
.".".$boucles[$idb]->primary
."'",$where)));
875 // inserer la condition (cf {lang?})
876 // traiter a part la date, elle est mise d'office par SPIP,
878 $pred = calculer_argument_precedent($idb, $col, $boucles);
879 if ($col == "date" OR $col == "date_redac") {
880 if($pred == "\$Pile[0]['".$col."']") {
881 $pred = "(\$Pile[0]['{$col}_default']?'':$pred)";
885 if ($op == '=' AND !$crit->not
)
886 $where = array("'?'", "(is_array($pred))",
887 critere_IN_cas ($idb, $boucles, 'COND', $arg, $op, array($pred), $col),
889 $where = array("'?'", "!(is_array($pred)?count($pred):strlen($pred))","''", $where);
890 if ($where_complement) // condition annexe du type "AND (objet='article')"
891 $where_complement = array("'?'", "!$pred","''", $where_complement);
894 $boucles[$idb]->where
[]= $where;
895 if ($where_complement) // condition annexe du type "AND (objet='article')"
896 $boucles[$idb]->where
[]= $where_complement;
899 // http://doc.spip.org/@calculer_critere_infixe
900 function calculer_critere_infixe($idb, &$boucles, $crit) {
902 global $table_criteres_infixes;
903 global $exceptions_des_jointures, $exceptions_des_tables;
905 $boucle = &$boucles[$idb];
906 $type = $boucle->type_requete
;
907 $table = $boucle->id_table
;
908 $desc = $boucle->show
;
911 list($fct, $col, $op, $val, $args_sql) =
912 calculer_critere_infixe_ops($idb, $boucles, $crit);
915 $where_complement =false;
917 // Cas particulier : id_enfant => utiliser la colonne id_objet
918 if ($col == 'id_enfant')
919 $col = $boucle->primary
;
921 // Cas particulier : id_parent => verifier les exceptions de tables
922 if ($col == 'id_parent')
923 $col = isset($exceptions_des_tables[$table]['id_parent']) ?
924 $exceptions_des_tables[$table]['id_parent'] :
927 // Cas particulier : id_secteur pour certaines tables
928 else if (($col == 'id_secteur')&&($type == 'breves')) {
929 $col = 'id_rubrique';
931 // et possibilite de gerer un critere secteur sur des tables de plugins (ie forums)
932 else if (($col == 'id_secteur') AND ($critere_secteur = charger_fonction("critere_secteur_$type","public",true))) {
933 $table = $critere_secteur($idb, $boucles, $val, $crit);
936 // cas id_article=xx qui se mappe en id_objet=xx AND objet=article
937 else if (count(trouver_champs_decomposes($col,$desc))>1){
938 $e = decompose_champ_id_objet($col);
939 $col = array_shift($e);
940 $where_complement = primary_doublee($e, $table);
943 // Cas particulier : expressions de date
944 else if ($date = tester_param_date($boucle->type_requete
, $col)) {
945 $col = calculer_critere_infixe_date($idb, $boucles, $date);
948 else if (preg_match('/^(.*)\.(.*)$/', $col, $r)) {
949 list(,$table, $col) = $r;
951 $table = calculer_critere_externe_init($boucle, array($table), $col, $desc, ($crit->cond
OR $op !='='), true);
952 if (!$table) return '';
954 elseif (@!array_key_exists($col, $desc['field'])) {
955 $r = calculer_critere_infixe_externe($boucle, $crit, $op, $desc, $col, $col_alias, $table);
957 list($col, $col_alias, $table, $where_complement) = $r;
959 // Si la colonne SQL est numerique ou le critere est une date relative
960 // virer les guillemets eventuels qui sont refuses par certains SQL
961 // Ne pas utiliser intval, PHP tronquant les Bigint de SQL
963 if (($op == '=' OR in_array($op, $table_criteres_infixes))
964 AND (($desc AND isset($desc['field'][$col]) AND sql_test_int($desc['field'][$col]))
965 OR ($date AND strpos($date[0], '_relatif')))) {
966 if (preg_match("/^\"'(-?\d+)'\"$/", $val[0], $r))
968 elseif (preg_match('/^sql_quote[(](.*?)(,[^)]*)?[)]\s*$/', $val[0], $r)) {
969 $r = $r[1] . ($r[2] ?
$r[2] : ",''") . ",'int'";
970 $val[0] = "sql_quote($r)";
973 // Indicateur pour permettre aux fonctionx boucle_X de modifier
974 // leurs requetes par defaut, notamment le champ statut
975 // Ne pas confondre champs de la table principale et des jointures
976 if ($table === $boucle->id_table
) {
977 $boucles[$idb]->modificateur
['criteres'][$col] = true;
978 if ($col_alias!=$col)
979 $boucles[$idb]->modificateur
['criteres'][$col_alias] = true;
982 // ajout pour le cas special d'une condition sur le champ statut:
983 // il faut alors interdire a la fonction de boucle
984 // de mettre ses propres criteres de statut
985 // http://www.spip.net/@statut (a documenter)
986 // garde pour compatibilite avec code des plugins anterieurs, mais redondant avec la ligne precedente
987 if ($col == 'statut') $boucles[$idb]->statut
= true;
989 // ajout pour le cas special des forums
990 // il faut alors interdire a la fonction de boucle sur forum
991 // de selectionner uniquement les forums sans pere
993 elseif ($boucles[$idb]->type_requete
== 'forums' AND
994 ($col == 'id_parent' OR $col == 'id_forum'))
995 $boucles[$idb]->modificateur
['plat'] = true;
996 // inserer le nom de la table SQL devant le nom du champ
999 $arg = "$table." . substr($col,1,-1);
1000 else $arg = "$table.$col";
1003 // inserer la fonction SQL
1004 if ($fct) $arg = "$fct($arg$args_sql)";
1006 return array($arg, $op, $val, $col_alias, $where_complement);
1009 function calculer_critere_infixe_externe(&$boucle, $crit, $op, $desc, $col, $col_alias, $table)
1011 global $exceptions_des_jointures;
1014 $calculer_critere_externe = 'calculer_critere_externe_init';
1015 // gestion par les plugins des jointures tordues
1016 // pas automatiques mais necessaires
1017 if (is_array($exceptions_des_jointures[$table])) {
1019 $t = $exceptions_des_jointures[$table];
1020 $index = isset($t[$col])
1021 ?
$t[$col] : (isset($t['']) ?
$t[''] : array());
1023 if (count($index)==3)
1024 list($t, $col, $calculer_critere_externe) = $index;
1025 elseif (count($index)==2)
1026 list($t, $col) = $t[$col];
1027 elseif (count($index)==1){
1028 list($calculer_critere_externe) = $index;
1032 $t=''; // jointure non declaree. La trouver.
1034 elseif (isset($exceptions_des_jointures[$col]))
1035 list($t, $col) = $exceptions_des_jointures[$col];
1036 else $t =''; // jointure non declaree. La trouver.
1038 $table = $calculer_critere_externe($boucle, $boucle->jointures
, $col, $desc, ($crit->cond
OR $op !='='), $t);
1040 if (!$table) return '';
1042 list($nom, $desc) = trouver_champ_exterieur($col, $boucle->jointures
, $boucle);
1043 if (count(trouver_champs_decomposes($col,$desc))>1){
1044 $col_alias = $col; // id_article devient juste le nom d'origine
1045 $e = decompose_champ_id_objet($col);
1046 $col = array_shift($e);
1047 $where = primary_doublee($e, $table);
1050 return array($col, $col_alias, $table, $where);
1053 // Ne pas appliquer sql_quote lors de la compilation,
1054 // car on ne connait pas le serveur SQL, donc s'il faut \' ou ''
1056 // http://doc.spip.org/@primary_doublee
1057 function primary_doublee($decompose, $table)
1059 $e1 = reset($decompose);
1060 $e2 = "sql_quote('" . end($decompose) ."')";
1061 return array("'='","'$table.". $e1 ."'",$e2);
1064 // Faute de copie du champ id_secteur dans la table des forums,
1065 // faut le retrouver par jointure
1066 // Pour chaque Row il faudrait tester si le forum est
1067 // d'article, de breve, de rubrique, ou de syndication.
1068 // Pour le moment on ne traite que les articles,
1069 // les 3 autres cas ne marcheront donc pas: ca ferait 4 jointures
1070 // qu'il faut traiter optimalement ou alors pas du tout.
1072 // http://doc.spip.org/@critere_secteur_forum
1073 function critere_secteur_forum($idb, &$boucles, $val, $crit)
1075 static $trouver_table;
1076 if (!$trouver_table)
1077 $trouver_table = charger_fonction('trouver_table', 'base');
1079 $desc = $trouver_table('articles', $boucles[$idb]->sql_serveur
);
1080 return calculer_critere_externe_init($boucles[$idb], array($desc['table']), 'id_secteur', $desc, $crit->cond
, true);
1083 // Champ hors table, ca ne peut etre qu'une jointure.
1084 // On cherche la table du champ et on regarde si elle est deja jointe
1085 // Si oui et qu'on y cherche un champ nouveau, pas de jointure supplementaire
1086 // Exemple: criteres {titre_mot=...}{type_mot=...}
1087 // Dans les 2 autres cas ==> jointure
1088 // (Exemple: criteres {type_mot=...}{type_mot=...} donne 2 jointures
1089 // pour selectioner ce qui a exactement ces 2 mots-cles.
1091 // http://doc.spip.org/@calculer_critere_externe_init
1092 function calculer_critere_externe_init(&$boucle, $joints, $col, $desc, $eg, $checkarrivee = false)
1094 $cle = trouver_champ_exterieur($col, $joints, $boucle, $checkarrivee);
1095 if (!$cle) return '';
1096 $t = array_search($cle[0], $boucle->from
);
1097 // transformer eventuellement id_xx en (id_objet,objet)
1098 $cols = trouver_champs_decomposes($col,$cle[1]);
1101 foreach($cols as $col){
1102 $c = '/\b' . $t . ".$col" . '\b/';
1103 if (trouver_champ($c, $boucle->where
)) $joindre = true;
1105 // mais ca peut etre dans le FIELD pour le Having
1106 $c = "/FIELD.$t" .".$col,/";
1107 if (trouver_champ($c, $boucle->select
)) $joindre = true;
1110 if (!$joindre) return $t;
1112 return calculer_jointure($boucle, array($boucle->id_table
, $desc), $cle, $cols, $eg);
1116 // http://doc.spip.org/@trouver_champ
1117 function trouver_champ($champ, $where)
1119 if (!is_array($where))
1120 return preg_match($champ,$where);
1122 foreach ($where as $clause) {
1123 if (trouver_champ($champ, $clause)) return true;
1130 // determine l'operateur et les operandes
1132 // http://doc.spip.org/@calculer_critere_infixe_ops
1133 function calculer_critere_infixe_ops($idb, &$boucles, $crit)
1135 // cas d'une valeur comparee a elle-meme ou son referent
1136 if (count($crit->param
) == 0)
1138 $col = $val = $crit->op
;
1139 if (preg_match('/^(.*)\.(.*)$/', $col, $r)) $val = $r[2];
1140 // Cas special {lang} : aller chercher $GLOBALS['spip_lang']
1142 $val = array(kwote('$GLOBALS[\'spip_lang\']'));
1144 // Si id_parent, comparer l'id_parent avec l'id_objet
1145 // de la boucle superieure.... faudrait verifier qu'il existe
1146 // pour eviter l'erreur SQL
1147 if ($val == 'id_parent')
1148 $val = $boucles[$idb]->primary
;
1149 // Si id_enfant, comparer l'id_objet avec l'id_parent
1150 // de la boucle superieure
1151 else if ($val == 'id_enfant')
1153 // un critere conditionnel sur date est traite a part
1154 // car la date est mise d'office par SPIP,
1155 $val = calculer_argument_precedent($idb, $val, $boucles);
1156 if ($crit->cond
AND ($col == "date" OR $col == "date_redac")) {
1157 if($val == "\$Pile[0]['".$col."']") {
1158 $val = "(\$Pile[0]['{$col}_default']?'':$val)";
1161 $val = array(kwote($val));
1164 // comparaison explicite
1165 // le phraseur impose que le premier param soit du texte
1166 $params = $crit->param
;
1168 if ($op == '==') $op = 'REGEXP';
1169 $col = array_shift($params);
1170 $col = $col[0]->texte
;
1173 $desc = array('id_mere' => $idb);
1174 $parent = $boucles[$idb]->id_parent
;
1176 // Dans le cas {x=='#DATE'} etc, defaire le travail du phraseur,
1177 // celui ne sachant pas ce qu'est un critere infixe
1178 // et a fortiori son 2e operande qu'entoure " ou '
1179 if (count($params)==1
1180 AND count($params[0]==3)
1181 AND $params[0][0]->type
== 'texte'
1182 AND @$params[0][2]->type
== 'texte'
1183 AND ($p=$params[0][0]->texte
) == $params[0][2]->texte
1184 AND (($p == "'") OR ($p == '"'))
1185 AND $params[0][1]->type
== 'champ' ) {
1186 $val[]= "$p\\$p#" . $params[0][1]->nom_champ
. "\\$p$p";
1188 foreach ((($op != 'IN') ?
$params : calculer_vieux_in($params)) as $p) {
1189 $a = calculer_liste($p, $desc, $boucles, $parent);
1190 if ($op == 'IN') $val[]= $a;
1191 else $val[]=kwote($a);
1195 $fct = $args_sql = '';
1197 if (preg_match('/^(.*)' . SQL_ARGS
. '$/', $col, $m)) {
1199 preg_match('/^\(([^,]*)(.*)\)$/', $m[2], $a);
1201 if (preg_match('/^(\S*)(\s+AS\s+.*)$/i', $col, $m)) {
1205 $args_sql .= $a[2];;
1208 return array($fct, $col, $op, $val, $args_sql);
1211 // compatibilite ancienne version
1213 // http://doc.spip.org/@calculer_vieux_in
1214 function calculer_vieux_in($params)
1216 $deb = $params[0][0];
1217 $k = count($params)-1;
1218 $last = $params[$k];
1219 $j = count($last)-1;
1221 $n = strlen($last->texte
);
1223 if (!(($deb->texte
[0] == '(') && ($last->texte
[$n-1] == ')')))
1225 $params[0][0]->texte
= substr($deb->texte
,1);
1226 // attention, on peut avoir k=0,j=0 ==> recalculer
1227 $last = $params[$k][$j];
1228 $n = strlen($last->texte
);
1229 $params[$k][$j]->texte
= substr($last->texte
,0,$n-1);
1231 foreach($params as $v) {
1232 if ($v[0]->type
!= 'texte')
1235 foreach(explode(',', $v[0]->texte
) as $x) {
1238 $newp[] = array($t);
1245 // http://doc.spip.org/@calculer_critere_infixe_date
1246 function calculer_critere_infixe_date($idb, &$boucles, $regs)
1249 $boucle = $boucles[$idb];
1251 $date_orig = $pred = isset($table_date[$boucle->type_requete
])?
$table_date[$boucle->type_requete
]:'date';
1252 if (isset($regs[3]) AND $suite=$regs[3]) {
1253 # Recherche de l'existence du champ date_xxxx,
1254 # si oui choisir ce champ, sinon choisir xxxx
1256 if ($t['field']["date$suite"])
1257 $date_orig = 'date'.$suite;
1259 $date_orig = substr($suite, 1);
1263 if (isset($regs[2]) AND $rel=$regs[2]) $pred = 'date';
1265 $date_compare = "\"' . normaliser_date(" .
1266 calculer_argument_precedent($idb, $pred, $boucles) .
1268 $date_orig = $boucle->id_table
. '.' . $date_orig;
1275 $col = "DAYOFMONTH($date_orig)";
1278 $col = "MONTH($date_orig)";
1281 $col = "YEAR($date_orig)";
1284 $col = "DATE_FORMAT($date_orig, '%H:%i')";
1287 $col = calculer_param_date("NOW()", $date_orig);
1290 $col = calculer_param_date($date_compare, $date_orig);
1292 case 'jour_relatif':
1293 $col = "LEAST(TO_DAYS(" .$date_compare . ")-TO_DAYS(" .
1294 $date_orig . "), DAYOFMONTH(" . $date_compare .
1295 ")-DAYOFMONTH(" . $date_orig . ")+30.4368*(MONTH(" .
1296 $date_compare . ")-MONTH(" . $date_orig .
1297 "))+365.2422*(YEAR(" . $date_compare . ")-YEAR(" .
1300 case 'mois_relatif':
1301 $col = "MONTH(" . $date_compare . ")-MONTH(" .
1302 $date_orig . ")+12*(YEAR(" . $date_compare .
1303 ")-YEAR(" . $date_orig . "))";
1305 case 'annee_relatif':
1306 $col = "YEAR(" . $date_compare . ")-YEAR(" .
1313 // http://doc.spip.org/@calculer_param_date
1314 function calculer_param_date($date_compare, $date_orig) {
1315 if (preg_match(",'\" *\.(.*)\. *\"',", $date_compare, $r)) {
1316 $init = "'\" . (\$x = $r[1]) . \"'";
1317 $date_compare = '\'$x\'';
1320 $init = $date_compare;
1323 "LEAST((UNIX_TIMESTAMP(" .
1325 ")-UNIX_TIMESTAMP(" .
1327 "))/86400,\n\tTO_DAYS(" .
1331 "),\n\tDAYOFMONTH(" .
1335 ")+30.4368*(MONTH(" .
1339 "))+365.2422*(YEAR(" .
1346 // http://doc.spip.org/@tester_param_date
1347 function tester_param_date($type, $col)
1350 if (isset($table_date[$type])
1351 AND preg_match(",^((age|jour|mois|annee)_relatif|date|mois|annee|jour|heure|age)(_[a-z]+)?$,", $col, $regs))