3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2007 *
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 \***************************************************************************/
15 // Definition des {criteres} d'une boucle
18 if (!defined("_ECRIRE_INC_VERSION")) return;
21 // http://www.spip.net/@racine
22 // http://doc.spip.org/@critere_racine_dist
23 function critere_racine_dist($idb, &$boucles, $crit) {
25 $boucle = &$boucles[$idb];
28 erreur_squelette(_T('zbug_info_erreur_squelette'), $crit->op
);
30 $boucle->where
[]= array("'='", "'$boucle->id_table." . "id_parent'", 0);
34 // http://www.spip.net/@exclus
35 // http://doc.spip.org/@critere_exclus_dist
36 function critere_exclus_dist($idb, &$boucles, $crit) {
39 $boucle = &$boucles[$idb];
40 $id = $boucle->primary
;
43 erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
45 $arg = kwote(calculer_argument_precedent($idb, $id, $boucles));
46 $boucle->where
[]= array("'!='", "'$boucle->id_table." . "$id'", $arg);
49 // {doublons} ou {unique}
50 // http://www.spip.net/@doublons
51 // attention: boucle->doublons designe une variable qu'on affecte
52 // http://doc.spip.org/@critere_doublons_dist
53 function critere_doublons_dist($idb, &$boucles, $crit) {
54 $boucle = &$boucles[$idb];
55 if (!$boucle->primary
)
56 erreur_squelette(_T('zbug_doublon_table_sans_index'), "BOUCLE$idb");
57 $nom = !isset($crit->param
[0]) ?
"''" : calculer_liste($crit->param
[0], array(), $boucles, $boucles[$idb]->id_parent
);
58 // mettre un tableau pour que ce ne soit pas vu comme une constante
59 $boucle->where
[]= array("calcul_mysql_in('".$boucle->id_table
. '.' . $boucle->primary
.
62 ($crit->not ?
'' : ($boucle->doublons
. "[]= ")) .
64 $boucle->type_requete
.
66 ($nom == "''" ?
'' : " . $nom") .
68 ($crit->not ?
'' : 'NOT') .
70 # la ligne suivante avait l'intention d'eviter une collecte deja faite
71 # mais elle fait planter une boucle a 2 critere doublons:
72 # {!doublons A}{doublons B}
73 # (de http://article.gmane.org/gmane.comp.web.spip.devel/31034)
74 # if ($crit->not) $boucle->doublons = "";
78 // http://www.spip.net/@lang_select
79 // http://doc.spip.org/@critere_lang_select_dist
80 function critere_lang_select_dist($idb, &$boucles, $crit) {
81 if (!($param = $crit->param
[1][0]->texte
)) $param = 'oui';
82 if ($crit->not
) $param = ($param=='oui') ?
'non' : 'oui';
83 $boucle = &$boucles[$idb];
84 $boucle->lang_select
= $param;
88 // http://www.spip.net/@debut_
89 // http://doc.spip.org/@critere_debut_dist
90 function critere_debut_dist($idb, &$boucles, $crit) {
91 $boucle = &$boucles[$idb];
92 $boucle->limit
= 'intval($GLOBALS["debut' .
93 $crit->param
[0][0]->texte
.
95 $crit->param
[1][0]->texte
.
100 // {pagination #ENV{pages,5}} etc
101 // {pagination 20 #ENV{truc,chose}} pour utiliser la variable debut_#ENV{truc,chose}
102 // http://www.spip.net/@pagination
103 // http://doc.spip.org/@critere_pagination_dist
104 function critere_pagination_dist($idb, &$boucles, $crit) {
106 // definition de la taille de la page
107 $pas = !isset($crit->param
[0][0]) ?
"''" : calculer_liste(array($crit->param
[0][0]), array(), $boucles, $boucles[$idb]->id_parent
);
108 $debut = !isset($crit->param
[0][1]) ?
"'$idb'" : calculer_liste(array($crit->param
[0][1]), array(), $boucles, $boucles[$idb]->id_parent
);
109 $pas = ($pas== "''") ?
'10' : "((\$a = intval($pas)) ? \$a : 10)";
111 $boucle = &$boucles[$idb];
112 $boucle->mode_partie
= 'p+';
113 $boucle->partie
= 'intval(_request("debut".'.$debut.'))';
114 $boucle->modificateur
['debut_nom'] = $debut;
115 $boucle->total_parties
= $pas;
116 if (!isset($boucle->modificateur
['fragment']))
117 $boucle->modificateur
['fragment'] = 'fragment_'.$boucle->descr
['nom'].$idb;
121 // http://www.spip.net/@fragment
122 // http://doc.spip.org/@critere_fragment_dist
123 function critere_fragment_dist($idb, &$boucles, $crit) {
124 if (!($param = $crit->param
[0][0]->texte
))
125 $param = 'fragment_'.$boucle->descr
['nom'].$idb;
128 $boucle = &$boucles[$idb];
129 $boucle->modificateur
['fragment'] = $param;
134 // http://www.spip.net/@recherche
135 // http://doc.spip.org/@critere_recherche_dist
136 function critere_recherche_dist($idb, &$boucles, $crit) {
137 global $table_des_tables;
138 $boucle = &$boucles[$idb];
139 $t = $boucle->id_table
;
140 if (in_array($t,$table_des_tables))
143 // Ne pas executer la requete en cas de hash vide
146 list($rech_select, $rech_where) = prepare_recherche($GLOBALS["recherche"], "'.$boucle->primary
.'", "'.$boucle->id_table
.'", "'.$t.'", "'.$crit->cond
.'");
149 // Sauf si le critere est conditionnel {recherche ?}
154 $t = $boucle->id_table
. '.' . $boucle->primary
;
155 if (!in_array($t, $boucles[$idb]->select
))
156 $boucle->select
[]= $t; # pour postgres, neuneu ici
157 $boucle->select
[]= '$rech_select as points';
159 // et la recherche trouve
160 $boucle->where
[]= '$rech_where';
164 // http://www.spip.net/@traduction
165 // (id_trad>0 AND id_trad=id_trad(precedent))
166 // OR id_article=id_article(precedent)
167 // http://doc.spip.org/@critere_traduction_dist
168 function critere_traduction_dist($idb, &$boucles, $crit) {
169 $boucle = &$boucles[$idb];
170 $prim = $boucle->primary
;
171 $table = $boucle->id_table
;
172 $arg = kwote(calculer_argument_precedent($idb, 'id_trad', $boucles));
173 $dprim = kwote(calculer_argument_precedent($idb, $prim, $boucles));
177 array("'='", "'$table.id_trad'", 0),
178 array("'='", "'$table.$prim'", $dprim)
181 array("'>'", "'$table.id_trad'", 0),
182 array("'='", "'$table.id_trad'", $arg)
187 // {origine_traduction}
188 // (id_trad>0 AND id_article=id_trad) OR (id_trad=0)
189 // http://www.spip.net/@origine_traduction
190 // http://doc.spip.org/@critere_origine_traduction_dist
191 function critere_origine_traduction_dist($idb, &$boucles, $crit) {
192 $boucle = &$boucles[$idb];
193 $prim = $boucle->primary
;
194 $table = $boucle->id_table
;
198 array("'='", "'$table." . "id_trad'", "'$table.$prim'"),
199 array("'='", "'$table.id_trad'", "'0'")
201 $boucle->where
[]= ($crit->not ?
array("'NOT'", $c) : $c);
205 // http://www.spip.net/@meme_parent
206 // http://doc.spip.org/@critere_meme_parent_dist
207 function critere_meme_parent_dist($idb, &$boucles, $crit) {
208 $boucle = &$boucles[$idb];
209 $arg = kwote(calculer_argument_precedent($idb, 'id_parent', $boucles));
210 $mparent = $boucle->id_table
. '.id_parent';
212 if ($boucle->type_requete
== 'rubriques') {
213 $boucle->where
[]= array("'='", "'$mparent'", $arg);
215 } else if ($boucle->type_requete
== 'forums') {
216 $boucle->where
[]= array("'='", "'$mparent'", $arg);
217 $boucle->where
[]= array("'>'", "'$mparent'", 0);
218 $boucle->modificateur
['plat'] = true
;
219 } else erreur_squelette(_T('zbug_info_erreur_squelette'), "{meme_parent} BOUCLE$idb");
223 // http://www.spip.net/@branche
224 // http://doc.spip.org/@critere_branche_dist
225 function critere_branche_dist($idb, &$boucles, $crit) {
227 $boucle = &$boucles[$idb];
229 $arg = calculer_argument_precedent($idb, 'id_rubrique', $boucles);
231 $c = "calcul_mysql_in('" .
233 ".id_rubrique', calcul_branche($arg), '')";
234 if ($crit->cond
) $c = "($arg ? $c : 1)";
237 $boucle->where
[]= array("'NOT'", $c);
239 $boucle->where
[]= $c;
242 // {logo} liste les objets qui ont un logo
243 // http://doc.spip.org/@critere_logo_dist
244 function critere_logo_dist($idb, &$boucles, $crit) {
246 $boucle = &$boucles[$idb];
248 $c = "calcul_mysql_in('" .
249 $boucle->id_table
. '.' . $boucle->primary
250 . "', lister_objets_avec_logos(". $boucle->type_requete
."), '')";
251 if ($crit->cond
) $c = "($arg ? $c : 1)";
254 $boucle->where
[]= array("'NOT'", $c);
256 $boucle->where
[]= $c;
259 // c'est la commande SQL "GROUP BY"
260 // par exemple <boucle(articles){fusion lang}>
261 // http://doc.spip.org/@critere_fusion_dist
262 function critere_fusion_dist($idb,&$boucles, $crit) {
263 if (isset($crit->param
[0])) {
264 $x = $crit->param
[0];
265 if ($x[0]->type
== 'texte')
266 $boucles[$idb]->group
[] = $x[0]->texte
;
267 else $boucles[$idb]->group
[] = '".' . calculer_critere_arg_dynamique($idb, $boucles, $x) . '."';
269 erreur_squelette(_T('zbug_info_erreur_squelette'),
270 "{groupby ?} BOUCLE$idb");
273 // http://doc.spip.org/@calculer_critere_arg_dynamique
274 function calculer_critere_arg_dynamique($idb, &$boucles, $crit, $suffix='')
276 global $table_des_tables, $tables_des_serveurs_sql;
278 $boucle = $boucles[$idb];
280 $arg = calculer_liste($crit, array(), $boucles, $boucle->id_parent
);
281 $r = $boucle->type_requete
;
282 $s = $boucles[$idb]->sql_serveur
;
283 if (!$s) $s = 'localhost';
284 $t = $table_des_tables[$r];
285 // pour les tables non Spip
286 if (!$t) $t = $r; else $t = "spip_$t";
287 $desc = $tables_des_serveurs_sql[$s][$t];
289 if (is_array($desc['field'])){
290 $liste_field = implode(',',array_map('_q',array_keys($desc['field'])));
291 return "((\$x = preg_replace(\"/\\W/\",'',$arg)) ? ( in_array(\$x,array($liste_field)) ? ('$boucle->id_table.' . \$x$suffix):(\$x$suffix) ) : '')";
293 return "((\$x = preg_replace(\"/\\W/\",'',$arg)) ? ('$boucle->id_table.' . \$x$suffix) : '')";
297 // http://www.spip.net/@par
298 // http://doc.spip.org/@critere_par_dist
299 function critere_par_dist($idb, &$boucles, $crit) {
300 critere_parinverse($idb, $boucles, $crit, '') ;
303 // http://doc.spip.org/@critere_parinverse
304 function critere_parinverse($idb, &$boucles, $crit, $sens) {
305 global $table_des_tables, $tables_des_serveurs_sql, $exceptions_des_jointures;
306 $boucle = &$boucles[$idb];
307 if ($crit->not
) $sens = $sens ?
"" : " . ' DESC'";
309 foreach ($crit->param
as $tri) {
311 $fct = ""; // en cas de fonction SQL
312 // tris specifies dynamiquement
313 if ($tri[0]->type
!= 'texte') {
314 // on sait pas faire pour les serveurs externes. A revoir.
315 if (!$boucles[$idb]->sql_serveur
) {
316 // calculer le order dynamique qui verifie les champs
317 $order = calculer_critere_arg_dynamique($idb, $boucles, $tri, $sens);
318 // et ajouter un champ hasard dans le select pour supporter 'hasard' comme tri dynamique
319 if (spip_abstract_select(array("RAND()")))
322 $par = "MOD(".$boucle->id_table
.'.'.$boucle->primary
323 ." * UNIX_TIMESTAMP(),32767) & UNIX_TIMESTAMP()";
324 $boucle->select
[]= $par . " AS hasard";
327 $par = array_shift($tri);
330 if (ereg("^multi[[:space:]]*(.*)$",$par, $m)) {
331 $texte = $boucle->id_table
. '.' . trim($m[1]);
332 $boucle->select
[] = " \".creer_objet_multi('".$texte."', \$GLOBALS['spip_lang']).\"" ;
334 // par num champ(, suite)
335 } else if (ereg("^num[[:space:]]*(.*)$",$par, $m)) {
336 $texte = '0+' . $boucle->id_table
. '.' . trim($m[1]);
337 $suite = calculer_liste($tri, array(), $boucles, $boucle->id_parent
);
339 $texte = "\" . ((\$x = $suite) ? ('$texte' . \$x) : '0')" . " . \"";
340 $as = 'num' .($boucle->order ?
count($boucle->order
) : "");
341 $boucle->select
[] = $texte . " AS $as";
344 if (!preg_match(",^" . CHAMP_SQL_PLUS_FONC
. '$,is', $par, $match))
345 erreur_squelette(_T('zbug_info_erreur_squelette'), "{par $par} BOUCLE$idb");
347 if ($match[2]) { $par = substr($match[2],1,-1); $fct = $match[1]; }
349 if ($par == 'hasard') {
350 // tester si cette version de MySQL accepte la commande RAND()
351 // sinon faire un gloubi-boulga maison avec de la mayonnaise.
352 if (spip_abstract_select(array("RAND()")))
355 $par = "MOD(".$boucle->id_table
.'.'.$boucle->primary
356 ." * UNIX_TIMESTAMP(),32767) & UNIX_TIMESTAMP()";
357 $boucle->select
[]= $par . " AS alea";
361 // (date la plus recente d'un message dans un fil de discussion)
362 else if ($par == 'date_thread') {
363 if ($boucle->type_requete
== 'forums') {
366 $t = critere_par_jointure($boucle, array('spip_forum','id_thread'));
367 $t = substr($t, 1, strpos($t,'.')-1);
369 $boucle->select
[] = "MAX($t" . ".".
370 $GLOBALS['table_date']['forums']
372 $boucle->group
[] = $t . ".id_thread";
373 $order = "'date_thread'";
374 $boucle->modificateur
['plat'] = true
;
376 // par titre_mot ou type_mot voire d'autres
377 else if (isset($exceptions_des_jointures[$par])) {
378 $order = critere_par_jointure($boucle, $exceptions_des_jointures[$par]);
380 else if ($par == 'date'
381 AND isset($GLOBALS['table_date'][$boucle->type_requete
])) {
382 $m = $GLOBALS['table_date'][$boucle->type_requete
];
383 $order = "'".$boucle->id_table
."." . $m . "'";
385 // par champ. Verifier qu'ils sont presents.
387 $r = $boucle->type_requete
;
388 $s = $boucles[$idb]->sql_serveur
;
389 if (!$s) $s = 'localhost';
390 $t = $table_des_tables[$r];
391 // pour les tables non Spip
392 if (!$t) $t = $r; else $t = "spip_$t";
393 $desc = $tables_des_serveurs_sql[$s][$t];
394 if ($desc['field'][$par])
395 $par = $boucle->id_table
.".".$par;
396 // sinon tant pis, ca doit etre un champ synthetise (cf points)
403 $boucle->order
[] = ($fct ?
"'$fct(' . $order . ')'" : $order) .
404 (($order[0]=="'") ?
$sens : "");
408 // http://doc.spip.org/@critere_par_jointure
409 function critere_par_jointure(&$boucle, $join)
411 global $table_des_tables;
412 list($table, $champ) = $join;
413 $t = array_search($table, $boucle->from
);
415 $type = $boucle->type_requete
;
416 $nom = $table_des_tables[$type];
417 list($nom, $desc) = trouver_def_table($nom ?
$nom : $type, $boucle);
419 $cle = trouver_champ_exterieur($champ, $boucle->jointures
, $boucle);
421 $cle = calculer_jointure($boucle, array($boucle->id_table
, $desc), $cle, false
);
422 if ($cle) $t = "L$cle";
423 else erreur_squelette(_T('zbug_info_erreur_squelette'), "{par ?} BOUCLE$idb");
426 return "'" . $t . '.' . $champ . "'";
430 // http://www.spip.net/@inverse
432 // http://doc.spip.org/@critere_inverse_dist
433 function critere_inverse_dist($idb, &$boucles, $crit) {
435 $boucle = &$boucles[$idb];
436 // Classement par ordre inverse
438 critere_parinverse($idb, $boucles, $crit, " . ' DESC'");
442 // Classement par ordre inverse fonction eventuelle de #ENV{...}
443 if (isset($crit->param
[0])){
444 $critere = calculer_liste($crit->param
[0], array(), $boucles, $boucles[$idb]->id_parent
);
445 $order = "(($critere)?' DESC':'')";
448 $n = count($boucle->order
);
450 $boucle->order
[$n-1] .= " . $order";
452 $boucle->default_order
[] = ' DESC';
456 // http://doc.spip.org/@critere_agenda_dist
457 function critere_agenda_dist($idb, &$boucles, $crit)
459 $params = $crit->param
;
461 if (count($params) < 1)
462 erreur_squelette(_T('zbug_info_erreur_squelette'),
463 "{agenda ?} BOUCLE$idb");
465 $parent = $boucles[$idb]->id_parent
;
467 // les valeurs $date et $type doivent etre connus a la compilation
468 // autrement dit ne pas etre des champs
470 $date = array_shift($params);
471 $date = $date[0]->texte
;
473 $type = array_shift($params);
474 $type = $type[0]->texte
;
476 $annee = $params ?
array_shift($params) : "";
477 $annee = "\n" . 'sprintf("%04d", ($x = ' .
478 calculer_liste($annee, array(), $boucles, $parent) .
479 ') ? $x : date("Y"))';
481 $mois = $params ?
array_shift($params) : "";
482 $mois = "\n" . 'sprintf("%02d", ($x = ' .
483 calculer_liste($mois, array(), $boucles, $parent) .
484 ') ? $x : date("m"))';
486 $jour = $params ?
array_shift($params) : "";
487 $jour = "\n" . 'sprintf("%02d", ($x = ' .
488 calculer_liste($jour, array(), $boucles, $parent) .
489 ') ? $x : date("d"))';
491 $annee2 = $params ?
array_shift($params) : "";
492 $annee2 = "\n" . 'sprintf("%04d", ($x = ' .
493 calculer_liste($annee2, array(), $boucles, $parent) .
494 ') ? $x : date("Y"))';
496 $mois2 = $params ?
array_shift($params) : "";
497 $mois2 = "\n" . 'sprintf("%02d", ($x = ' .
498 calculer_liste($mois2, array(), $boucles, $parent) .
499 ') ? $x : date("m"))';
501 $jour2 = $params ?
array_shift($params) : "";
502 $jour2 = "\n" . 'sprintf("%02d", ($x = ' .
503 calculer_liste($jour2, array(), $boucles, $parent) .
504 ') ? $x : date("d"))';
506 $boucle = &$boucles[$idb];
507 $date = $boucle->id_table
. ".$date";
510 $boucle->where
[]= array("'='", "'DATE_FORMAT($date, \'%Y%m%d\')'",
511 ("$annee . $mois . $jour"));
512 elseif ($type == 'mois')
513 $boucle->where
[]= array("'='", "'DATE_FORMAT($date, \'%Y%m\')'",
515 elseif ($type == 'semaine')
516 $boucle->where
[]= array("'AND'",
518 "'DATE_FORMAT($date, \'%Y%m%d\')'",
519 ("date_debut_semaine($annee, $mois, $jour)")),
521 "'DATE_FORMAT($date, \'%Y%m%d\')'",
522 ("date_fin_semaine($annee, $mois, $jour)")));
523 elseif (count($crit->param
) > 2)
524 $boucle->where
[]= array("'AND'",
526 "'DATE_FORMAT($date, \'%Y%m%d\')'",
527 ("$annee . $mois . $jour")),
528 array("'<='", "'DATE_FORMAT($date, \'%Y%m%d\')'", ("$annee2 . $mois2 . $jour2")));
529 // sinon on prend tout
532 // http://doc.spip.org/@calculer_critere_parties
533 function calculer_critere_parties($idb, &$boucles, $crit) {
534 $boucle = &$boucles[$idb];
535 $a1 = $crit->param
[0];
536 $a2 = $crit->param
[1];
538 list($a11,$a12) = calculer_critere_parties_aux($idb, $boucles, $a1);
539 list($a21,$a22) = calculer_critere_parties_aux($idb, $boucles, $a2);
540 if (($op== ',')&&(is_numeric($a11) && (is_numeric($a21))))
541 $boucle->limit
= $a11 .',' . $a21;
543 $boucle->partie
= ($a11 != 'n') ?
$a11 : $a12;
544 $boucle->total_parties
= ($a21 != 'n') ?
$a21 : $a22;
545 $boucle->mode_partie
= (($op == '/') ?
'/' :
546 (($a11=='n') ?
'-' : '+').(($a21=='n') ?
'-' : '+'));
550 // http://doc.spip.org/@calculer_critere_parties_aux
551 function calculer_critere_parties_aux($idb, &$boucles, $param) {
552 if ($param[0]->type
!= 'texte')
554 $a1 = calculer_liste(array($param[0]), array('id_mere' => $idb), $boucles, $boucles[$idb]->id_parent
);
555 ereg('^ *(-([0-9]+))? *$', $param[1]->texte
, $m);
556 return array("intval($a1)", ($m[2] ?
$m[2] : 0));
558 ereg('^ *(([0-9]+)|n) *(- *([0-9]+)? *)?$', $param[0]->texte
, $m);
561 return array($a1, 0);
563 return array($a1, $m[4]);
564 else return array($a1,
565 calculer_liste(array($param[1]), array(), $boucles[$idb]->id_parent
, $boucles));
570 // La fonction d'aiguillage sur le nom du critere
573 // http://doc.spip.org/@calculer_criteres
574 function calculer_criteres ($idb, &$boucles) {
576 foreach($boucles[$idb]->criteres
as $crit) {
577 $critere = $crit->op
;
578 // critere personnalise ?
579 $f = "critere_".$critere;
580 if (!function_exists($f))
583 // fonction critere standard ?
584 if (!function_exists($f)) {
585 // double cas particulier repere a l'analyse lexicale
586 if (($critere == ",") OR ($critere == '/'))
587 $f = 'calculer_critere_parties';
588 else $f = 'calculer_critere_DEFAUT';
590 // Applique le critere
591 $res = $f($idb, $boucles, $crit);
594 if (is_array($res)) erreur_squelette($res);
598 // Madeleine de Proust, revision MIT-1958 sqq, revision CERN-1989
599 // hum, c'est kwoi cette fonxion ?
600 // http://doc.spip.org/@kwote
601 function kwote($lisp)
603 if (preg_match(",^(\n//[^\n]*\n)? *'(.*)' *$,", $lisp, $r))
604 return $r[1] . "\"" . _q(str_replace(array("\\'","\\\\"),array("'","\\"),$r[2])) . "\"" ;
609 // Si on a une liste de valeurs dans #ENV{x}, utiliser la double etoile
610 // pour faire par exemple {id_article IN #ENV**{liste_articles}}
611 // http://doc.spip.org/@critere_IN_dist
612 function critere_IN_dist ($idb, &$boucles, $crit)
615 list($arg, $op, $val, $col)= calculer_critere_infixe($idb, $boucles, $crit);
617 $var = '$in' . $cpt++
;
618 $x= "\n\t$var = array();";
619 foreach ($val as $k => $v) {
620 if (preg_match(",^(\n//.*\n)?'(.*)'$,", $v, $r)) {
621 // optimiser le traitement des constantes
622 if (is_numeric($r[2]))
623 $x .= "\n\t$var" . "[]= $r[2];";
625 $x .= "\n\t$var" . "[]= " . _q($r[2]) . ";";
627 // Pour permettre de passer des tableaux de valeurs
628 // on repere l'utilisation brute de #ENV**{X},
629 // c'est-a-dire sa traduction en ($PILE[0][X]).
630 // et on deballe mais en rajoutant l'anti XSS
631 $x .= "\n\tif (!(is_array($v)))\n\t\t$var" ."[]= $v;\n\telse $var = array_merge($var, $v);";
635 $boucles[$idb]->in
.= $x;
637 // inserer la negation (cf !...)
639 $boucles[$idb]->default_order
[] = "'cpt$cpt'";
643 $arg = "FIELD($arg,\" . join(',',array_map('_q', $var)) . \")";
644 if ($boucles[$idb]->group
) $arg = "SUM($arg)";
645 $boucles[$idb]->select
[]= "$arg AS cpt$cpt";
646 $op = array("'$op'", "'cpt$cpt'", 0);
648 // inserer la condition; exemple: {id_mot ?IN (66, 62, 64)}
650 $boucles[$idb]->having
[]= (!$crit->cond ?
$op :
652 calculer_argument_precedent($idb, $col, $boucles),
658 # Criteres de comparaison
660 // http://doc.spip.org/@calculer_critere_DEFAUT
661 function calculer_critere_DEFAUT($idb, &$boucles, $crit)
663 list($arg, $op, $val, $col)= calculer_critere_infixe($idb, $boucles, $crit);
665 $where = array("'$op'", "'$arg'", $val[0]);
667 // inserer la negation (cf !...)
669 if ($crit->not
) $where = array("'NOT'", $where);
671 // inserer la condition (cf {lang?})
673 $boucles[$idb]->where
[]= (!$crit->cond ?
$where :
675 calculer_argument_precedent($idb, $col, $boucles),
680 // http://doc.spip.org/@calculer_critere_infixe
681 function calculer_critere_infixe($idb, &$boucles, $crit) {
683 global $table_des_tables, $tables_principales, $table_date;
684 global $exceptions_des_jointures;
685 $boucle = &$boucles[$idb];
686 $type = $boucle->type_requete
;
687 $table = $boucle->id_table
;
689 list($fct, $col, $op, $val, $args_sql) =
690 calculer_critere_infixe_ops($idb, $boucles, $crit);
693 // Cas particulier : id_enfant => utiliser la colonne id_objet
694 if ($col == 'id_enfant')
695 $col = $boucle->primary
;
697 // Cas particulier : id_secteur pour certaines tables
698 else if (($col == 'id_secteur')&&($type == 'breves')) {
699 $col = 'id_rubrique';
701 else if (($col == 'id_secteur')&& ($type == 'forums')) {
702 $table = critere_secteur_forum($idb, $boucles, $val, $crit);
705 // Cas particulier : expressions de date
706 else if ($table_date[$type]
707 AND preg_match(",^((age|jour|mois|annee)_relatif|date|mois|annee|jour|heure|age)(_[a-z]+)?$,",
710 calculer_critere_infixe_date($idb, $boucles, $regs);
713 // HACK : selection des documents selon mode 'image'
714 // => on cherche en fait 'vignette'
715 else if ($type == 'documents' AND $col == 'mode')
716 $val[0] = str_replace('image', 'vignette', $val[0]);
719 $nom = $table_des_tables[$type];
720 list($nom, $desc) = trouver_def_table($nom ?
$nom : $type, $boucle);
721 if (@!array_key_exists($col, $desc['field'])) {
722 $calculer_critere_externe = 'calculer_critere_externe_init';
723 // gestion par les plugins des jointures tordues pas automatiques mais necessaires
724 if (isset($exceptions_des_jointures[$table][$col])){
725 if (count($exceptions_des_jointures[$table][$col])==3)
726 list($t, $col, $calculer_critere_externe) = $exceptions_des_jointures[$table][$col];
728 list($t, $col) = $exceptions_des_jointures[$table][$col];
730 else if (isset($exceptions_des_jointures[$col]))
731 // on ignore la table, quel luxe!
732 list($t, $col) = $exceptions_des_jointures[$col];
733 $table = $calculer_critere_externe($boucle, $boucle->jointures
, $col, $desc, ($crit->cond
OR $op !='='), $t);
736 // tag du critere pour permettre aux boucles de modifier leurs requetes par defaut en fonction de ca
737 $boucles[$idb]->modificateur
['criteres'][$col] = true
;
739 // ajout pour le cas special d'une condition sur le champ statut:
740 // il faut alors interdire a la fonction de boucle
741 // de mettre ses propres criteres de statut
742 // http://www.spip.net/@statut (a documenter)
743 // garde pour compatibilite avec code des plugins anterieurs, mais redondant avec la ligne precedente
744 if ($col == 'statut') $boucles[$idb]->statut
= true
;
746 // ajout pour le cas special des forums
747 // il faut alors interdire a la fonction de boucle sur forum
748 // de selectionner uniquement les forums sans pere
750 elseif ($boucles[$idb]->type_requete
== 'forums' AND
751 ($col == 'id_parent' OR $col == 'id_forum'))
752 $boucles[$idb]->modificateur
['plat'] = true
;
753 // inserer le nom de la table SQL devant le nom du champ
756 $arg = "$table." . substr($col,1,-1);
757 else $arg = "$table.$col";
760 // inserer la fonction SQL
761 if ($fct) $arg = "$fct($arg$args_sql)";
763 return array($arg, $op, $val, $col_alias);
766 // Faute de copie du champ id_secteur dans la table des forums,
767 // faut le retrouver par jointure
768 // Pour chaque Row il faudrait tester si le forum est
769 // d'article, de breve, de rubrique, ou de syndication.
770 // Pour le moment on ne traite que les articles,
771 // les 3 autres cas ne marcheront donc pas: ca ferait 4 jointures
772 // qu'il faut traiter optimalement ou alors pas du tout.
774 // http://doc.spip.org/@critere_secteur_forum
775 function critere_secteur_forum($idb, &$boucles, $val, $crit)
777 list($nom, $desc) = trouver_def_table('articles', $boucles[$idb]);
778 return calculer_critere_externe_init($boucles[$idb], array($nom), 'id_secteur', $desc, $crit->cond
, true
);
781 // Champ hors table, ca ne peut etre qu'une jointure.
782 // On cherche la table du champ et on regarde si elle est deja jointe
783 // Si oui et qu'on y cherche un champ nouveau, pas de jointure supplementaire
784 // Exemple: criteres {titre_mot=...}{type_mot=...}
785 // Dans les 2 autres cas ==> jointure
786 // (Exemple: criteres {type_mot=...}{type_mot=...} donne 2 jointures
787 // pour selectioner ce qui a exactement ces 2 mots-cles.
789 // http://doc.spip.org/@calculer_critere_externe_init
790 function calculer_critere_externe_init(&$boucle, $joints, $col, $desc, $eg, $checkarrivee = false
)
792 $cle = trouver_champ_exterieur($col, $joints, $boucle, $checkarrivee);
794 $t = array_search($cle[0], $boucle->from
);
796 $c = '/\b' . $t . ".$col" . '\b/';
797 if (!trouver_champ($c, $boucle->where
)) {
798 // mais ca peut etre dans le FIELD pour le Having
799 $c = "/FIELD.$t" .".$col,/";
800 if (!trouver_champ($c, $boucle->select
)) return $t;
803 $cle = calculer_jointure($boucle, array($boucle->id_table
, $desc), $cle, $col, $eg);
804 if ($cle) return "L$cle";
807 erreur_squelette(_T('zbug_info_erreur_squelette'),
810 _T('zbug_critere_inconnu',
811 array('critere' => $col)));
814 // http://doc.spip.org/@trouver_champ
815 function trouver_champ($champ, $where)
817 if (!is_array($where))
818 return preg_match($champ,$where);
820 foreach ($where as $clause) {
821 if (trouver_champ($champ, $clause)) return true
;
827 // deduction automatique des jointures
828 // une jointure sur une table avec primary key doit se faire sur celle-ci.
830 // http://doc.spip.org/@calculer_jointure
831 function calculer_jointure(&$boucle, $depart, $arrivee, $col='', $cond=false
)
834 $res = calculer_chaine_jointures($boucle, $depart, $arrivee);
835 if (!$res) return "";
837 list($dnom,$ddesc) = $depart;
838 $id_primary = $ddesc['key']['PRIMARY KEY'];
839 $keys = preg_split('/,\s*/', $id_primary);
841 $cpt = &$num[$boucle->descr
['nom']][$boucle->id_boucle
];
842 foreach($res as $r) {
843 list($d, $a, $j) = $r;
845 $boucle->join
[$n]= array(($id_table ?
$id_table : $d), $j);
846 $boucle->from
[$id_table = "L$n"] = $a[0];
849 // pas besoin de group by
850 // (cf http://article.gmane.org/gmane.comp.web.spip.devel/30555)
851 // si une seule jointure et sur une table avec primary key formee
852 // de l'index principal et de l'index de jointure (non conditionnel! [6031])
853 // et operateur d'egalite (http://trac.rezo.net/trac/spip/ticket/477)
855 if ($pk = (count($boucle->from
) == 1) && !$cond) {
856 if ($pk = $a[1]['key']['PRIMARY KEY']) {
857 $pk=preg_match("/^$id_primary, *$col$/", $pk) OR
858 preg_match("/^$col, *$id_primary$/", $pk);
861 // la clause Group by est en conflit avec ORDER BY, a completer
864 foreach($keys as $id_prim){
865 $id_field = $dnom . '.' . $id_prim;
866 if (!in_array($id_field, $boucle->group
)) {
867 $boucle->group
[] = $id_field;
868 // postgres exige que le champ pour GROUP soit dans le SELECT
869 if (!in_array($id_field, $boucle->select
))
870 $boucle->select
[] = $id_field;
874 $boucle->modificateur
['lien'] = true
;
878 // http://doc.spip.org/@calculer_chaine_jointures
879 function calculer_chaine_jointures(&$boucle, $depart, $arrivee, $vu=array(), $milieu_prec = false
)
881 list($dnom,$ddesc) = $depart;
882 list($anom,$adesc) = $arrivee;
884 $vu[] = $dnom; // ne pas oublier la table de depart
886 $keys = $ddesc['key'];
887 if ($v = $adesc['key']['PRIMARY KEY']) {
888 unset($adesc['key']['PRIMARY KEY']);
889 $akeys = array_merge(preg_split('/,\s*/', $v), $adesc['key']);
891 else $akeys = $adesc['key'];
892 // priorite a la primaire, qui peut etre multiple
893 if ($v = (preg_split('/,\s*/', $keys['PRIMARY KEY'])))
895 $v = array_intersect($keys, $akeys);
897 return array(array($dnom, $arrivee, array_shift($v)));
900 foreach($boucle->jointures
as $v) {
901 if ($v && (!in_array($v,$vu)) &&
902 ($def = trouver_def_table($v, $boucle))) {
903 list($table,$join) = $def;
904 $milieu = array_intersect($ddesc['key'], trouver_cles_table($join['key']));
906 foreach ($milieu as $k)
907 if ($k!=$milieu_prec) // ne pas repasser par la meme cle car c'est un chemin inutilement long
909 $r = calculer_chaine_jointures($boucle, array($v, $join), $arrivee, $new, $k);
911 array_unshift($r, array($dnom, $def, $k));
921 // applatit les cles multiples
923 // http://doc.spip.org/@trouver_cles_table
924 function trouver_cles_table($keys)
927 foreach ($keys as $v) {
931 foreach (split(" *, *", $v) as $k) {
936 return array_keys($res);
939 // Trouve la description d'une table dans les globales de Spip
940 // (le prefixe des tables y est toujours 'spip_', son chgt est ulterieur)
941 // Si on ne la trouve pas, on demande au serveur SQL (marche pas toujours)
943 // http://doc.spip.org/@trouver_def_table
944 function trouver_def_table($nom, &$boucle)
946 global $tables_principales, $tables_auxiliaires, $table_des_tables, $tables_des_serveurs_sql;
949 $s = $boucle->sql_serveur
;
952 if (in_array($nom, $table_des_tables))
953 $nom_table = 'spip_' . $nom;
956 $desc = $tables_des_serveurs_sql[$s];
958 if (isset($desc[$nom_table]))
959 return array($nom_table, $desc[$nom_table]);
961 include_spip('base/auxiliaires');
962 $nom_table = 'spip_' . $nom;
963 if ($desc = $tables_auxiliaires[$nom_table])
964 return array($nom_table, $desc);
966 if ($desc = spip_abstract_showtable($nom, $boucle->sql_serveur
))
967 if (isset($desc['field'])) {
968 // faudrait aussi prevoir le cas du serveur externe
969 $tables_principales[$nom] = $desc;
970 return array($nom, $desc);
972 erreur_squelette(_T('zbug_table_inconnue', array('table' => $nom)),
976 // http://doc.spip.org/@trouver_champ_exterieur
977 function trouver_champ_exterieur($cle, $joints, &$boucle, $checkarrivee = false
)
979 foreach($joints as $k => $join) {
980 if ($join && $table = trouver_def_table($join, $boucle)) {
981 if (array_key_exists($cle, $table[1]['field'])
982 && ($checkarrivee==false ||
$checkarrivee==$table[0])) // si on sait ou on veut arriver, il faut que ca colle
989 // determine l'operateur et les operandes
991 // http://doc.spip.org/@calculer_critere_infixe_ops
992 function calculer_critere_infixe_ops($idb, &$boucles, $crit)
994 // cas d'une valeur comparee a elle-meme ou son referent
995 if (count($crit->param
) == 0)
997 $col = $val = $crit->op
;
998 // Cas special {lang} : aller chercher $GLOBALS['spip_lang']
1000 $val = array(kwote('$GLOBALS[\'spip_lang\']'));
1002 // Si id_parent, comparer l'id_parent avec l'id_objet
1003 // de la boucle superieure.... faudrait verifier qu'il existe
1004 // pour eviter l'erreur SQL
1005 if ($val == 'id_parent')
1006 $val = $boucles[$idb]->primary
;
1007 // Si id_enfant, comparer l'id_objet avec l'id_parent
1008 // de la boucle superieure
1009 else if ($val == 'id_enfant')
1011 $val = array(kwote(calculer_argument_precedent($idb, $val, $boucles)));
1014 // comparaison explicite
1015 // le phraseur impose que le premier param soit du texte
1016 $params = $crit->param
;
1018 if ($op == '==') $op = 'REGEXP';
1019 $col = array_shift($params);
1020 $col = $col[0]->texte
;
1023 $desc = array('id_mere' => $idb);
1024 $parent = $boucles[$idb]->id_parent
;
1026 // Dans le cas {x=='#DATE'} etc, defaire le travail du phraseur,
1027 // celui ne sachant pas ce qu'est un critere infixe
1028 // et a fortiori son 2e operande qu'entoure " ou '
1029 if (count($params)==1
1030 AND count($params[0]==3)
1031 AND $params[0][0]->type
== 'texte'
1032 AND $params[0][2]->type
== 'texte'
1033 AND ($p=$params[0][0]->texte
) == $params[0][2]->texte
1034 AND (($p == "'") OR ($p == '"'))
1035 AND $params[0][1]->type
== 'champ' ) {
1036 $val[]= "$p\\$p#" . $params[0][1]->nom_champ
. "\\$p$p";
1039 foreach ((($op != 'IN') ?
$params : calculer_vieux_in($params)) as $p) {
1040 $a = calculer_liste($p, $desc, $boucles, $parent);
1041 if ($op == 'IN') $val[]= $a;
1042 else $val[]=kwote($a);
1046 $fct = $args_sql = '';
1048 if (preg_match('/^(.*)' . SQL_ARGS
. '$/', $col, $m)) {
1050 preg_match('/^\(([^,]*)(.*)\)$/', $m[2], $a);
1052 if (preg_match('/^(\S*)(\s+AS\s+.*)$/i', $col, $m)) {
1056 $args_sql .= $a[2];;
1058 return array($fct, $col, $op, $val, $args_sql);
1061 // compatibilite ancienne version
1063 // http://doc.spip.org/@calculer_vieux_in
1064 function calculer_vieux_in($params)
1066 $deb = $params[0][0];
1067 $k = count($params)-1;
1068 $last = $params[$k];
1069 $j = count($last)-1;
1071 $n = strlen($last->texte
);
1073 if (!(($deb->texte
[0] == '(') && ($last->texte
[$n-1] == ')')))
1075 $params[0][0]->texte
= substr($deb->texte
,1);
1076 // attention, on peut avoir k=0,j=0 ==> recalculer
1077 $last = $params[$k][$j];
1078 $n = strlen($last->texte
);
1079 $params[$k][$j]->texte
= substr($last->texte
,0,$n-1);
1081 foreach($params as $v) {
1082 if ($v[0]->type
!= 'texte')
1085 foreach(split(',', $v[0]->texte
) as $x) {
1088 $newp[] = array($t);
1095 // http://doc.spip.org/@calculer_critere_infixe_date
1096 function calculer_critere_infixe_date($idb, &$boucles, $regs)
1098 global $table_date, $table_des_tables, $tables_principales;
1099 $boucle = $boucles[$idb];
1100 list(,$col, $rel, $suite) = $regs;
1101 $date_orig = $pred = $table_date[$boucle->type_requete
];
1103 # Recherche de l'existence du champ date_xxxx,
1104 # si oui choisir ce champ, sinon choisir xxxx
1105 list(,$t)= trouver_def_table($boucle->type_requete
, $boucle);
1106 if ($t['field']["date$suite"])
1107 $date_orig = 'date'.$suite;
1109 $date_orig = substr($suite, 1);
1111 } else if ($rel) $pred = 'date';
1113 $date_compare = "\"' . normaliser_date(" .
1114 calculer_argument_precedent($idb, $pred, $boucles) .
1116 $date_orig = $boucle->id_table
. '.' . $date_orig;
1118 if ($col == 'date') {
1122 else if ($col == 'jour') {
1123 $col = "DAYOFMONTH($date_orig)";
1126 else if ($col == 'mois') {
1127 $col = "MONTH($date_orig)";
1130 else if ($col == 'annee') {
1131 $col = "YEAR($date_orig)";
1134 else if ($col == 'heure') {
1135 $col = "DATE_FORMAT($date_orig, '%H:%i')";
1138 else if ($col == 'age') {
1139 $col = calculer_param_date("now()", $date_orig);
1142 else if ($col == 'age_relatif') {
1143 $col = calculer_param_date($date_compare, $date_orig);
1146 else if ($col == 'jour_relatif') {
1147 $col = "LEAST(TO_DAYS(" .$date_compare . ")-TO_DAYS(" .
1148 $date_orig . "), DAYOFMONTH(" . $date_compare .
1149 ")-DAYOFMONTH(" . $date_orig . ")+30.4368*(MONTH(" .
1150 $date_compare . ")-MONTH(" . $date_orig .
1151 "))+365.2422*(YEAR(" . $date_compare . ")-YEAR(" .
1155 else if ($col == 'mois_relatif') {
1156 $col = "MONTH(" . $date_compare . ")-MONTH(" .
1157 $date_orig . ")+12*(YEAR(" . $date_compare .
1158 ")-YEAR(" . $date_orig . "))";
1161 else if ($col == 'annee_relatif') {
1162 $col = "YEAR(" . $date_compare . ")-YEAR(" .
1166 return array($col, $col_table);
1169 // http://doc.spip.org/@calculer_param_date
1170 function calculer_param_date($date_compare, $date_orig) {
1171 if (ereg("'\" *\.(.*)\. *\"'", $date_compare, $r)) {
1172 $init = "'\" . (\$x = $r[1]) . \"'";
1173 $date_compare = '\'$x\'';
1176 $init = $date_compare;
1179 "LEAST((UNIX_TIMESTAMP(" .
1181 ")-UNIX_TIMESTAMP(" .
1183 "))/86400,\n\tTO_DAYS(" .
1187 "),\n\tDAYOFMONTH(" .
1191 ")+30.4368*(MONTH(" .
1195 "))+365.2422*(YEAR(" .