[SPIP] +2.1.12
[velocampus/web/www.git] / www / ecrire / public / composer.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2011 *
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 if (!defined('_ECRIRE_INC_VERSION')) return;
14
15 include_spip('inc/texte');
16 include_spip('inc/documents');
17 include_spip('inc/distant');
18 include_spip('inc/rubriques'); # pour calcul_branche (cf critere branche)
19 include_spip('inc/acces'); // Gestion des acces pour ical
20 include_spip('public/interfaces');
21 include_spip('public/quete');
22
23 # Charge et retourne un composeur ou '' s'il est inconnu. Le compile au besoin
24 # Charge egalement un fichier homonyme de celui du squelette
25 # mais de suffixe '_fonctions.php' pouvant contenir:
26 # 1. des filtres
27 # 2. des fonctions de traduction de balise, de critere et de boucle
28 # 3. des declaration de tables SQL supplementaires
29 # Toutefois pour 2. et 3. preferer la technique de la surcharge
30
31 // http://doc.spip.org/@public_composer_dist
32 function public_composer_dist($squelette, $mime_type, $gram, $source, $connect='') {
33
34 $nom = calculer_nom_fonction_squel($squelette, $mime_type, $connect);
35
36 // si deja en memoire (INCLURE a repetition) c'est bon.
37
38 if (function_exists($nom)) return array($nom);
39
40 if (isset($GLOBALS['var_mode']) && ($GLOBALS['var_mode'] == 'debug'))
41 $GLOBALS['debug_objets']['courant'] = $nom;
42
43 $phpfile = sous_repertoire(_DIR_SKELS,'',false,true) . $nom . '.php';
44
45 // si squelette est deja compile et perenne, le charger
46 if (!squelette_obsolete($phpfile, $source)
47 AND lire_fichier ($phpfile, $skel_code,
48 array('critique' => 'oui', 'phpcheck' => 'oui')))
49 eval('?'.'>'.$skel_code);
50 # spip_log($skel_code, 'comp')
51 if (@file_exists($lib = $squelette . '_fonctions'.'.php'))
52 include_once $lib;
53
54 // tester si le eval ci-dessus a mis le squelette en memoire
55
56 if (function_exists($nom)) return array($nom, $skel_code);
57
58 // charger le source, si possible, et compiler
59 if (lire_fichier ($source, $skel)) {
60 $compiler = charger_fonction('compiler', 'public');
61 $skel_code = $compiler($skel, $nom, $gram, $source, $connect);
62 }
63
64 // Ne plus rien faire si le compilateur n'a pas pu operer.
65 if (!$skel_code) return false;
66
67 foreach($skel_code as $id => $boucle) {
68 $f = $boucle->return;
69 if (@eval("return true; $f ;") === false) {
70 // Code syntaxiquement faux (critere etc mal programme')
71 $msg = array('zbug_erreur_compilation');
72 erreur_squelette($msg, $boucle);
73 // continuer pour trouver d'autres fautes eventuelles
74 // mais prevenir que c'est mort
75 $nom = '';
76 }
77 // Contexte de compil inutile a present
78 // (mais la derniere valeur de $boucle est utilisee ci-dessous)
79 $skel_code[$id] = $f;
80 }
81
82 if ($nom) {
83 // Si le code est bon, concatener et mettre en cache
84 if (function_exists($nom))
85 $code = squelette_traduit($skel, $source, $phpfile, $skel_code);
86 else {
87 // code semantiquement faux: bug du compilateur
88 // $boucle est en fait ici la fct principale du squelette
89 $msg = array('zbug_erreur_compilation');
90 erreur_squelette($msg, $boucle);
91 $nom = '';
92 }
93 }
94
95 if (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode'] == 'debug') {
96
97 // Tracer ce qui vient d'etre compile
98 $GLOBALS['debug_objets']['code'][$nom . 'tout'] = $code;
99
100 // si c'est ce que demande le debusqueur, lui passer la main
101 if ($GLOBALS['debug_objets']['sourcefile']
102 AND (_request('var_mode_objet') == $nom)
103 AND (_request('var_mode_affiche') == 'code') )
104 erreur_squelette();
105 }
106 return $nom ? array($nom, $code) : false;
107 }
108
109 function squelette_traduit($squelette, $sourcefile, $phpfile, $boucles)
110 {
111
112 // Le dernier index est '' (fonction principale)
113 $noms = substr(join (', ', array_keys($boucles)), 0, -2);
114 if (CODE_COMMENTE)
115 $code = "
116 /*
117 * Squelette : $sourcefile
118 * Date : ".gmdate("D, d M Y H:i:s", @filemtime($sourcefile))." GMT
119 * Compile : ".gmdate("D, d M Y H:i:s", time())." GMT
120 * " . (!$boucles ? "Pas de boucle" : ("Boucles : " . $noms)) ."
121 */ " ;
122
123 $code = '<'. "?php\n" . $code . join('', $boucles) . "\n?" .'>';
124 if (!isset($GLOBALS['var_nocache']) OR !$GLOBALS['var_nocache'])
125 ecrire_fichier($phpfile, $code);
126 return $code;
127 }
128
129 // Le squelette compile est-il trop vieux ?
130 // http://doc.spip.org/@squelette_obsolete
131 function squelette_obsolete($skel, $squelette) {
132 static $date_change = null;
133 // ne verifier la date de mes_fonctions et mes_options qu'une seule fois
134 // par hit
135 if (is_null($date_change)){
136 if (@file_exists($fonc = 'mes_fonctions.php')
137 OR @file_exists($fonc = 'mes_fonctions.php3'))
138 $date_change = @filemtime($fonc); # compatibilite
139 if (defined('_FILE_OPTIONS'))
140 $date_change = max($date_change,@filemtime(_FILE_OPTIONS));
141 }
142 return (
143 (isset($GLOBALS['var_mode']) AND in_array($GLOBALS['var_mode'], array('recalcul','preview','debug')))
144 OR !@file_exists($skel)
145 OR ((@file_exists($squelette)?@filemtime($squelette):0)
146 > ($date = @filemtime($skel)))
147 OR ($date_change > $date)
148 );
149 }
150
151 // Activer l'invalideur de session
152 // http://doc.spip.org/@invalideur_session
153 function invalideur_session(&$Cache, $code=NULL) {
154 $Cache['session']=spip_session();
155 return $code;
156 }
157
158
159 //
160 // Des fonctions diverses utilisees lors du calcul d'une page ; ces fonctions
161 // bien pratiques n'ont guere de logique organisationnelle ; elles sont
162 // appelees par certaines balises au moment du calcul des pages. (Peut-on
163 // trouver un modele de donnees qui les associe physiquement au fichier
164 // definissant leur balise ???
165 //
166
167 // http://doc.spip.org/@echapper_php_callback
168 function echapper_php_callback($r) {
169 static $src = array();
170 static $dst = array();
171
172 // si on recoit un tableau, on est en mode echappement
173 // on enregistre le code a echapper dans dst, et le code echappe dans src
174 if (is_array($r)) {
175 $dst[] = $r[0];
176 return $src[] = '___'.md5($r[0]).'___';
177 }
178
179 // si on recoit une chaine, on est en mode remplacement
180 $r = str_replace($src, $dst, $r);
181 $src = $dst = array(); // raz de la memoire
182 return $r;
183 }
184
185 // http://doc.spip.org/@analyse_resultat_skel
186 function analyse_resultat_skel($nom, $cache, $corps, $source='') {
187 $headers = array();
188
189 // Recupere les < ?php header('Xx: y'); ? > pour $page['headers']
190 // note: on essaie d'attrapper aussi certains de ces entetes codes
191 // "a la main" dans les squelettes, mais evidemment sans exhaustivite
192 if (preg_match_all(
193 '/(<[?]php\s+)@?header\s*\(\s*.([^:\']*):?\s*([^)]*)[^)]\s*\)\s*[;]?\s*[?]>/ims',
194 $corps, $regs, PREG_SET_ORDER)){
195 foreach ($regs as $r) {
196 $corps = str_replace($r[0], '', $corps);
197 # $j = Content-Type, et pas content-TYPE.
198 $j = join('-', array_map('ucwords', explode('-', strtolower($r[2]))));
199
200 if ($j=='X-Spip-Filtre' AND isset($headers[$j]))
201 $headers[$j].="|".$r[3];
202 else
203 $headers[$j] = $r[3];
204 }
205 }
206 // S'agit-il d'un resultat constant ou contenant du code php
207 $process_ins = (
208 strpos($corps,'<'.'?') === false
209 OR strpos(str_replace('<'.'?xml', '', $corps),'<'.'?') === false
210 )
211 ? 'html'
212 : 'php';
213
214 // traiter #FILTRE{} ?
215 if (isset($headers['X-Spip-Filtre'])
216 AND strlen($headers['X-Spip-Filtre'])) {
217 // proteger les <INCLUDE> et tous les morceaux de php
218 if ($process_ins == 'php')
219 $corps = preg_replace_callback(',<[?](\s|php|=).*[?]>,UimsS',
220 'echapper_php_callback', $corps);
221 foreach (explode('|', $headers['X-Spip-Filtre']) as $filtre) {
222 if ($f = chercher_filtre($filtre))
223 $corps = $f($corps);
224 }
225 // restaurer les echappements
226 $corps = echapper_php_callback($corps);
227 unset($headers['X-Spip-Filtre']);
228 }
229
230 return array('texte' => $corps,
231 'squelette' => $nom,
232 'source' => $source,
233 'process_ins' => $process_ins,
234 'invalideurs' => $cache,
235 'entetes' => $headers,
236 'duree' => isset($headers['X-Spip-Cache']) ? intval($headers['X-Spip-Cache']) : 0
237 );
238 }
239
240
241 //
242 // fonction standard de calcul de la balise #INTRODUCTION
243 // on peut la surcharger en definissant dans mes_fonctions :
244 // function filtre_introduction()
245 //
246 // http://doc.spip.org/@filtre_introduction_dist
247 function filtre_introduction_dist($descriptif, $texte, $longueur, $connect) {
248 // Si un descriptif est envoye, on l'utilise directement
249 if (strlen($descriptif))
250 return propre($descriptif,$connect);
251
252 // De preference ce qui est marque <intro>...</intro>
253 $intro = '';
254 $texte = preg_replace(",(</?)intro>,i", "\\1intro>", $texte); // minuscules
255 while ($fin = strpos($texte, "</intro>")) {
256 $zone = substr($texte, 0, $fin);
257 $texte = substr($texte, $fin + strlen("</intro>"));
258 if ($deb = strpos($zone, "<intro>") OR substr($zone, 0, 7) == "<intro>")
259 $zone = substr($zone, $deb + 7);
260 $intro .= $zone;
261 }
262
263 // [12025] On ne *PEUT* pas couper simplement ici car c'est du texte brut,
264 // qui inclus raccourcis et modeles
265 // un simple <articlexx> peut etre ensuite transforme en 1000 lignes ...
266 // par ailleurs le nettoyage des raccourcis ne tient pas compte
267 // des surcharges et enrichissement de propre
268 // couper doit se faire apres propre
269 //$texte = nettoyer_raccourcis_typo($intro ? $intro : $texte, $connect);
270
271 // Cependant pour des questions de perfs on coupe quand meme, en prenant
272 // large et en se mefiant des tableaux #1323
273
274 if (strlen($intro))
275 $texte = $intro;
276
277 else
278 if (strpos("\n".$texte, "\n|")===false
279 AND strlen($texte) > 2.5*$longueur)
280 $texte = couper($texte, 2*$longueur);
281
282 // ne pas tenir compte des notes
283 $notes = charger_fonction('notes', 'inc');
284 $notes('','empiler');
285 $texte = propre($texte,$connect);
286 $notes('','depiler');
287
288 if (!defined('_INTRODUCTION_SUITE')) define('_INTRODUCTION_SUITE', '&nbsp;(...)');
289 $texte = couper($texte, $longueur, _INTRODUCTION_SUITE);
290
291 return $texte;
292 }
293
294 //
295 // Balises dynamiques
296 //
297
298 // elles sont traitees comme des inclusions
299 // http://doc.spip.org/@synthetiser_balise_dynamique
300
301 define('CODE_INCLURE_BALISE', '<' . '?php
302 include_once("./" . _DIR_RACINE . "%s");
303 if ($lang_select = "%s") $lang_select = lang_select($lang_select);
304 inserer_balise_dynamique(balise_%s_dyn(%s), array(%s));
305 if ($lang_select) lang_select();
306 ?'
307 .'>');
308
309
310 function synthetiser_balise_dynamique($nom, $args, $file, $context_compil) {
311 $r = sprintf(CODE_INCLURE_BALISE,
312 $file,
313 $context_compil[4]?$context_compil[4]:'',
314 $nom,
315 join(', ', array_map('argumenter_squelette', $args)),
316 join(', ', array_map('_q', $context_compil)));
317 return $r;
318 }
319
320 // http://doc.spip.org/@argumenter_squelette
321 function argumenter_squelette($v) {
322
323 if (!is_array($v))
324 return "'" . texte_script($v) . "'";
325 else {
326 $out = array();
327 foreach($v as $k=>$val)
328 $out [] = argumenter_squelette($k) . '=>' . argumenter_squelette($val);
329 return 'array(' . join(", ", $out) . ')';
330 }
331 }
332
333 // verifier leurs arguments et filtres, et calculer le code a inclure
334 // http://doc.spip.org/@executer_balise_dynamique
335 function executer_balise_dynamique($nom, $args, $context_compil) {
336 $p = strpos($nom,"_");
337 $nomfonction = $nom;
338 $nomfonction_generique = substr($nom,0,$p+1);
339 if (!$file = include_spip("balise/". strtolower($nomfonction))) {
340 // pas de fichier associe, passer au traitement generique
341 $file = include_spip("balise/" .strtolower($nomfonction_generique));
342 if ($file) {
343 // et injecter en premier arg le nom de la balise
344 array_unshift($args,$nom);
345 // et passer sur la fonction generique pour la suite
346 $nomfonction = $nomfonction_generique;
347 }
348 else {
349 $msg = array('zbug_balise_inexistante',array('from'=>'CVT','balise'=>$nom));
350 erreur_squelette($msg, $context_compil);
351 return '';
352 }
353 }
354 // Y a-t-il une fonction de traitement des arguments ?
355 $f = 'balise_' . $nomfonction . '_stat';
356
357 $r = !function_exists($f) ? $args : $f($args, $context_compil);
358
359 if (!is_array($r)) return $r;
360
361 // verifier que la fonction dyn est la,
362 // sinon se replier sur la generique si elle existe
363 if (!function_exists('balise_' . $nomfonction . '_dyn')) {
364 $file = include_spip("balise/" .strtolower($nomfonction_generique));
365 if (function_exists('balise_' . $nomfonction_generique . '_dyn')) {
366 // et lui injecter en premier arg le nom de la balise
367 array_unshift($r,$nom);
368 $nomfonction = $nomfonction_generique;
369 } else {
370 $msg = array('zbug_balise_inexistante',array('from'=>'CVT','balise'=>$nom));
371 erreur_squelette($msg, $context_compil);
372 return '';
373 }
374 }
375
376 if (!_DIR_RESTREINT)
377 $file = _DIR_RESTREINT_ABS . $file;
378 return synthetiser_balise_dynamique($nomfonction, $r, $file, $context_compil);
379 }
380
381 // http://doc.spip.org/@lister_objets_avec_logos
382 function lister_objets_avec_logos ($type) {
383 global $formats_logos;
384 $logos = array();
385 $chercher_logo = charger_fonction('chercher_logo', 'inc');
386 $type = '/'
387 . type_du_logo($type)
388 . "on(\d+)\.("
389 . join('|',$formats_logos)
390 . ")$/";
391
392 if ($d = @opendir(_DIR_LOGOS)) {
393 while($f = readdir($d)) {
394 if (preg_match($type, $f, $r))
395 $logos[] = $r[1];
396 }
397 }
398 @closedir($d);
399 return join(',',$logos);
400 }
401
402 // fonction appelee par la balise #NOTES
403 // Renvoyer l'etat courant des notes, le purger et en preparer un nouveau
404 // http://doc.spip.org/@calculer_notes
405 function calculer_notes() {
406 $notes = charger_fonction('notes', 'inc');
407 $r = $notes(array());
408 $notes('','depiler');
409 $notes('','empiler');
410 return $r;
411 }
412
413 // Selectionner la langue de l'objet dans la boucle, sauf dans les
414 // cas ou il ne le faut pas :-)
415 function lang_select_public($lang, $lang_select, $titre=null) {
416 // Cas 1. forcer_lang = true et pas de critere {lang_select}
417 if (isset($GLOBALS['forcer_lang']) AND $GLOBALS['forcer_lang']
418 AND $lang_select !== 'oui')
419 return;
420
421 // Cas 2. l'objet n'a pas de langue definie (ou definie a '')
422 if (!strlen($lang))
423 return;
424
425 // Cas 3. l'objet est multilingue !
426 if ($lang_select !== 'oui'
427 AND strlen($titre) > 10
428 AND strpos($titre, '<multi>') !== false
429 AND strpos(echappe_html($titre), '<multi>') !== false)
430 return;
431
432 // Tous les cas ayant ete elimines, faire le job
433 $GLOBALS['spip_lang'] = $lang;
434 return;
435 }
436
437
438 // Si un tableau &doublons[articles] est passe en parametre,
439 // il faut le nettoyer car il pourrait etre injecte en SQL
440 // http://doc.spip.org/@nettoyer_env_doublons
441 function nettoyer_env_doublons($envd) {
442 foreach ($envd as $table => $liste) {
443 $n = '';
444 foreach(explode(',',$liste) as $val) {
445 if ($a = intval($val) AND $val === strval($a))
446 $n.= ','.$val;
447 }
448 if (strlen($n))
449 $envd[$table] = $n;
450 else
451 unset($envd[$table]);
452 }
453 return $envd;
454 }
455
456 // http://doc.spip.org/@match_self
457 function match_self($w){
458 if (is_string($w)) return false;
459 if (is_array($w)) {
460 if (in_array(reset($w),array("SELF","SUBSELECT"))) return $w;
461 foreach($w as $sw)
462 if ($m=match_self($sw)) return $m;
463 }
464 return false;
465 }
466 // http://doc.spip.org/@remplace_sous_requete
467 function remplace_sous_requete($w,$sousrequete){
468 if (is_array($w)) {
469 if (in_array(reset($w),array("SELF","SUBSELECT"))) return $sousrequete;
470 foreach($w as $k=>$sw)
471 $w[$k] = remplace_sous_requete($sw,$sousrequete);
472 }
473 return $w;
474 }
475 // http://doc.spip.org/@trouver_sous_requetes
476 function trouver_sous_requetes($where){
477 $where_simples = array();
478 $where_sous = array();
479 foreach($where as $k=>$w){
480 if (match_self($w)) $where_sous[$k] = $w;
481 else $where_simples[$k] = $w;
482 }
483 return array($where_simples,$where_sous);
484 }
485
486 // La fonction presente dans les squelettes compiles
487
488 // http://doc.spip.org/@calculer_select
489 function calculer_select ($select = array(), $from = array(),
490 $from_type = array(),
491 $where = array(), $join=array(),
492 $groupby = array(), $orderby = array(), $limit = '',
493 $having=array(), $table = '', $id = '', $serveur='', $requeter=true) {
494
495 // retirer les criteres vides:
496 // {X ?} avec X absent de l'URL
497 // {par #ENV{X}} avec X absent de l'URL
498 // IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil)
499
500 $menage = false;
501 foreach($where as $k => $v) {
502 if (is_array($v)){
503 if ((count($v)>=2) && ($v[0]=='REGEXP') && ($v[2]=="'.*'")) $op= false;
504 elseif ((count($v)>=2) && ($v[0]=='LIKE') && ($v[2]=="'%'")) $op= false;
505 else $op = $v[0] ? $v[0] : $v;
506 } else $op = $v;
507 if ((!$op) OR ($op==1) OR ($op=='0=0')) {
508 unset($where[$k]);
509 $menage = true;
510 }
511 }
512
513 // evacuer les eventuels groupby vide issus d'un calcul dynamique
514 $groupby = array_diff($groupby,array(''));
515
516 // remplacer les sous requetes recursives au calcul
517 list($where_simples,$where_sous) = trouver_sous_requetes($where);
518 //var_dump($where_sous);
519 foreach($where_sous as $k=>$w) {
520 $menage = true;
521 // on recupere la sous requete
522 $sous = match_self($w);
523 if ($sous[0]=='SELF') {
524 // c'est une sous requete identique a elle meme sous la forme (SELF,$select,$where)
525 array_push($where_simples,$sous[2]);
526 $where[$k] = remplace_sous_requete($w,"(".calculer_select(
527 $sous[1],
528 $from,
529 $from_type,
530 array($sous[2],'0=0'), // pour accepter une string et forcer a faire le menage car on a surement simplifie select et where
531 $join,
532 array(),array(),'',
533 $having,$table,$id,$serveur,false).")");
534 }
535 if ($sous[0]=='SUBSELECT') {
536 // c'est une sous requete explicite sous la forme identique a sql_select : (SUBSELECT,$select,$from,$where,$groupby,$orderby,$limit,$having)
537 array_push($where_simples,$sous[3]); // est-ce utile dans ce cas ?
538 $where[$k] = remplace_sous_requete($w,"(".calculer_select(
539 $sous[1], # select
540 $sous[2], #from
541 array(), #from_type
542 $sous[3]?(is_array($sous[3])?$sous[3]:array($sous[3])):array(), #where, qui peut etre de la forme string comme dans sql_select
543 array(), #join
544 $sous[4]?$sous[4]:array(), #groupby
545 $sous[5]?$sous[5]:array(), #orderby
546 $sous[6], #limit
547 $sous[7]?$sous[7]:array(), #having
548 $table,$id,$serveur,false
549 ).")");
550 }
551 array_pop($where_simples);
552 }
553
554 foreach($having as $k => $v) {
555 if ((!$v) OR ($v==1) OR ($v=='0=0')) {
556 unset($having[$k]);
557 }
558 }
559
560 // Installer les jointures.
561 // Retirer celles seulement utiles aux criteres finalement absents mais
562 // parcourir de la plus recente a la moins recente pour pouvoir eliminer Ln
563 // si elle est seulement utile a Ln+1 elle meme inutile
564
565 $afrom = array();
566 $equiv = array();
567 $k = count($join);
568 foreach(array_reverse($join,true) as $cledef=>$j) {
569 $cle = $cledef;
570 // le format de join est :
571 // array(table depart, cle depart [,cle arrivee[,condition optionnelle and ...]])
572 if (count($join[$cle])==2) $join[$cle][] = $join[$cle][1];
573 if (count($join[$cle])==3) $join[$cle][] = '';
574 list($t,$c,$carr,$and) = $join[$cle];
575 // si le nom de la jointure n'a pas ete specifiee, on prend Lx avec x sont rang dans la liste
576 // pour compat avec ancienne convention
577 if (is_numeric($cle))
578 $cle = "L$k";
579 if (!$menage
580 OR isset($afrom[$cle])
581 OR calculer_jointnul($cle, $select)
582 OR calculer_jointnul($cle, array_diff($join,array($cle=>$join[$cle])))
583 OR calculer_jointnul($cle, $having)
584 OR calculer_jointnul($cle, $where_simples)) {
585 // on garde une ecriture decomposee pour permettre une simplification ulterieure si besoin
586 // sans recours a preg_match
587 // un implode(' ',..) est fait dans reinjecte_joint un peu plus bas
588 $afrom[$t][$cle] = array("\n" .
589 (isset($from_type[$cle])?$from_type[$cle]:"INNER")." JOIN",
590 $from[$cle],
591 "AS $cle",
592 "ON (",
593 "$cle.$c",
594 "=",
595 "$t.$carr",
596 ($and ? "AND ". $and:"") .
597 ")");
598 if (isset($afrom[$cle])){
599 $afrom[$t] = $afrom[$t] + $afrom[$cle];
600 unset($afrom[$cle]);
601 }
602 $equiv[]= $carr;
603 } else { unset($join[$cledef]);}
604 unset($from[$cle]);
605 $k--;
606 }
607
608 if (count($afrom)) {
609 // Regarder si la table principale ne sert finalement a rien comme dans
610 //<BOUCLE3(MOTS){id_article}{id_mot}> class='on'</BOUCLE3>
611 //<BOUCLE2(MOTS){id_article} />#TOTAL_BOUCLE<//B2>
612 //<BOUCLE5(RUBRIQUES){id_mot}{tout} />#TOTAL_BOUCLE<//B5>
613 // ou dans
614 //<BOUCLE8(HIERARCHIE){id_rubrique}{tout}{type='Squelette'}{inverse}{0,1}{lang_select=non} />#TOTAL_BOUCLE<//B8>
615 // qui comporte plusieurs jointures
616 // ou dans
617 // <BOUCLE6(ARTICLES){id_mot=2}{statut==.*} />#TOTAL_BOUCLE<//B6>
618 // <BOUCLE7(ARTICLES){id_mot>0}{statut?} />#TOTAL_BOUCLE<//B7>
619 // penser a regarder aussi la clause orderby pour ne pas simplifier abusivement
620 // <BOUCLE9(ARTICLES){recherche truc}{par titre}>#ID_ARTICLE</BOUCLE9>
621 // penser a regarder aussi la clause groubpy pour ne pas simplifier abusivement
622 // <BOUCLE10(EVENEMENTS){id_rubrique} />#TOTAL_BOUCLE<//B10>
623
624 list($t,$c) = each($from);
625 reset($from);
626 $e = '/\b(' . "$t\\." . join("|" . $t . '\.', $equiv) . ')\b/';
627 if (!(strpos($t, ' ') OR // jointure des le depart cf boucle_doc
628 calculer_jointnul($t, $select, $e) OR
629 calculer_jointnul($t, $join, $e) OR
630 calculer_jointnul($t, $where, $e) OR
631 calculer_jointnul($t, $orderby, $e) OR
632 calculer_jointnul($t, $groupby, $e) OR
633 calculer_jointnul($t, $having, $e))
634 && count($afrom[$t])) {
635 reset($afrom[$t]);
636 list($nt,$nfrom) = each($afrom[$t]);
637 unset($from[$t]);
638 $from[$nt] = $nfrom[1];
639 unset($afrom[$t][$nt]);
640 $afrom[$nt] = $afrom[$t];
641 unset($afrom[$t]);
642 $e = '/\b'.preg_quote($nfrom[6]).'\b/';
643 $t = $nfrom[4];
644 $alias = "";
645 // verifier que les deux cles sont homonymes, sinon installer un alias dans le select
646 $oldcle = explode('.',$nfrom[6]);
647 $oldcle = end($oldcle);
648 $newcle = explode('.',$nfrom[4]);
649 $newcle = end($newcle);
650 if ($newcle!=$oldcle){
651 $alias = ", ".$nfrom[4]." AS $oldcle";
652 }
653 $select = remplacer_jointnul($t . $alias, $select, $e);
654 $join = remplacer_jointnul($t, $join, $e);
655 $where = remplacer_jointnul($t, $where, $e);
656 $having = remplacer_jointnul($t, $having, $e);
657 $groupby = remplacer_jointnul($t, $groupby, $e);
658 $orderby = remplacer_jointnul($t, $orderby, $e);
659 }
660 $from = reinjecte_joint($afrom, $from);
661 }
662 $GLOBALS['debug']['aucasou'] = array ($table, $id, $serveur, $requeter);
663 $r = sql_select($select, $from, $where,
664 $groupby, array_filter($orderby), $limit, $having, $serveur, $requeter);
665 unset($GLOBALS['debug']['aucasou']);
666 return $r;
667 }
668
669 //condition suffisante (mais non necessaire) pour qu'une table soit utile
670
671 // http://doc.spip.org/@calculer_jointnul
672 function calculer_jointnul($cle, $exp, $equiv='')
673 {
674 if (!is_array($exp)) {
675 if ($equiv) $exp = preg_replace($equiv, '', $exp);
676 return preg_match("/\\b$cle\\./", $exp);
677 } else {
678 foreach($exp as $v) {
679 if (calculer_jointnul($cle, $v, $equiv)) return true;
680 }
681 return false;
682 }
683 }
684
685 // http://doc.spip.org/@reinjecte_joint
686 function reinjecte_joint($afrom, $from)
687 {
688 $from_synth = array();
689 foreach($from as $k=>$v){
690 $from_synth[$k]=$from[$k];
691 if (isset($afrom[$k])) {
692 foreach($afrom[$k] as $kk=>$vv) $afrom[$k][$kk] = implode(' ',$afrom[$k][$kk]);
693 $from_synth["$k@"]= implode(' ',$afrom[$k]);
694 unset($afrom[$k]);
695 }
696 }
697 return $from_synth;
698 }
699
700 // http://doc.spip.org/@remplacer_jointnul
701 function remplacer_jointnul($cle, $exp, $equiv='')
702 {
703 if (!is_array($exp)) {
704 return preg_replace($equiv, $cle, $exp);
705 } else {
706 foreach($exp as $k => $v) {
707 $exp[$k] = remplacer_jointnul($cle, $v, $equiv);
708 }
709 return $exp;
710 }
711 }
712
713 // calcul du nom du squelette
714 // http://doc.spip.org/@calculer_nom_fonction_squel
715 function calculer_nom_fonction_squel($skel, $mime_type='html', $connect='')
716 {
717 // ne pas doublonner les squelette selon qu'ils sont calcules depuis ecrire/ ou depuis la racine
718 if (strlen(_DIR_RACINE) AND substr($skel,0,strlen(_DIR_RACINE))==_DIR_RACINE)
719 $skel = substr($skel,strlen(_DIR_RACINE));
720 return $mime_type
721 . (!$connect ? '' : preg_replace('/\W/',"_", $connect)) . '_'
722 . md5($GLOBALS['spip_version_code'] . ' * ' . $skel);
723 }
724
725 ?>