squellete2
[aidenligne_francais_universite.git] / ecrire / public / criteres.php
CommitLineData
c495c100
P
1<?php
2
3/***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2007 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
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\***************************************************************************/
12
13
14//
15// Definition des {criteres} d'une boucle
16//
17
18if (!defined("_ECRIRE_INC_VERSION")) return;
19
20// {racine}
21// http://www.spip.net/@racine
22// http://doc.spip.org/@critere_racine_dist
23function critere_racine_dist($idb, &$boucles, $crit) {
24 $not = $crit->not;
25 $boucle = &$boucles[$idb];
26
27 if ($not)
28 erreur_squelette(_T('zbug_info_erreur_squelette'), $crit->op);
29
30 $boucle->where[]= array("'='", "'$boucle->id_table." . "id_parent'", 0);
31}
32
33// {exclus}
34// http://www.spip.net/@exclus
35// http://doc.spip.org/@critere_exclus_dist
36function critere_exclus_dist($idb, &$boucles, $crit) {
37 $param = $crit->op;
38 $not = $crit->not;
39 $boucle = &$boucles[$idb];
40 $id = $boucle->primary;
41
42 if ($not OR !$id)
43 erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
44
45 $arg = kwote(calculer_argument_precedent($idb, $id, $boucles));
46 $boucle->where[]= array("'!='", "'$boucle->id_table." . "$id'", $arg);
47}
48
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
53function 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 .
60 "', " .
61 '"0".$doublons[' .
62 ($crit->not ? '' : ($boucle->doublons . "[]= ")) .
63 "('" .
64 $boucle->type_requete .
65 "'" .
66 ($nom == "''" ? '' : " . $nom") .
67 ')], \'' .
68 ($crit->not ? '' : 'NOT') .
69 "')");
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 = "";
75}
76
77// {lang_select}
78// http://www.spip.net/@lang_select
79// http://doc.spip.org/@critere_lang_select_dist
80function 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;
85}
86
87// {debut_xxx}
88// http://www.spip.net/@debut_
89// http://doc.spip.org/@critere_debut_dist
90function critere_debut_dist($idb, &$boucles, $crit) {
91 $boucle = &$boucles[$idb];
92 $boucle->limit = 'intval($GLOBALS["debut' .
93 $crit->param[0][0]->texte .
94 '"]) . ",' .
95 $crit->param[1][0]->texte .
96 '"' ;
97}
98// {pagination}
99// {pagination 20}
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
104function critere_pagination_dist($idb, &$boucles, $crit) {
105
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)";
110
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;
118}
119
120// {fragment}
121// http://www.spip.net/@fragment
122// http://doc.spip.org/@critere_fragment_dist
123function critere_fragment_dist($idb, &$boucles, $crit) {
124 if (!($param = $crit->param[0][0]->texte))
125 $param = 'fragment_'.$boucle->descr['nom'].$idb;
126 if ($crit->not)
127 $param = false;
128 $boucle = &$boucles[$idb];
129 $boucle->modificateur['fragment'] = $param;
130}
131
132
133// {recherche}
134// http://www.spip.net/@recherche
135// http://doc.spip.org/@critere_recherche_dist
136function 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))
141 $t = "spip_$t";
142
143 // Ne pas executer la requete en cas de hash vide
144 $boucle->hash = '
145 // RECHERCHE
146 list($rech_select, $rech_where) = prepare_recherche($GLOBALS["recherche"], "'.$boucle->primary.'", "'.$boucle->id_table.'", "'.$t.'", "'.$crit->cond.'");
147 ';
148
149 // Sauf si le critere est conditionnel {recherche ?}
150 if (!$crit->cond)
151 $boucle->hash .= '
152 if ($rech_where) ';
153
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';
158
159 // et la recherche trouve
160 $boucle->where[]= '$rech_where';
161}
162
163// {traduction}
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
168function 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));
174 $boucle->where[]=
175 array("'OR'",
176 array("'AND'",
177 array("'='", "'$table.id_trad'", 0),
178 array("'='", "'$table.$prim'", $dprim)
179 ),
180 array("'AND'",
181 array("'>'", "'$table.id_trad'", 0),
182 array("'='", "'$table.id_trad'", $arg)
183 )
184 );
185}
186
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
191function critere_origine_traduction_dist($idb, &$boucles, $crit) {
192 $boucle = &$boucles[$idb];
193 $prim = $boucle->primary;
194 $table = $boucle->id_table;
195
196 $c =
197 array("'OR'",
198 array("'='", "'$table." . "id_trad'", "'$table.$prim'"),
199 array("'='", "'$table.id_trad'", "'0'")
200 );
201 $boucle->where[]= ($crit->not ? array("'NOT'", $c) : $c);
202}
203
204// {meme_parent}
205// http://www.spip.net/@meme_parent
206// http://doc.spip.org/@critere_meme_parent_dist
207function 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';
211
212 if ($boucle->type_requete == 'rubriques') {
213 $boucle->where[]= array("'='", "'$mparent'", $arg);
214
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");
220}
221
222// {branche ?}
223// http://www.spip.net/@branche
224// http://doc.spip.org/@critere_branche_dist
225function critere_branche_dist($idb, &$boucles, $crit) {
226 $not = $crit->not;
227 $boucle = &$boucles[$idb];
228
229 $arg = calculer_argument_precedent($idb, 'id_rubrique', $boucles);
230
231 $c = "calcul_mysql_in('" .
232 $boucle->id_table .
233 ".id_rubrique', calcul_branche($arg), '')";
234 if ($crit->cond) $c = "($arg ? $c : 1)";
235
236 if ($not)
237 $boucle->where[]= array("'NOT'", $c);
238 else
239 $boucle->where[]= $c;
240}
241
242// {logo} liste les objets qui ont un logo
243// http://doc.spip.org/@critere_logo_dist
244function critere_logo_dist($idb, &$boucles, $crit) {
245 $not = $crit->not;
246 $boucle = &$boucles[$idb];
247
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)";
252
253 if ($not)
254 $boucle->where[]= array("'NOT'", $c);
255 else
256 $boucle->where[]= $c;
257}
258
259// c'est la commande SQL "GROUP BY"
260// par exemple <boucle(articles){fusion lang}>
261// http://doc.spip.org/@critere_fusion_dist
262function 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) . '."';
268 } else
269 erreur_squelette(_T('zbug_info_erreur_squelette'),
270 "{groupby ?} BOUCLE$idb");
271}
272
273// http://doc.spip.org/@calculer_critere_arg_dynamique
274function calculer_critere_arg_dynamique($idb, &$boucles, $crit, $suffix='')
275{
276 global $table_des_tables, $tables_des_serveurs_sql;
277
278 $boucle = $boucles[$idb];
279
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];
288
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) ) : '')";
292 } else {
293 return "((\$x = preg_replace(\"/\\W/\",'',$arg)) ? ('$boucle->id_table.' . \$x$suffix) : '')";
294 }
295}
296// Tri : {par xxxx}
297// http://www.spip.net/@par
298// http://doc.spip.org/@critere_par_dist
299function critere_par_dist($idb, &$boucles, $crit) {
300 critere_parinverse($idb, $boucles, $crit, '') ;
301}
302
303// http://doc.spip.org/@critere_parinverse
304function 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'";
308
309 foreach ($crit->param as $tri) {
310
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()")))
320 $par = "RAND()";
321 else
322 $par = "MOD(".$boucle->id_table.'.'.$boucle->primary
323 ." * UNIX_TIMESTAMP(),32767) & UNIX_TIMESTAMP()";
324 $boucle->select[]= $par . " AS hasard";
325 }
326 } else {
327 $par = array_shift($tri);
328 $par = $par->texte;
329 // par multi champ
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']).\"" ;
333 $order = "multi";
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);
338 if ($suite !== "''")
339 $texte = "\" . ((\$x = $suite) ? ('$texte' . \$x) : '0')" . " . \"";
340 $as = 'num' .($boucle->order ? count($boucle->order) : "");
341 $boucle->select[] = $texte . " AS $as";
342 $order = "'$as'";
343 } else {
344 if (!preg_match(",^" . CHAMP_SQL_PLUS_FONC . '$,is', $par, $match))
345 erreur_squelette(_T('zbug_info_erreur_squelette'), "{par $par} BOUCLE$idb");
346 else {
347 if ($match[2]) { $par = substr($match[2],1,-1); $fct = $match[1]; }
348 // par hasard
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()")))
353 $par = "RAND()";
354 else
355 $par = "MOD(".$boucle->id_table.'.'.$boucle->primary
356 ." * UNIX_TIMESTAMP(),32767) & UNIX_TIMESTAMP()";
357 $boucle->select[]= $par . " AS alea";
358 $order = "'alea'";
359 }
360 // par date_thread
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') {
364 $t = 'forum';
365 } else {
366 $t = critere_par_jointure($boucle, array('spip_forum','id_thread'));
367 $t = substr($t, 1, strpos($t,'.')-1);
368 }
369 $boucle->select[] = "MAX($t" . ".".
370 $GLOBALS['table_date']['forums']
371 .") AS date_thread";
372 $boucle->group[] = $t . ".id_thread";
373 $order = "'date_thread'";
374 $boucle->modificateur['plat'] = true;
375 }
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]);
379 }
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 . "'";
384 }
385 // par champ. Verifier qu'ils sont presents.
386 else {
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)
397 $order = "'$par'";
398 }
399 }
400 }
401 }
402 if ($order)
403 $boucle->order[] = ($fct ? "'$fct(' . $order . ')'" : $order) .
404 (($order[0]=="'") ? $sens : "");
405 }
406}
407
408// http://doc.spip.org/@critere_par_jointure
409function critere_par_jointure(&$boucle, $join)
410{
411 global $table_des_tables;
412 list($table, $champ) = $join;
413 $t = array_search($table, $boucle->from);
414 if (!$t) {
415 $type = $boucle->type_requete;
416 $nom = $table_des_tables[$type];
417 list($nom, $desc) = trouver_def_table($nom ? $nom : $type, $boucle);
418
419 $cle = trouver_champ_exterieur($champ, $boucle->jointures, $boucle);
420 if ($cle)
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");
424
425 }
426 return "'" . $t . '.' . $champ . "'";
427}
428
429// {inverse}
430// http://www.spip.net/@inverse
431
432// http://doc.spip.org/@critere_inverse_dist
433function critere_inverse_dist($idb, &$boucles, $crit) {
434
435 $boucle = &$boucles[$idb];
436 // Classement par ordre inverse
437 if ($crit->not)
438 critere_parinverse($idb, $boucles, $crit, " . ' DESC'");
439 else
440 {
441 $order = "' 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':'')";
446 }
447
448 $n = count($boucle->order);
449 if ($n)
450 $boucle->order[$n-1] .= " . $order";
451 else
452 $boucle->default_order[] = ' DESC';
453 }
454}
455
456// http://doc.spip.org/@critere_agenda_dist
457function critere_agenda_dist($idb, &$boucles, $crit)
458{
459 $params = $crit->param;
460
461 if (count($params) < 1)
462 erreur_squelette(_T('zbug_info_erreur_squelette'),
463 "{agenda ?} BOUCLE$idb");
464
465 $parent = $boucles[$idb]->id_parent;
466
467 // les valeurs $date et $type doivent etre connus a la compilation
468 // autrement dit ne pas etre des champs
469
470 $date = array_shift($params);
471 $date = $date[0]->texte;
472
473 $type = array_shift($params);
474 $type = $type[0]->texte;
475
476 $annee = $params ? array_shift($params) : "";
477 $annee = "\n" . 'sprintf("%04d", ($x = ' .
478 calculer_liste($annee, array(), $boucles, $parent) .
479 ') ? $x : date("Y"))';
480
481 $mois = $params ? array_shift($params) : "";
482 $mois = "\n" . 'sprintf("%02d", ($x = ' .
483 calculer_liste($mois, array(), $boucles, $parent) .
484 ') ? $x : date("m"))';
485
486 $jour = $params ? array_shift($params) : "";
487 $jour = "\n" . 'sprintf("%02d", ($x = ' .
488 calculer_liste($jour, array(), $boucles, $parent) .
489 ') ? $x : date("d"))';
490
491 $annee2 = $params ? array_shift($params) : "";
492 $annee2 = "\n" . 'sprintf("%04d", ($x = ' .
493 calculer_liste($annee2, array(), $boucles, $parent) .
494 ') ? $x : date("Y"))';
495
496 $mois2 = $params ? array_shift($params) : "";
497 $mois2 = "\n" . 'sprintf("%02d", ($x = ' .
498 calculer_liste($mois2, array(), $boucles, $parent) .
499 ') ? $x : date("m"))';
500
501 $jour2 = $params ? array_shift($params) : "";
502 $jour2 = "\n" . 'sprintf("%02d", ($x = ' .
503 calculer_liste($jour2, array(), $boucles, $parent) .
504 ') ? $x : date("d"))';
505
506 $boucle = &$boucles[$idb];
507 $date = $boucle->id_table . ".$date";
508
509 if ($type == 'jour')
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\')'",
514 ("$annee . $mois"));
515 elseif ($type == 'semaine')
516 $boucle->where[]= array("'AND'",
517 array("'>='",
518 "'DATE_FORMAT($date, \'%Y%m%d\')'",
519 ("date_debut_semaine($annee, $mois, $jour)")),
520 array("'<='",
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'",
525 array("'>='",
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
530}
531
532// http://doc.spip.org/@calculer_critere_parties
533function calculer_critere_parties($idb, &$boucles, $crit) {
534 $boucle = &$boucles[$idb];
535 $a1 = $crit->param[0];
536 $a2 = $crit->param[1];
537 $op = $crit->op;
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;
542 else {
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') ? '-' : '+'));
547 }
548}
549
550// http://doc.spip.org/@calculer_critere_parties_aux
551function calculer_critere_parties_aux($idb, &$boucles, $param) {
552 if ($param[0]->type != 'texte')
553 {
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));
557 } else {
558 ereg('^ *(([0-9]+)|n) *(- *([0-9]+)? *)?$', $param[0]->texte, $m);
559 $a1 = $m[1];
560 if (!$m[3])
561 return array($a1, 0);
562 elseif ($m[4])
563 return array($a1, $m[4]);
564 else return array($a1,
565 calculer_liste(array($param[1]), array(), $boucles[$idb]->id_parent, $boucles));
566 }
567}
568
569//
570// La fonction d'aiguillage sur le nom du critere
571//
572
573// http://doc.spip.org/@calculer_criteres
574function calculer_criteres ($idb, &$boucles) {
575
576 foreach($boucles[$idb]->criteres as $crit) {
577 $critere = $crit->op;
578 // critere personnalise ?
579 $f = "critere_".$critere;
580 if (!function_exists($f))
581 $f .= '_dist';
582
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';
589 }
590 // Applique le critere
591 $res = $f($idb, $boucles, $crit);
592
593 // Gestion d'erreur
594 if (is_array($res)) erreur_squelette($res);
595 }
596}
597
598// Madeleine de Proust, revision MIT-1958 sqq, revision CERN-1989
599// hum, c'est kwoi cette fonxion ?
600// http://doc.spip.org/@kwote
601function kwote($lisp)
602{
603 if (preg_match(",^(\n//[^\n]*\n)? *'(.*)' *$,", $lisp, $r))
604 return $r[1] . "\"" . _q(str_replace(array("\\'","\\\\"),array("'","\\"),$r[2])) . "\"" ;
605 else
606 return "_q($lisp)";
607}
608
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
612function critere_IN_dist ($idb, &$boucles, $crit)
613{
614 static $cpt = 0;
615 list($arg, $op, $val, $col)= calculer_critere_infixe($idb, $boucles, $crit);
616
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];";
624 else
625 $x .= "\n\t$var" . "[]= " . _q($r[2]) . ";";
626 } else {
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);";
632 }
633 }
634
635 $boucles[$idb]->in .= $x;
636
637 // inserer la negation (cf !...)
638 if (!$crit->not) {
639 $boucles[$idb]->default_order[] = "'cpt$cpt'";
640 $op = '<>';
641 } else $op = '=';
642
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);
647
648// inserer la condition; exemple: {id_mot ?IN (66, 62, 64)}
649
650 $boucles[$idb]->having[]= (!$crit->cond ? $op :
651 array("'?'",
652 calculer_argument_precedent($idb, $col, $boucles),
653 $op,
654 "''"));
655}
656
657
658# Criteres de comparaison
659
660// http://doc.spip.org/@calculer_critere_DEFAUT
661function calculer_critere_DEFAUT($idb, &$boucles, $crit)
662{
663 list($arg, $op, $val, $col)= calculer_critere_infixe($idb, $boucles, $crit);
664
665 $where = array("'$op'", "'$arg'", $val[0]);
666
667 // inserer la negation (cf !...)
668
669 if ($crit->not) $where = array("'NOT'", $where);
670
671 // inserer la condition (cf {lang?})
672
673 $boucles[$idb]->where[]= (!$crit->cond ? $where :
674 array("'?'",
675 calculer_argument_precedent($idb, $col, $boucles),
676 $where,
677 "''"));
678}
679
680// http://doc.spip.org/@calculer_critere_infixe
681function calculer_critere_infixe($idb, &$boucles, $crit) {
682
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;
688
689 list($fct, $col, $op, $val, $args_sql) =
690 calculer_critere_infixe_ops($idb, $boucles, $crit);
691 $col_alias = $col;
692
693 // Cas particulier : id_enfant => utiliser la colonne id_objet
694 if ($col == 'id_enfant')
695 $col = $boucle->primary;
696
697 // Cas particulier : id_secteur pour certaines tables
698 else if (($col == 'id_secteur')&&($type == 'breves')) {
699 $col = 'id_rubrique';
700 }
701 else if (($col == 'id_secteur')&& ($type == 'forums')) {
702 $table = critere_secteur_forum($idb, $boucles, $val, $crit);
703 }
704
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]+)?$,",
708 $col, $regs)) {
709 list($col, $table) =
710 calculer_critere_infixe_date($idb, $boucles, $regs);
711 }
712
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]);
717
718 else {
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];
727 else
728 list($t, $col) = $exceptions_des_jointures[$table][$col];
729 }
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);
734 }
735 }
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;
738
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;
745
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
749
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
754 if ($table) {
755 if ($col[0] == "`")
756 $arg = "$table." . substr($col,1,-1);
757 else $arg = "$table.$col";
758 } else $arg = $col;
759
760 // inserer la fonction SQL
761 if ($fct) $arg = "$fct($arg$args_sql)";
762
763 return array($arg, $op, $val, $col_alias);
764}
765
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.
773
774// http://doc.spip.org/@critere_secteur_forum
775function critere_secteur_forum($idb, &$boucles, $val, $crit)
776{
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);
779}
780
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.
788
789// http://doc.spip.org/@calculer_critere_externe_init
790function calculer_critere_externe_init(&$boucle, $joints, $col, $desc, $eg, $checkarrivee = false)
791{
792 $cle = trouver_champ_exterieur($col, $joints, $boucle, $checkarrivee);
793 if ($cle) {
794 $t = array_search($cle[0], $boucle->from);
795 if ($t) {
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;
801 }
802 }
803 $cle = calculer_jointure($boucle, array($boucle->id_table, $desc), $cle, $col, $eg);
804 if ($cle) return "L$cle";
805 }
806
807 erreur_squelette(_T('zbug_info_erreur_squelette'),
808 _T('zbug_boucle') .
809 " $idb " .
810 _T('zbug_critere_inconnu',
811 array('critere' => $col)));
812}
813
814// http://doc.spip.org/@trouver_champ
815function trouver_champ($champ, $where)
816{
817 if (!is_array($where))
818 return preg_match($champ,$where);
819 else {
820 foreach ($where as $clause) {
821 if (trouver_champ($champ, $clause)) return true;
822 }
823 return false;
824 }
825}
826
827// deduction automatique des jointures
828// une jointure sur une table avec primary key doit se faire sur celle-ci.
829
830// http://doc.spip.org/@calculer_jointure
831function calculer_jointure(&$boucle, $depart, $arrivee, $col='', $cond=false)
832{
833 static $num=array();
834 $res = calculer_chaine_jointures($boucle, $depart, $arrivee);
835 if (!$res) return "";
836
837 list($dnom,$ddesc) = $depart;
838 $id_primary = $ddesc['key']['PRIMARY KEY'];
839 $keys = preg_split('/,\s*/', $id_primary);
840 $id_table = "";
841 $cpt = &$num[$boucle->descr['nom']][$boucle->id_boucle];
842 foreach($res as $r) {
843 list($d, $a, $j) = $r;
844 $n = ++$cpt;
845 $boucle->join[$n]= array(($id_table ? $id_table : $d), $j);
846 $boucle->from[$id_table = "L$n"] = $a[0];
847 }
848
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)
854
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);
859 }
860 }
861 // la clause Group by est en conflit avec ORDER BY, a completer
862
863 if (!$pk)
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;
871 }
872 }
873
874 $boucle->modificateur['lien'] = true;
875 return $n;
876}
877
878// http://doc.spip.org/@calculer_chaine_jointures
879function calculer_chaine_jointures(&$boucle, $depart, $arrivee, $vu=array(), $milieu_prec = false)
880{
881 list($dnom,$ddesc) = $depart;
882 list($anom,$adesc) = $arrivee;
883 if (!count($vu))
884 $vu[] = $dnom; // ne pas oublier la table de depart
885
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']);
890 }
891 else $akeys = $adesc['key'];
892 // priorite a la primaire, qui peut etre multiple
893 if ($v = (preg_split('/,\s*/', $keys['PRIMARY KEY'])))
894 $keys = $v;
895 $v = array_intersect($keys, $akeys);
896 if ($v)
897 return array(array($dnom, $arrivee, array_shift($v)));
898 else {
899 $new = $vu;
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']));
905 $new[] = $v;
906 foreach ($milieu as $k)
907 if ($k!=$milieu_prec) // ne pas repasser par la meme cle car c'est un chemin inutilement long
908 {
909 $r = calculer_chaine_jointures($boucle, array($v, $join), $arrivee, $new, $k);
910 if ($r) {
911 array_unshift($r, array($dnom, $def, $k));
912 return $r;
913 }
914 }
915 }
916 }
917 }
918 return array();
919}
920
921// applatit les cles multiples
922
923// http://doc.spip.org/@trouver_cles_table
924function trouver_cles_table($keys)
925{
926 $res =array();
927 foreach ($keys as $v) {
928 if (!strpos($v,","))
929 $res[$v]=1;
930 else {
931 foreach (split(" *, *", $v) as $k) {
932 $res[$k]=1;
933 }
934 }
935 }
936 return array_keys($res);
937}
938
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)
942
943// http://doc.spip.org/@trouver_def_table
944function trouver_def_table($nom, &$boucle)
945{
946 global $tables_principales, $tables_auxiliaires, $table_des_tables, $tables_des_serveurs_sql;
947
948 $nom_table = $nom;
949 $s = $boucle->sql_serveur;
950 if (!$s) {
951 $s = 'localhost';
952 if (in_array($nom, $table_des_tables))
953 $nom_table = 'spip_' . $nom;
954 }
955
956 $desc = $tables_des_serveurs_sql[$s];
957
958 if (isset($desc[$nom_table]))
959 return array($nom_table, $desc[$nom_table]);
960
961 include_spip('base/auxiliaires');
962 $nom_table = 'spip_' . $nom;
963 if ($desc = $tables_auxiliaires[$nom_table])
964 return array($nom_table, $desc);
965
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);
971 }
972 erreur_squelette(_T('zbug_table_inconnue', array('table' => $nom)),
973 $boucle->id_boucle);
974 }
975
976// http://doc.spip.org/@trouver_champ_exterieur
977function trouver_champ_exterieur($cle, $joints, &$boucle, $checkarrivee = false)
978{
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
983 return $table;
984 }
985 }
986 return "";
987}
988
989// determine l'operateur et les operandes
990
991// http://doc.spip.org/@calculer_critere_infixe_ops
992function calculer_critere_infixe_ops($idb, &$boucles, $crit)
993{
994 // cas d'une valeur comparee a elle-meme ou son referent
995 if (count($crit->param) == 0)
996 { $op = '=';
997 $col = $val = $crit->op;
998 // Cas special {lang} : aller chercher $GLOBALS['spip_lang']
999 if ($val == 'lang')
1000 $val = array(kwote('$GLOBALS[\'spip_lang\']'));
1001 else {
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')
1010 $val = 'id_parent';
1011 $val = array(kwote(calculer_argument_precedent($idb, $val, $boucles)));
1012 }
1013 } else {
1014 // comparaison explicite
1015 // le phraseur impose que le premier param soit du texte
1016 $params = $crit->param;
1017 $op = $crit->op;
1018 if ($op == '==') $op = 'REGEXP';
1019 $col = array_shift($params);
1020 $col = $col[0]->texte;
1021
1022 $val = array();
1023 $desc = array('id_mere' => $idb);
1024 $parent = $boucles[$idb]->id_parent;
1025
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";
1037 }
1038 else
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);
1043 }
1044 }
1045
1046 $fct = $args_sql = '';
1047 // fonction SQL ?
1048 if (preg_match('/^(.*)' . SQL_ARGS . '$/', $col, $m)) {
1049 $fct = $m[1];
1050 preg_match('/^\(([^,]*)(.*)\)$/', $m[2], $a);
1051 $col = $a[1];
1052 if (preg_match('/^(\S*)(\s+AS\s+.*)$/i', $col, $m)) {
1053 $col=$m[1];
1054 $args_sql = $m[2];
1055 }
1056 $args_sql .= $a[2];;
1057 }
1058 return array($fct, $col, $op, $val, $args_sql);
1059}
1060
1061// compatibilite ancienne version
1062
1063// http://doc.spip.org/@calculer_vieux_in
1064function calculer_vieux_in($params)
1065{
1066 $deb = $params[0][0];
1067 $k = count($params)-1;
1068 $last = $params[$k];
1069 $j = count($last)-1;
1070 $last = $last[$j];
1071 $n = strlen($last->texte);
1072
1073 if (!(($deb->texte[0] == '(') && ($last->texte[$n-1] == ')')))
1074 return $params;
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);
1080 $newp = array();
1081 foreach($params as $v) {
1082 if ($v[0]->type != 'texte')
1083 $newp[] = $v;
1084 else {
1085 foreach(split(',', $v[0]->texte) as $x) {
1086 $t = new Texte;
1087 $t->texte = $x;
1088 $newp[] = array($t);
1089 }
1090 }
1091 }
1092 return $newp;
1093}
1094
1095// http://doc.spip.org/@calculer_critere_infixe_date
1096function calculer_critere_infixe_date($idb, &$boucles, $regs)
1097{
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];
1102 if ($suite) {
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;
1108 else
1109 $date_orig = substr($suite, 1);
1110 $pred = $date_orig;
1111 } else if ($rel) $pred = 'date';
1112
1113 $date_compare = "\"' . normaliser_date(" .
1114 calculer_argument_precedent($idb, $pred, $boucles) .
1115 ") . '\"";
1116 $date_orig = $boucle->id_table . '.' . $date_orig;
1117
1118 if ($col == 'date') {
1119 $col = $date_orig;
1120 $col_table = '';
1121 }
1122 else if ($col == 'jour') {
1123 $col = "DAYOFMONTH($date_orig)";
1124 $col_table = '';
1125 }
1126 else if ($col == 'mois') {
1127 $col = "MONTH($date_orig)";
1128 $col_table = '';
1129 }
1130 else if ($col == 'annee') {
1131 $col = "YEAR($date_orig)";
1132 $col_table = '';
1133 }
1134 else if ($col == 'heure') {
1135 $col = "DATE_FORMAT($date_orig, '%H:%i')";
1136 $col_table = '';
1137 }
1138 else if ($col == 'age') {
1139 $col = calculer_param_date("now()", $date_orig);
1140 $col_table = '';
1141 }
1142 else if ($col == 'age_relatif') {
1143 $col = calculer_param_date($date_compare, $date_orig);
1144 $col_table = '';
1145 }
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(" .
1152 $date_orig . ")))";
1153 $col_table = '';
1154 }
1155 else if ($col == 'mois_relatif') {
1156 $col = "MONTH(" . $date_compare . ")-MONTH(" .
1157 $date_orig . ")+12*(YEAR(" . $date_compare .
1158 ")-YEAR(" . $date_orig . "))";
1159 $col_table = '';
1160 }
1161 else if ($col == 'annee_relatif') {
1162 $col = "YEAR(" . $date_compare . ")-YEAR(" .
1163 $date_orig . ")";
1164 $col_table = '';
1165 }
1166 return array($col, $col_table);
1167}
1168
1169// http://doc.spip.org/@calculer_param_date
1170function calculer_param_date($date_compare, $date_orig) {
1171 if (ereg("'\" *\.(.*)\. *\"'", $date_compare, $r)) {
1172 $init = "'\" . (\$x = $r[1]) . \"'";
1173 $date_compare = '\'$x\'';
1174 }
1175 else
1176 $init = $date_compare;
1177
1178 return
1179 "LEAST((UNIX_TIMESTAMP(" .
1180 $init .
1181 ")-UNIX_TIMESTAMP(" .
1182 $date_orig .
1183 "))/86400,\n\tTO_DAYS(" .
1184 $date_compare .
1185 ")-TO_DAYS(" .
1186 $date_orig .
1187 "),\n\tDAYOFMONTH(" .
1188 $date_compare .
1189 ")-DAYOFMONTH(" .
1190 $date_orig .
1191 ")+30.4368*(MONTH(" .
1192 $date_compare .
1193 ")-MONTH(" .
1194 $date_orig .
1195 "))+365.2422*(YEAR(" .
1196 $date_compare .
1197 ")-YEAR(" .
1198 $date_orig .
1199 ")))";
1200}
1201?>