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