Completed
Push — spip-2-stable ( d79e3d )
by cam
30:47 queued 16:59
created

compiler.php ➔ calculer_boucle_nonrec()   F

Complexity

Conditions 39
Paths > 20000

Size

Total Lines 143
Code Lines 93

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 39
eloc 93
nc 2009088
nop 3
dl 0
loc 143
rs 2
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2016                                                *
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
// Fichier principal du compilateur de squelettes
15
//
16
17
if (!defined('_ECRIRE_INC_VERSION')) return;
18
19
// reperer un code ne calculant rien, meme avec commentaire
20
define('CODE_MONOTONE', ",^(\n//[^\n]*\n)?\(?'([^'])*'\)?$,");
21
// s'il faut commenter le code produit
22
define('CODE_COMMENTE', true);
23
24
// definition des structures de donnees
25
include_spip('public/interfaces');
26
27
// Definition de la structure $p, et fonctions de recherche et de reservation
28
// dans l'arborescence des boucles
29
include_spip('public/references');
30
31
// definition des boucles
32
include_spip('public/boucles');
33
34
// definition des criteres
35
include_spip('public/criteres');
36
37
// definition des balises
38
include_spip('public/balises');
39
40
// Gestion des jointures
41
include_spip('public/jointures');
42
43
// Les 2 ecritures INCLURE{A1,A2,A3...} et INCLURE(A1){A2}{A3}... sont admises
44
// Preferer la premiere.
45
// Les Ai sont de la forme Vi=Ei ou bien Vi qui veut alors dire Vi=Vi
46
// Le resultat est un tableau indexe par les Vi
47
// Toutefois, si le premier argument n'est pas de la forme Vi=Ei
48
// il est conventionnellement la valeur de l'index 1.
49
// pour la balise #INCLURE
50
// mais pas pour <INCLURE> dont le fond est defini explicitement.
51
52
53
// http://doc.spip.org/@argumenter_inclure
54
function argumenter_inclure($params, $rejet_filtres, $p, &$boucles, $id_boucle, $echap = true, $lang = '', $fond1 = false){
0 ignored issues
show
Unused Code introduced by
The parameter $rejet_filtres is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
55
	$l = array();
56
	$erreur_p_i_i = '';
57
	if (!is_array($params)) return $l;
58
	foreach ($params as $k => $couple){
59
		// la liste d'arguments d'inclusion peut se terminer par un filtre
60
		$filtre = array_shift($couple);
61
		if ($filtre) break;
62
		foreach ($couple as $n => $val){
63
			$var = $val[0];
64
			if ($var->type!='texte'){
65
				if ($n OR $k OR $fond1){
66
					$erreur_p_i_i = array('zbug_parametres_inclus_incorrects',
67
						array('param' => $var->nom_champ));
68
					erreur_squelette($erreur_p_i_i, $p);
69
				} else $l[1] = calculer_liste($val, $p->descr, $boucles, $id_boucle);
70
				break;
71
			} else {
72
				preg_match(",^([^=]*)(=?)(.*)$,", $var->texte, $m);
73
				$var = $m[1];
74
				$auto = false;
75
				;
76
				if ($m[2]){
77
					$v = $m[3];
78
					if (preg_match(',^[\'"](.*)[\'"]$,', $v, $m)) $v = $m[1];
79
					$val[0] = new Texte;
80
					$val[0]->texte = $v;
81
				} elseif ($k OR $n OR $fond1) {
82
					$auto = true;
83
				} else $var = 1;
84
85
				if ($var=='lang'){
86
					$lang = !$auto
87
						? calculer_liste($val, $p->descr, $boucles, $id_boucle)
88
						: '$GLOBALS["spip_lang"]';
89
				} else {
90
					$val = $auto
91
						? index_pile($id_boucle, $var, $boucles)
92
						: calculer_liste($val, $p->descr, $boucles, $id_boucle);
93
					if ($var!==1)
94
						$val = ($echap ? "\'$var\' => ' . argumenter_squelette(" : "'$var' => ")
95
							. $val . ($echap ? ") . '" : " ");
96
					else $val = $echap ? "'.$val.'" : $val;
97
					$l[$var] = $val;
98
				}
99
			}
100
		}
101
	}
102
	if ($erreur_p_i_i) return false;
103
	// Cas particulier de la langue : si {lang=xx} est definie, on
104
	// la passe, sinon on passe la langue courante au moment du calcul
105
	// sauf si on n'en veut pas 
106
	if ($lang===false) return $l;
107
	if (!$lang) $lang = '$GLOBALS["spip_lang"]';
108
	$l['lang'] = ($echap ? "\'lang\' => ' . argumenter_squelette(" : "'lang' => ") . $lang . ($echap ? ") . '" : " ");
109
110
	return $l;
111
}
112
113
//
114
// Calculer un <INCLURE()>
115
// La constante ci-dessous donne le code general quand il s'agit d'un script.
116
117
define('CODE_INCLURE_SCRIPT', 'if (($path = %s) AND is_readable($path))
118
	include $path;
119
else erreur_squelette(array("fichier_introuvable", array("fichier" => "%s")), array(%s));'
120
);
121
122
// // et celle-ci pour un squelette (aussi pour #INCLURE, #MODELE #LES_AUTEURS)
123
124
define('CODE_RECUPERER_FOND', 'recuperer_fond(%s, %s, array(%s), %s)');
125
126
// http://doc.spip.org/@calculer_inclure
127
function calculer_inclure($p, &$boucles, $id_boucle){
128
129
	$_contexte = argumenter_inclure($p->param, false, $p, $boucles, $id_boucle, true, '', true);
130
	if (is_string($p->texte)){
131
		$fichier = $p->texte;
132
		$code = "\"$fichier\"";
133
134
	} else {
135
		$code = calculer_liste($p->texte, $p->descr, $boucles, $id_boucle);
136
		if ($code AND preg_match("/^'([^']*)'/s", $code, $r))
137
			$fichier = $r[1];
138
		else $fichier = '';
139
	}
140
	if (!$code OR $code==='""'){
141
		$erreur_p_i_i = array('zbug_parametres_inclus_incorrects',
142
			array('param' => $code));
143
		erreur_squelette($erreur_p_i_i, $p);
144
		return false;
145
	}
146
	$compil = texte_script(memoriser_contexte_compil($p));
147
148
	if (is_array($_contexte)){
149
		// Critere d'inclusion {env} (et {self} pour compatibilite ascendante)
150
		if ($env = (isset($_contexte['env']) || isset($_contexte['self']))){
151
			unset($_contexte['env']);
152
		}
153
154
		// noter les doublons dans l'appel a public.php
155
		if (isset($_contexte['doublons'])){
156
			$_contexte['doublons'] = "\\'doublons\\' => '.var_export(\$doublons,true).'";
157
		}
158
159
		if ($ajax = isset($_contexte['ajax']))
160
			unset($_contexte['ajax']);
161
162
		$_contexte = join(",\n\t", $_contexte);
163
	}
164
	else
165
		return false; // j'aurais voulu toucher le fond ...
166
167
	$contexte = 'array(' . $_contexte . ')';
168
169
	if ($env){
170
		$contexte = "array_merge('.var_export(\$Pile[0],1).',$contexte)";
171
	}
172
173
	// s'il y a une extension .php, ce n'est pas un squelette
174
	if (preg_match('/^.+[.]php$/s', $fichier)){
175
		// si inexistant, on essaiera a l'execution
176
		if ($path = find_in_path($fichier))
177
			$path = "\"$path\"";
178
		else $path = "find_in_path(\"$fichier\")";
179
180
		$code = sprintf(CODE_INCLURE_SCRIPT, $path, $fichier, $compil);
181
	} else {
182
		$_options[] = "\"compil\"=>array($compil)";
0 ignored issues
show
Coding Style Comprehensibility introduced by
$_options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $_options = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
183
		if ($ajax)
184
			$_options[] = "\"ajax\"=>true";
185
		$code = " ' . argumenter_squelette($code) . '";
186
		$code = "echo " . sprintf(CODE_RECUPERER_FOND, $code, $contexte, implode(',', $_options), "_request(\"connect\")") . ';';
187
	}
188
189
	return "\n'<'.'" . "?php " . $code . "\n?'." . "'>'";
190
}
191
192
//
193
// calculer_boucle() produit le corps PHP d'une boucle Spip. 
194
// ce corps remplit une variable $t0 retournee en valeur.
195
// Ici on distingue boucles recursives et boucle a requete SQL
196
// et on insere le code d'envoi au debusqueur du resultat de la fonction.
197
198
// http://doc.spip.org/@calculer_boucle
199
function calculer_boucle($id_boucle, &$boucles){
200
201
	$boucles[$id_boucle] = pipeline('post_boucle', $boucles[$id_boucle]);
202
203
	// en mode debug memoriser les premiers passages dans la boucle,
204
	// mais pas tous, sinon ca pete.
205
	if (_request('var_mode_affiche')!='resultat')
206
		$trace = '';
207
	else {
208
		$trace = $boucles[$id_boucle]->descr['nom'] . $id_boucle;
209
		$trace = "if (count(@\$GLOBALS['debug_objets']['resultat']['$trace'])<3)
210
	    \$GLOBALS['debug_objets']['resultat']['$trace'][] = \$t0;";
211
	}
212
	return ($boucles[$id_boucle]->type_requete=='boucle')
213
		? calculer_boucle_rec($id_boucle, $boucles, $trace)
214
		: calculer_boucle_nonrec($id_boucle, $boucles, $trace);
215
}
216
217
// compil d'une boucle recursive. 
218
// il suffit (ET IL FAUT) sauvegarder les valeurs des arguments passes par
219
// reference, car par definition un tel passage ne les sauvegarde pas
220
221
// http://doc.spip.org/@calculer_boucle_rec
222
function calculer_boucle_rec($id_boucle, &$boucles, $trace){
223
	$nom = $boucles[$id_boucle]->param[0];
224
	return "\n\t\$save_numrows = (\$Numrows['$nom']);"
225
		. "\n\t\$t0 = " . $boucles[$id_boucle]->return . ";"
226
		. "\n\t\$Numrows['$nom'] = (\$save_numrows);"
227
		. $trace
228
		. "\n\treturn \$t0;";
229
}
230
231
// Compilation d'une boucle non recursive. 
232
// Ci-dessous la constante donnant le cadre systematique du code:
233
// %s1: initialisation des arguments de calculer_select
234
// %s2: appel de calculer_select en donnant un contexte pour les cas d'erreur
235
// %s3: initialisation du sous-tableau Numrows[id_boucle]
236
// %s4: sauvegarde de la langue et calcul des invariants de boucle sur elle
237
// %s5: boucle while sql_fetch ou str_repeat si corps monotone
238
// %s6: restauration de la langue
239
// %s7: liberation de la ressource, en tenant compte du serveur SQL 
240
// %s8: code de trace eventuel avant le retour
241
242
define('CODE_CORPS_BOUCLE', '%s
243
	$t0 = "";
244
	// REQUETE
245
	$result = calculer_select($select, $from, $type, $where, $join, $groupby, $orderby, $limit, $having, $table, $id, $connect,
246
		 array(%s));
247
	if ($result) {
248
	%s%s$SP++;
249
	// RESULTATS
250
	%s
251
	%s@sql_free($result%s);
252
	}%s
253
	return $t0;'
254
);
255
256
// http://doc.spip.org/@calculer_boucle_nonrec
257
function calculer_boucle_nonrec($id_boucle, &$boucles, $trace){
258
259
	$boucle = &$boucles[$id_boucle];
260
	$return = $boucle->return;
261
	$type_boucle = $boucle->type_requete;
262
	$primary = $boucle->primary;
263
	$constant = preg_match(CODE_MONOTONE, str_replace("\\'", '', $return));
264
	$flag_cpt = $boucle->mode_partie || $boucle->cptrows;
265
    $_doublons = $boucle->doublons;
266
	$corps = '';
267
268
    // un peu dommage de calculer la clause Where ici et une 2e fois plus loin,
269
    // mais faudrait revoir la representation des doublons pour l'eviter
270
	if ($_doublons AND strpos(calculer_dump_array($boucle->where), $_doublons)) {
271
	// faudrait expanser le foreach a la compil, car y en a souvent qu'un 
272
	// et puis faire un [] plutot qu'un "','."
273
		$corps = "\n\t\t\tforeach($_doublons as " . '$k) $doublons[$k] .= "," . ' .
274
			index_pile($id_boucle, $primary, $boucles)
275
			. "; // doublons\n";
276
    } else $_doublons = '';
277
278
	// La boucle doit-elle selectionner la langue ?
279
	// -. par defaut, les boucles suivantes le font
280
	//    (sauf si forcer_lang==true ou si le titre contient <multi>).
281
	// - . a moins d'une demande explicite via {!lang_select}
282
	if (!$constant && $boucle->lang_select!='non' &&
283
		(($boucle->lang_select=='oui') ||
284
			in_array($type_boucle, array(
285
				'articles', 'rubriques', 'hierarchie', 'breves'
286
			)))
287
	){
288
		// Memoriser la langue avant la boucle et la restituer apres
289
		// afin que le corps de boucle affecte la globale directement
290
		$init_lang = "lang_select(\$GLOBALS['spip_lang']);\n\t";
291
		$fin_lang = "lang_select();\n\t";
292
293
		$corps .=
294
			"\n\t\tlang_select_public("
295
				. index_pile($id_boucle, 'lang', $boucles)
296
				. ", '" . $boucle->lang_select . "'"
297
				. (in_array($type_boucle, array(
298
				'articles', 'rubriques', 'hierarchie', 'breves'
299
			)) ? ', ' . index_pile($id_boucle, 'titre', $boucles) : '')
300
				. ');';
301
	}
302
	else {
303
		$init_lang = '';
304
		$fin_lang = '';
305
		// sortir les appels au traducteur (invariants de boucle)
306
		if (strpos($return, '?php')===false
307
			AND preg_match_all("/\W(_T[(]'[^']*'[)])/", $return, $r)
308
		){
309
			$i = 1;
310
			foreach ($r[1] as $t){
311
				$init_lang .= "\n\t\$l$i = $t;";
312
				$return = str_replace($t, "\$l$i", $return);
313
				$i++;
314
			}
315
		}
316
	}
317
318
	// gestion optimale des separateurs et des boucles constantes
319
	if (count($boucle->separateur))
320
		$code_sep = ("'" . str_replace("'", "\'", join('', $boucle->separateur)) . "'");
321
322
	$corps .=
323
		((!$boucle->separateur) ?
324
			(($constant && !$corps && !$flag_cpt) ? $return :
325
				(($return==="''") ? '' :
326
					("\n\t\t" . '$t0 .= ' . $return . ";"))) :
327
			("\n\t\t\$t1 " .
328
				((strpos($return, '$t1.')===0) ?
329
					(".=" . substr($return, 4)) :
330
					('= ' . $return)) .
331
				";\n\t\t" .
332
				'$t0 .= (($t1 && $t0) ? ' . $code_sep . " : '') . \$t1;"));
0 ignored issues
show
Bug introduced by
The variable $code_sep does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
333
334
	// Calculer les invalideurs si c'est une boucle non constante et si on
335
	// souhaite invalider ces elements
336
	if (!$constant AND $primary){
337
		include_spip('inc/invalideur');
338
		if (function_exists($i = 'calcul_invalideurs'))
339
			$corps = $i($corps, $primary, $boucles, $id_boucle);
340
	}
341
342
	// gerer le compteur de boucle 
343
	// avec ou sans son utilisation par les criteres {1/3} {1,4} {n-2,1}...
344
345
	if ($boucle->partie OR $boucle->cptrows)
346
		$corps = "\n\t\t\$Numrows['$id_boucle']['compteur_boucle']++;"
347
			. $boucle->partie
348
			. $corps;
349
350
	$serveur = !$boucle->sql_serveur ? ''
351
		: (', ' . _q($boucle->sql_serveur));
352
353
	// si le corps est une constante, ne pas appeler le serveur N fois!
354
355
	if (preg_match(CODE_MONOTONE, str_replace("\\'", '', $corps), $r)){
356
		if (!isset($r[2]) OR (!$r[2])){
357
			if (!$boucle->numrows)
358
				return "\n\t\$t0 = '';";
359
			else
360
				$corps = "";
361
		} else {
362
			$boucle->numrows = true;
363
			$corps = "\n\t\$t0 = str_repeat($corps, \$Numrows['$id_boucle']['total']);";
364
		}
365
	} else $corps = "while (\$Pile[\$SP] = @sql_fetch(\$result$serveur)) {\n$corps\n	}";
366
367
	$count = '';
368
	if (!$boucle->select){
369
		if (!$boucle->numrows OR $boucle->limit OR $boucle_mode_partie OR $boucle->group)
0 ignored issues
show
Bug introduced by
The variable $boucle_mode_partie does not exist. Did you mean $boucle?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
370
			$count = '1';
371
		else $count = 'count(*)';
372
		$boucles[$id_boucle]->select[] = $count;
373
	}
374
375
	if ($flag_cpt)
376
		$nums = "\n\t// COMPTEUR\n\t"
377
			. "\$Numrows['$id_boucle']['compteur_boucle'] = 0;\n\t";
378
	else $nums = '';
379
380
	if ($boucle->numrows OR $boucle->mode_partie){
381
		if ($count=='count(*)')
382
			$count = "(\$cc=sql_fetch(\$result$serveur))?array_shift(\$cc):0";
383
		else
384
			$count = "sql_count(\$result$serveur)";
385
		$nums .= "\$Numrows['$id_boucle']['total'] = @intval($count);"
386
			. $boucle->mode_partie
387
			. "\n\t";
388
	}
389
390
	// Ne calculer la requete que maintenant
391
	// car ce qui precede appelle index_pile qui influe dessus
392
393
	$init = ($_doublons ? ("\n\t$_doublons = array();") : '')
394
		. calculer_requete_sql($boucles[$id_boucle]);
395
396
	$contexte = memoriser_contexte_compil($boucle);
397
398
	return sprintf(CODE_CORPS_BOUCLE, $init, $contexte, $nums, $init_lang, $corps, $fin_lang, $serveur, $trace);
399
}
400
401
402
// http://doc.spip.org/@calculer_requete_sql
403
function calculer_requete_sql($boucle){
404
	return ($boucle->hierarchie ? "\n\t$boucle->hierarchie" : '')
405
		. $boucle->in
406
		. $boucle->hash
407
		. calculer_dec('$table', "'" . $boucle->id_table . "'")
408
		. calculer_dec('$id', "'" . $boucle->id_boucle . "'")
409
		# En absence de champ c'est un decompte : 
410
		. calculer_dec('$from', calculer_from($boucle))
411
		. calculer_dec('$type', calculer_from_type($boucle))
412
		. calculer_dec('$groupby', 'array(' . (($g = join("\",\n\t\t\"", $boucle->group)) ? '"' . $g . '"' : '') . ")")
413
		. calculer_dec('$select', 'array("' . join("\",\n\t\t\"", $boucle->select) . "\")")
414
		. calculer_dec('$orderby', 'array(' . calculer_order($boucle) . ")")
415
		. calculer_dec('$where', calculer_dump_array($boucle->where))
416
		. calculer_dec('$join', calculer_dump_join($boucle->join))
417
		. calculer_dec('$limit', (strpos($boucle->limit, 'intval')===false ?
418
			"'" . $boucle->limit . "'" :
419
			$boucle->limit))
420
		. calculer_dec('$having', calculer_dump_array($boucle->having));
421
}
422
423
function memoriser_contexte_compil($p){
424
	return join(',', array(
425
		_q($p->descr['sourcefile']),
426
		_q($p->descr['nom']),
427
		@_q($p->id_boucle),
428
		intval($p->ligne),
429
		'$GLOBALS[\'spip_lang\']'));
430
}
431
432
function reconstruire_contexte_compil($context_compil){
433
	if (!is_array($context_compil)) return $context_compil;
434
	include_spip('public/interfaces');
435
	$p = new Contexte;
436
	$p->descr = array('sourcefile' => $context_compil[0],
437
		'nom' => $context_compil[1]);
438
	$p->id_boucle = $context_compil[2];
439
	$p->ligne = $context_compil[3];
440
	$p->lang = $context_compil[4];
441
	return $p;
442
}
443
444
// http://doc.spip.org/@calculer_dec
445
function calculer_dec($nom, $val){
446
	$static = "static ";
447
	if (
448
		strpos($val, '$')!==false
449
		OR strpos($val, 'sql_')!==false
450
		OR (
451
			$test = str_replace(array("array(", '\"', "\'"), array("", "", ""), $val) // supprimer les array( et les echappements de guillemets
452
			AND strpos($test, "(")!==FALSE // si pas de parenthese ouvrante, pas de fonction, on peut sortir
453
			AND $test = preg_replace(",'[^']*',UimsS", "", $test) // supprimer les chaines qui peuvent contenir des fonctions SQL qui ne genent pas
454
			AND preg_match(",\w+\s*\(,UimsS", $test, $regs) // tester la presence de fonctions restantes
455
		)
456
	){
457
		$static = "";
458
	}
459
	return "\n\t" . $static . $nom . ' = ' . $val . ';';
460
}
461
462
// http://doc.spip.org/@calculer_dump_array
463
function calculer_dump_array($a){
464
	if (!is_array($a)) return $a;
465
	$res = "";
466
	if ($a AND $a[0]=="'?'")
467
		return ("(" . calculer_dump_array($a[1]) .
468
			" ? " . calculer_dump_array($a[2]) .
469
			" : " . calculer_dump_array($a[3]) .
470
			")");
471
	else {
472
		foreach ($a as $v) $res .= ", " . calculer_dump_array($v);
473
		return "\n\t\t\tarray(" . substr($res, 2) . ')';
474
	}
475
}
476
477
// http://doc.spip.org/@calculer_dump_join
478
function calculer_dump_join($a){
479
	$res = "";
480
	foreach ($a as $k => $v)
481
		$res .= ", '$k' => array(" . implode(',', $v) . ")";
482
	return 'array(' . substr($res, 2) . ')';
483
}
484
485
// http://doc.spip.org/@calculer_from
486
function calculer_from(&$boucle){
487
	$res = "";
488
	foreach ($boucle->from as $k => $v) $res .= ",'$k' => '$v'";
489
	return 'array(' . substr($res, 1) . ')';
490
}
491
492
// http://doc.spip.org/@calculer_from_type
493
function calculer_from_type(&$boucle){
494
	$res = "";
495
	foreach ($boucle->from_type as $k => $v) $res .= ",'$k' => '$v'";
496
	return 'array(' . substr($res, 1) . ')';
497
}
498
499
// http://doc.spip.org/@calculer_order
500
function calculer_order(&$boucle){
501
	if (!$order = $boucle->order
502
		AND !$order = $boucle->default_order
503
	)
504
		$order = array();
505
506
	/*if (isset($boucle->modificateur['collate'])){
507
		$col = "." . $boucle->modificateur['collate'];
508
		foreach($order as $k=>$o)
509
			if (strpos($order[$k],'COLLATE')===false)
510
				$order[$k].= $col;
511
	}*/
512
	return join(', ', $order);
513
}
514
515
// Production du code PHP a partir de la sequence livree par le phraseur
516
// $boucles est passe par reference pour affectation par index_pile.
517
// Retourne une expression PHP,
518
// (qui sera argument d'un Return ou la partie droite d'une affectation).
519
520
// http://doc.spip.org/@calculer_liste
521
function calculer_liste($tableau, $descr, &$boucles, $id_boucle = ''){
522
	if (!$tableau) return "''";
523
	if (!isset($descr['niv'])) $descr['niv'] = 0;
524
	$codes = compile_cas($tableau, $descr, $boucles, $id_boucle);
525
	if ($codes===false) return false;
526
	$n = count($codes);
527
	if (!$n) return "''";
528
	$tab = str_repeat("\t", $descr['niv']);
529
	if (_request('var_mode_affiche')!='validation'){
530
		if ($n==1)
531
			return $codes[0];
532
		else {
533
			$res = '';
534
			foreach ($codes as $code){
535
				if (!preg_match("/^'[^']*'$/", $code)
536
					OR substr($res, -1, 1)!=="'"
537
				)
538
					$res .= " .\n$tab$code";
539
				else {
540
					$res = substr($res, 0, -1) . substr($code, 1);
541
				}
542
			}
543
			return '(' . substr($res, 2+$descr['niv']) . ')';
544
		}
545
	} else {
546
		$nom = $descr['nom'] . $id_boucle . ($descr['niv'] ? $descr['niv'] : '');
547
		return "join('', array_map('array_shift', \$GLOBALS['debug_objets']['sequence']['$nom'] = array(" . join(" ,\n$tab", $codes) . ")))";
548
	}
549
}
550
551
define('_REGEXP_COND_VIDE_NONVIDE', "/^[(](.*)[?]\s*''\s*:\s*('[^']+')\s*[)]$/");
552
define('_REGEXP_COND_NONVIDE_VIDE', "/^[(](.*)[?]\s*('[^']+')\s*:\s*''\s*[)]$/");
553
define('_REGEXP_CONCAT_NON_VIDE', "/^(.*)[.]\s*'[^']+'\s*$/");
554
555
// http://doc.spip.org/@compile_cas
556
function compile_cas($tableau, $descr, &$boucles, $id_boucle){
557
558
	$codes = array();
559
	// cas de la boucle recursive
560
	if (is_array($id_boucle))
561
		$id_boucle = $id_boucle[0];
562
	$type = !$id_boucle ? '' : $boucles[$id_boucle]->type_requete;
563
	$tab = str_repeat("\t", ++$descr['niv']);
564
	$mode = _request('var_mode_affiche');
565
	$err_e_c = '';
566
	// chaque commentaire introduit dans le code doit commencer
567
	// par un caractere distinguant le cas, pour exploitation par debug.
568
	foreach ($tableau as $p){
569
570
		switch ($p->type) {
571
			// texte seul
572
			case 'texte':
573
				$code = "'" . str_replace(array("\\", "'"), array("\\\\", "\\'"), $p->texte) . "'";
574
575
				$commentaire = strlen($p->texte) . " signes";
576
				$avant = '';
577
				$apres = '';
578
				$altern = "''";
579
				break;
580
581
			case 'polyglotte':
582
				$code = "";
583
				foreach ($p->traductions as $k => $v){
584
					$code .= ",'" .
585
						str_replace(array("\\", "'"), array("\\\\", "\\'"), $k) .
586
						"' => '" .
587
						str_replace(array("\\", "'"), array("\\\\", "\\'"), $v) .
588
						"'";
589
				}
590
				$code = "choisir_traduction(array(" .
591
					substr($code, 1) .
592
					"))";
593
				$commentaire = '&';
594
				$avant = '';
595
				$apres = '';
596
				$altern = "''";
597
				break;
598
599
			// inclure
600
			case 'include':
601
				$p->descr = $descr;
602
				$code = calculer_inclure($p, $boucles, $id_boucle);
603
				if ($code===false){
604
					$err_e_c = true;
605
					$code = "''";
606
				} else {
607
					$commentaire = '<INCLURE ' . addslashes(str_replace("\n", ' ', $code)) . '>';
608
					$avant = '';
609
					$apres = '';
610
					$altern = "''";
611
				}
612
				break;
613
614
			// boucle
615
			case 'boucle':
616
				$nom = $p->id_boucle;
617
				$newdescr = $descr;
618
				$newdescr['id_mere'] = $nom;
619
				$newdescr['niv']++;
620
				$avant = calculer_liste($p->avant,
621
					$newdescr, $boucles, $id_boucle);
622
				$apres = calculer_liste($p->apres,
623
					$newdescr, $boucles, $id_boucle);
624
				$newdescr['niv']--;
625
				$altern = calculer_liste($p->altern,
626
					$newdescr, $boucles, $id_boucle);
627
				if (($avant===false) OR ($apres===false) OR ($altern===false)){
628
					$err_e_c = true;
629
					$code = "''";
630
				} else {
631
					$code = 'BOUCLE' .
632
						str_replace("-", "_", $nom) . $descr['nom'] .
633
						'($Cache, $Pile, $doublons, $Numrows, $SP)';
634
					$commentaire = "?$nom";
635
					if (!$boucles[$nom]->milieu
636
						AND $boucles[$nom]->type_requete<>'boucle'
637
					){
638
						if ($altern!="''") $code .= "\n. $altern";
639
						if ($avant<>"''" OR $apres<>"''")
640
							spip_log("boucle $nom toujours vide, code superflu dans $id");
0 ignored issues
show
Bug introduced by
The variable $id does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
641
						$avant = $apres = $altern = "''";
642
					} else if ($altern!="''") $altern = "($altern)";
643
				}
644
				break;
645
646
			case 'idiome':
647
				$l = array();
648
				$code = '';
649
				foreach ($p->arg as $k => $v){
650
					$_v = calculer_liste($v, $descr, $boucles, $id_boucle);
651
					if ($k) 
652
					  $l[] = _q($k) . ' => ' . $_v;
653
					else $code = $_v;
654
				}
655
				/// Si le module n'est pas fourni,
656
				/// l'expliciter sauf si calcule
657
				if ($p->module) {
658
					$m = $p->module .':'.$p->nom_champ;
659
				} elseif ($p->nom_champ) {
660
					$m = MODULES_IDIOMES .':'.$p->nom_champ;
661
				} else  $m = '';
662
663
				$code = (!$code ? "'$m'" :
664
						($m ? "'$m' . $code" : 
665
							("(strpos(\$x=$code, ':') ? \$x : ('" . MODULES_IDIOMES . ":' . \$x))")))
666
				. (!$l ? '' : (", array(" . implode(",\n", $l) . ")"));
667
				$code = "_T($code)";
668
				if ($p->param){
669
					$p->id_boucle = $id_boucle;
670
					$p->boucles = &$boucles;
671
					$code = compose_filtres($p, $code);
672
				}
673
				$commentaire = ":";
674
				$avant = '';
675
				$apres = '';
676
				$altern = "''";
677
				break;
678
679
			case 'champ':
680
681
				// cette structure pourrait etre completee des le phrase' (a faire)
682
				$p->id_boucle = $id_boucle;
683
				$p->boucles = &$boucles;
684
				$p->descr = $descr;
685
				#$p->interdire_scripts = true;
686
				$p->type_requete = $type;
687
688
				$code = calculer_champ($p);
689
				$commentaire = '#' . $p->nom_champ . $p->etoile;
690
				$avant = calculer_liste($p->avant,
691
					$descr, $boucles, $id_boucle);
692
				$apres = calculer_liste($p->apres,
693
					$descr, $boucles, $id_boucle);
694
				$altern = "''";
695
				// Si la valeur est destinee a une comparaison a ''
696
				// forcer la conversion en une chaine par strval
697
				// si ca peut etre autre chose qu'une chaine
698
				if (($avant!="''" OR $apres!="''")
699
					AND $code[0]!="'"
700
#			AND (strpos($code,'interdire_scripts') !== 0)
701
						AND !preg_match(_REGEXP_COND_VIDE_NONVIDE, $code)
702
							AND !preg_match(_REGEXP_COND_NONVIDE_VIDE, $code)
703
								AND !preg_match(_REGEXP_CONCAT_NON_VIDE, $code)
704
				)
705
					$code = "strval($code)";
706
				break;
707
708
			default:
709
				// Erreur de construction de l'arbre de syntaxe abstraite
710
				$code = "''";
711
				$p->descr = $descr;
712
				$err_e_c = array('zbug_erreur_compilation');
713
				erreur_squelette($err_e_c, $p);
714
		} // switch
715
716
		if ($code!="''"){
717
			$code = compile_retour($code, $avant, $apres, $altern, $tab, $descr['niv']);
0 ignored issues
show
Bug introduced by
The variable $avant does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $apres does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $altern does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
718
			$codes[] = (($mode=='validation') ?
719
				"array($code, '$commentaire', " . $p->ligne . ")"
0 ignored issues
show
Bug introduced by
The variable $commentaire does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
720
				: (($mode=='code') ?
721
					"\n// $commentaire\n$code" :
722
					$code));
723
		}
724
	} // foreach
725
726
	return $err_e_c ? false : $codes;
727
}
728
729
// production d'une expression conditionnelle ((v=EXP) ? (p . v .s) : a)
730
// mais si EXP est de la forme (t ? 'C' : '') on produit (t ? (p . C . s) : a)
731
// de meme si EXP est de la forme (t ? '' : 'C')
732
733
// http://doc.spip.org/@compile_retour
734
function compile_retour($code, $avant, $apres, $altern, $tab, $n){
735
	if ($avant=="''") $avant = '';
736
	if ($apres=="''") $apres = '';
737
	if (!$avant AND !$apres AND ($altern==="''")) return $code;
738
739
	if (preg_match(_REGEXP_CONCAT_NON_VIDE, $code)){
740
		$t = $code;
741
		$cond = '';
742
	} elseif (preg_match(_REGEXP_COND_VIDE_NONVIDE, $code, $r)) {
743
		$t = $r[2];
744
		$cond = '!' . $r[1];
745
	} else if (preg_match(_REGEXP_COND_NONVIDE_VIDE, $code, $r)){
746
		$t = $r[2];
747
		$cond = $r[1];
748
	} else {
749
		$t = '$t' . $n;
750
		$cond = "($t = $code)!==''";
751
	}
752
753
	$res = (!$avant ? "" : "$avant . ") .
754
		$t .
755
		(!$apres ? "" : " . $apres");
756
757
	if ($res!==$t) $res = "($res)";
758
	return !$cond ? $res : "($cond ?\n\t$tab$res :\n\t$tab$altern)";
759
}
760
761
762
function compile_inclure_doublons($lexemes){
763
	foreach ($lexemes as $v)
764
		if ($v->type==='include' AND $v->param)
765
			foreach ($v->param as $r)
766
				if (trim($r[0])==='doublons')
767
					return true;
768
	return false;
769
}
770
771
// Prend en argument le texte d'un squelette, le nom de son fichier d'origine,
772
// sa grammaire et un nom. Retourne False en cas d'erreur,
773
// sinon retourne un tableau de fonctions PHP compilees a evaluer,
774
// notamment une fonction portant ce nom et calculant une page.
775
// Pour appeler la fonction produite, lui fournir 2 tableaux de 1 e'le'ment:
776
// - 1er: element 'cache' => nom (du fichier ou` mettre la page)
777
// - 2e: element 0 contenant un environnement ('id_article => $id_article, etc)
778
// Elle retournera alors un tableau de 5 e'le'ments:
779
// - 'texte' => page HTML, application du squelette a` l'environnement;
780
// - 'squelette' => le nom du squelette
781
// - 'process_ins' => 'html' ou 'php' selon la pre'sence de PHP dynamique
782
// - 'invalideurs' =>  de'pendances de cette page, pour invalider son cache.
783
// - 'entetes' => tableau des entetes http
784
// En cas d'erreur, elle retournera un tableau des 2 premiers elements seulement
785
786
// http://doc.spip.org/@public_compiler_dist
787
function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect = ''){
788
	// Pre-traitement : reperer le charset du squelette, et le convertir
789
	// Bonus : supprime le BOM
790
	include_spip('inc/charsets');
791
	$squelette = transcoder_page($squelette);
792
793
	$descr = array('nom' => $nom,
794
		'gram' => $gram,
795
		'sourcefile' => $sourcefile,
796
		'squelette' => $squelette);
797
798
	// Phraser le squelette, selon sa grammaire
799
800
	$boucles = array();
801
	$f = charger_fonction('phraser_' . $gram, 'public');
802
803
	$squelette = $f($squelette, '', $boucles, $descr);
804
805
	return compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect);
806
}
807
808
// Point d'entree pour arbre de syntaxe abstraite fourni en premier argument
809
// Autres specifications comme ci-dessus
810
811
function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect = ''){
812
	global $tables_jointures;
813
	static $trouver_table;
814
	spip_timer('calcul_skel');
815
816
	if (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode']=='debug'){
817
		$GLOBALS['debug_objets']['squelette'][$nom] = $descr['squelette'];
818
		$GLOBALS['debug_objets']['sourcefile'][$nom] = $sourcefile;
819
820
		if (!isset($GLOBALS['debug_objets']['principal']))
821
			$GLOBALS['debug_objets']['principal'] = $nom;
822
	}
823
	foreach ($boucles as $id => $boucle){
824
		$GLOBALS['debug_objets']['boucle'][$nom . $id] = $boucle;
825
	}
826
	$descr['documents'] = compile_inclure_doublons($squelette);
827
828
	// Demander la description des tables une fois pour toutes
829
	// et reperer si les doublons sont demandes
830
	// pour un inclure ou une boucle document
831
	// c'est utile a la fonction champs_traitements
832
	if (!$trouver_table)
833
		$trouver_table = charger_fonction('trouver_table', 'base');
834
835
	foreach ($boucles as $id => $boucle){
836
		if (!($type = $boucle->type_requete)) continue;
837
		if (!$descr['documents'] AND (
838
			(($type=='documents') AND $boucle->doublons) OR
839
				compile_inclure_doublons($boucle->avant) OR
840
				compile_inclure_doublons($boucle->apres) OR
841
				compile_inclure_doublons($boucle->milieu) OR
842
				compile_inclure_doublons($boucle->altern))
843
		)
844
			$descr['documents'] = true;
845
		if ($type!='boucle'){
846
			if (!$boucles[$id]->sql_serveur AND $connect)
847
				$boucles[$id]->sql_serveur = $connect;
848
			$show = $trouver_table($type, $boucles[$id]->sql_serveur);
849
			// si la table n'existe pas avec le connecteur par defaut, 
850
			// c'est peut etre une table qui necessite son connecteur dedie fourni
851
			// permet une ecriture allegee (GEO) -> (geo:GEO)
852
			if (!$show AND $show = $trouver_table($type, strtolower($type)))
853
				$boucles[$id]->sql_serveur = strtolower($type);
854
			if ($show){
855
				$boucles[$id]->show = $show;
856
				// recopie les infos les plus importantes
857
				$boucles[$id]->primary = $show['key']["PRIMARY KEY"];
858
				$boucles[$id]->id_table = $x = $show['id_table'];
859
				$boucles[$id]->from[$x] = $nom_table = $show['table'];
860
861
				$boucles[$id]->descr = &$descr;
862
				if ((!$boucles[$id]->jointures)
863
					AND (isset($tables_jointures[$nom_table]))
864
						AND is_array($x = $tables_jointures[$nom_table])
865
				)
866
					$boucles[$id]->jointures = $x;
867
				if ($boucles[$id]->jointures_explicites){
868
					$jointures = preg_split("/\s+/", $boucles[$id]->jointures_explicites);
869
					while ($j = array_pop($jointures))
870
						array_unshift($boucles[$id]->jointures, $j);
871
				}
872
			} else {
873
				// Pas une erreur si la table est optionnelle
874
				if ($boucles[$id]->table_optionnelle)
875
					$boucles[$id]->type_requete = '';
876
				else {
877
					$boucles[$id]->type_requete = false;
878
					$boucle = $boucles[$id];
879
					$x = (!$boucle->sql_serveur ? '' :
880
						($boucle->sql_serveur . ":")) .
881
						$type;
882
					$msg = array('zbug_table_inconnue',
883
						array('table' => $x));
884
					erreur_squelette($msg, $boucle);
885
				}
886
			}
887
		}
888
	}
889
890
	// Commencer par reperer les boucles appelees explicitement 
891
	// car elles indexent les arguments de maniere derogatoire
892
	foreach ($boucles as $id => $boucle){
893
		if ($boucle->type_requete=='boucle' AND $boucle->param){
894
			$boucles[$id]->descr = &$descr;
895
			$rec = &$boucles[$boucle->param[0]];
896
			if (!$rec){
897
				$msg = array('zbug_boucle_recursive_undef',
898
					array('nom' => $boucle->param[0]));
899
				erreur_squelette($msg, $boucle);
900
				$boucles[$id]->type_requete = false;
901
			} else {
902
				$rec->externe = $id;
903
				$descr['id_mere'] = $id;
904
				$boucles[$id]->return =
905
					calculer_liste(array($rec),
906
						$descr,
907
						$boucles,
908
						$boucle->param);
909
			}
910
		}
911
	}
912
	foreach ($boucles as $id => $boucle){
913
		$id = strval($id); // attention au type dans index_pile
914
		$type = $boucle->type_requete;
915
		if ($type AND $type!='boucle'){
916
			$crit = !$boucle->param ? true : calculer_criteres($id, $boucles);
917
			$descr['id_mere'] = $id;
918
			$boucles[$id]->return =
919
				calculer_liste($boucle->milieu,
920
					$descr,
921
					$boucles,
922
					$id);
923
			// Si les criteres se sont mal compiles
924
			// ne pas tenter d'assembler le code final
925
			// (mais compiler le corps pour detection d'erreurs)
926
			if (is_array($crit))
927
				$boucles[$id]->type_requete = false;
928
		}
929
	}
930
931
	// idem pour la racine
932
	$descr['id_mere'] = '';
933
	$corps = calculer_liste($squelette, $descr, $boucles);
934
	$debug = (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode']=='debug');
935
936
	if ($debug){
937
		include_spip('public/decompiler');
938
		include_spip('public/format_' . _EXTENSION_SQUELETTES);
939
	}
940
	// Calcul du corps de toutes les fonctions PHP,
941
	// en particulier les requetes SQL et TOTAL_BOUCLE
942
	// de'terminables seulement maintenant
943
944
	foreach ($boucles as $id => $boucle){
945
		$boucle = $boucles[$id] = pipeline('pre_boucle', $boucle);
946
		if ($boucle->return===false) continue;
947
		// appeler la fonction de definition de la boucle
948
949
		if ($req = $boucle->type_requete){
950
			$f = 'boucle_' . strtoupper($req);
951
			// si pas de definition perso, definition spip
952
			if (!function_exists($f)) $f = $f . '_dist';
953
			// laquelle a une definition par defaut
954
			if (!function_exists($f)) $f = 'boucle_DEFAUT';
955
			if (!function_exists($f)) $f = 'boucle_DEFAUT_dist';
956
			$req = "\n\n\tstatic \$connect = " .
957
				_q($boucle->sql_serveur) .
958
				";" .
959
				$f($id, $boucles);
960
		} else $req = ("\n\treturn '';");
961
962
		$boucles[$id]->return =
963
			"function BOUCLE" . strtr($id, "-", "_") . $nom .
964
				'(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' .
965
				$req .
966
				"\n}\n\n";
967
968
		if ($debug)
969
			$GLOBALS['debug_objets']['code'][$nom . $id] = $boucles[$id]->return;
970
	}
971
972
	// Au final, si le corps ou un critere au moins s'est mal compile
973
	// retourner False, sinon inserer leur decompilation
974
	if (is_bool($corps)) return false;
975
	foreach ($boucles as $id => $boucle){
976
		if ($boucle->return===false) return false;
977
		$boucle->return = "\n\n/* BOUCLE " .
978
			$boucle->type_requete .
979
			" " .
980
			(!$debug ? '' :
981
				str_replace('*/', '* /',
982
					decompiler_criteres($boucle->param,
983
						$boucle->criteres))) .
984
			" */\n\n " .
985
			$boucle->return;
986
	}
987
988
	$secondes = spip_timer('calcul_skel');
989
	spip_log("COMPIL ($secondes) [$sourcefile] $nom.php");
990
	// $connect n'est pas sûr : on nettoie
991
	$connect = preg_replace(',[^\w],', '', $connect);
992
993
	// Assimiler la fct principale a une boucle anonyme, c'est plus simple
994
	$code = new Boucle;
995
	$code->descr = $descr;
996
	$code->return = '
997
//
998
// Fonction principale du squelette ' .
999
		$sourcefile .
1000
		($connect ? " pour $connect" : '') .
1001
		(!CODE_COMMENTE ? '' : "\n// Temps de compilation total: $secondes") .
1002
		"\n//" .
1003
		(!$debug ? '' : ("\n/*\n" .
1004
			str_replace('*/', '* /', public_decompiler($squelette))
1005
			. "\n*/")) . "
1006
1007
function " . $nom . '($Cache, $Pile, $doublons=array(), $Numrows=array(), $SP=0) {
1008
1009
'
1010
		// reporter de maniere securisee les doublons inclus
1011
		. '
1012
	if (isset($Pile[0]["doublons"]) AND is_array($Pile[0]["doublons"]))
1013
		$doublons = nettoyer_env_doublons($Pile[0]["doublons"]);
1014
1015
	$connect = ' .
1016
		_q($connect) . ';
1017
	$page = ' .
1018
		// ATTENTION, le calcul de l'expression $corps affectera $Cache
1019
		// c'est pourquoi on l'affecte a la variable auxiliaire $page.
1020
		// avant de referencer $Cache
1021
		$corps . ";
1022
1023
	return analyse_resultat_skel(" . var_export($nom, true)
1024
		. ", \$Cache, \$page, " . var_export($sourcefile, true) . ");
1025
}";
1026
1027
	$boucles[''] = $code;
1028
	return $boucles;
1029
}
1030
1031
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
1032