Completed
Push — master ( d694f8...872600 )
by cam
04:32 queued 10s
created

compiler.php ➔ compile_concatene_parties_codes()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2019                                                *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
14
/**
15
 * Fichier principal du compilateur de squelettes
16
 *
17
 * @package SPIP\Core\Compilateur\Compilation
18
 **/
19
20
if (!defined('_ECRIRE_INC_VERSION')) {
21
	return;
22
}
23
24
/** Repérer un code ne calculant rien, meme avec commentaire */
25
define('CODE_MONOTONE', ",^(\n//[^\n]*\n)?\(?'([^'])*'\)?$,");
26
/** Indique s'il faut commenter le code produit */
27
define('CODE_COMMENTE', true);
28
29
// definition des structures de donnees
30
include_spip('public/interfaces');
31
32
// Definition de la structure $p, et fonctions de recherche et de reservation
33
// dans l'arborescence des boucles
34
include_spip('public/references');
35
36
// production du code qui peut etre securisee
37
include_spip('public/sandbox');
38
39
// definition des boucles
40
include_spip('public/boucles');
41
42
// definition des criteres
43
include_spip('public/criteres');
44
45
// definition des balises
46
include_spip('public/balises');
47
48
// Gestion des jointures
49
include_spip('public/jointures');
50
51
// Les 2 ecritures INCLURE{A1,A2,A3...} et INCLURE(A1){A2}{A3}... sont admises
52
// Preferer la premiere.
53
// Les Ai sont de la forme Vi=Ei ou bien Vi qui veut alors dire Vi=Vi
54
// Le resultat est un tableau indexe par les Vi
55
// Toutefois, si le premier argument n'est pas de la forme Vi=Ei
56
// il est conventionnellement la valeur de l'index 1.
57
// pour la balise #INCLURE
58
// mais pas pour <INCLURE> dont le fond est defini explicitement.
59
60
61
// http://code.spip.net/@argumenter_inclure
62
function argumenter_inclure(
63
	$params,
64
	$rejet_filtres,
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...
65
	$p,
66
	&$boucles,
67
	$id_boucle,
68
	$echap = true,
69
	$lang = '',
70
	$fond1 = false
71
) {
72
	$l = array();
73
	$erreur_p_i_i = '';
74
	if (!is_array($params)) {
75
		return $l;
76
	}
77
	foreach ($params as $k => $couple) {
78
		// la liste d'arguments d'inclusion peut se terminer par un filtre
79
		$filtre = array_shift($couple);
80
		if ($filtre) {
81
			break;
82
		}
83
		foreach ($couple as $n => $val) {
84
			$var = $val[0];
85
			if ($var->type != 'texte') {
86
				if ($n or $k or $fond1) {
87
					$erreur_p_i_i = array(
88
						'zbug_parametres_inclus_incorrects',
89
						array('param' => $var->nom_champ)
90
					);
91
					erreur_squelette($erreur_p_i_i, $p);
92
					break;
93
				} else {
94
					$l[1] = calculer_liste($val, $p->descr, $boucles, $id_boucle);
95
				}
96
			} else {
97
				preg_match(",^([^=]*)(=?)(.*)$,m", $var->texte, $m);
98
				$m = array_pad($m, 3, null);
99
				$var = $m[1];
100
				$auto = false;;
101
				if ($m[2]) {
102
					$v = $m[3];
103
					if (preg_match(',^[\'"](.*)[\'"]$,', $v, $m)) {
104
						$v = $m[1];
105
					}
106
					$val[0] = new Texte;
107
					$val[0]->texte = $v;
108
				} elseif ($k or $n or $fond1) {
109
					$auto = true;
110
				} else {
111
					$var = 1;
112
				}
113
114
				if ($var == 'lang') {
115
					$lang = !$auto
116
						? calculer_liste($val, $p->descr, $boucles, $id_boucle)
117
						: '$GLOBALS["spip_lang"]';
118
				} else {
119
					$val = $auto
120
						? index_pile($id_boucle, $var, $boucles)
121
						: calculer_liste($val, $p->descr, $boucles, $id_boucle);
122
					if ($var !== 1) {
123
						$val = ($echap ? "\'$var\' => ' . argumenter_squelette(" : "'$var' => ")
124
							. $val . ($echap ? ") . '" : " ");
125
					} else {
126
						$val = $echap ? "'.$val.'" : $val;
127
					}
128
					$l[$var] = $val;
129
				}
130
			}
131
		}
132
	}
133
	if ($erreur_p_i_i) {
134
		return false;
135
	}
136
	// Cas particulier de la langue : si {lang=xx} est definie, on
137
	// la passe, sinon on passe la langue courante au moment du calcul
138
	// sauf si on n'en veut pas 
139
	if ($lang === false) {
140
		return $l;
141
	}
142
	if (!$lang) {
143
		$lang = '$GLOBALS["spip_lang"]';
144
	}
145
	$l['lang'] = ($echap ? "\'lang\' => ' . argumenter_squelette(" : "'lang' => ") . $lang . ($echap ? ") . '" : " ");
146
147
	return $l;
148
}
149
150
/**
151
 * Code d'appel à un <INCLURE()>
152
 *
153
 * Code PHP pour un squelette (aussi pour #INCLURE, #MODELE #LES_AUTEURS)
154
 */
155
define('CODE_RECUPERER_FOND', 'recuperer_fond(%s, %s, array(%s), %s)');
156
157
/**
158
 * Compile une inclusion <INCLURE> ou #INCLURE
159
 *
160
 * @param Inclure $p
161
 *     Description de l'inclusion (AST au niveau de l'inclure)
162
 * @param array $boucles
163
 *     AST du squelette
164
 * @param string $id_boucle
165
 *     Identifiant de la boucle contenant l'inclure
166
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be false|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
167
 *     Code PHP appelant l'inclusion
168
 **/
169
function calculer_inclure($p, &$boucles, $id_boucle) {
170
171
	$_contexte = argumenter_inclure($p->param, false, $p, $boucles, $id_boucle, true, '', true);
172
	if (is_string($p->texte)) {
173
		$fichier = $p->texte;
174
		$code = "\"".str_replace('"','\"',$fichier)."\"";
175
176
	} else {
177
		$code = calculer_liste($p->texte, $p->descr, $boucles, $id_boucle);
0 ignored issues
show
Bug introduced by
The property descr does not seem to exist in Inclure.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
178
		if ($code and preg_match("/^'([^']*)'/s", $code, $r)) {
179
			$fichier = $r[1];
180
		} else {
181
			$fichier = '';
182
		}
183
	}
184
	if (!$code or $code === '""') {
185
		$erreur_p_i_i = array(
186
			'zbug_parametres_inclus_incorrects',
187
			array('param' => $code)
188
		);
189
		erreur_squelette($erreur_p_i_i, $p);
190
191
		return false;
192
	}
193
	$compil = texte_script(memoriser_contexte_compil($p));
194
195
	if (is_array($_contexte)) {
196
		// Critere d'inclusion {env} (et {self} pour compatibilite ascendante)
197 View Code Duplication
		if ($env = (isset($_contexte['env']) || isset($_contexte['self']))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
198
			unset($_contexte['env']);
199
		}
200
201
		// noter les doublons dans l'appel a public.php
202
		if (isset($_contexte['doublons'])) {
203
			$_contexte['doublons'] = "\\'doublons\\' => '.var_export(\$doublons,true).'";
204
		}
205
206 View Code Duplication
		if ($ajax = isset($_contexte['ajax'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
207
			$ajax = preg_replace(",=>(.*)$,ims", '=> ($v=(\\1))?$v:true', $_contexte['ajax']);
208
			unset($_contexte['ajax']);
209
		}
210
211
		$_contexte = join(",\n\t", $_contexte);
212
	} else {
213
		return false;
214
	} // j'aurais voulu toucher le fond ...
215
216
	$contexte = 'array(' . $_contexte . ')';
217
218
	if ($env) {
219
		$contexte = "array_merge('.var_export(\$Pile[0],1).',$contexte)";
220
	}
221
222
	// s'il y a une extension .php, ce n'est pas un squelette
223
	if ($fichier and preg_match('/^.+[.]php$/s', $fichier)) {
224
		$code = sandbox_composer_inclure_php($fichier, $p, $contexte);
225
	} else {
226
		$_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...
227
		if ($ajax) {
228
			$_options[] = $ajax;
229
		}
230
		$code = " ' . argumenter_squelette($code) . '";
231
		$code = "echo " . sprintf(CODE_RECUPERER_FOND, $code, $contexte, implode(',', $_options),
232
				"_request(\"connect\")") . ';';
233
	}
234
235
	return "\n'<'.'" . "?php " . $code . "\n?'." . "'>'";
236
}
237
238
239
/**
240
 * Gérer les statuts declarés pour cette table
241
 *
242
 * S'il existe des statuts sur cette table, déclarés dans la description
243
 * d'un objet éditorial, applique leurs contraintes
244
 *
245
 * @param Boucle $boucle
246
 *     Descrition de la boucle
247
 * @param bool $echapper
248
 *     true pour échapper le code créé
249
 * @param bool $ignore_previsu
250
 *     true pour ne tester que le cas publie et ignorer l'eventuel var_mode=preview de la page
251
 */
252
function instituer_boucle(&$boucle, $echapper = true, $ignore_previsu = false) {
253
	/*
254
	$show['statut'][] = array(
255
		'champ'=>'statut',  // champ de la table sur lequel porte le filtrage par le statut
256
		'publie'=>'publie', // valeur ou liste de valeurs, qui definissent l'objet comme publie.
257
		'previsu'=>'publie,prop', // valeur ou liste de valeurs qui sont visibles en previsu
258
		'post_date'=>'date', // un champ de date pour la prise en compte des post_dates, ou rien sinon
259
	  'exception'=>'statut', // liste des modificateurs qui annulent le filtrage par statut
260
	                         // si plusieurs valeurs : array('statut','tout','lien')
261
	);
262
263
	Pour 'publier' ou 'previsu', si la chaine commence par un "!" on exclu au lieu de filtrer sur les valeurs donnees
264
	si la chaine est vide, on ne garde rien si elle est seulement "!" on n'exclu rien
265
266
	Si le statut repose sur une jointure, 'champ' est alors un tableau du format suivant :
267
	'champ'=>array(
268
	    array(table1, cle1),
269
	    ...
270
	    array(tablen, clen),
271
	    champstatut
272
	 )
273
274
	champstatut est alors le champ statut sur la tablen
275
	dans les jointures, clen peut etre un tableau pour une jointure complexe : array('id_objet','id_article','objet','article')
276
*/
277
	$id_table = $boucle->id_table;
278
	$show = $boucle->show;
279
	if (isset($show['statut']) and $show['statut']) {
280
		foreach ($show['statut'] as $k => $s) {
281
			// Restreindre aux elements publies si pas de {statut} ou autre dans les criteres
282
			$filtrer = true;
283
			if (isset($s['exception'])) {
284
				foreach (is_array($s['exception']) ? $s['exception'] : array($s['exception']) as $m) {
285
					if (isset($boucle->modificateur[$m]) or isset($boucle->modificateur['criteres'][$m])) {
286
						$filtrer = false;
287
						break;
288
					}
289
				}
290
			}
291
292
			if ($filtrer) {
293
				if (is_array($s['champ'])) {
294
					$statut = preg_replace(',\W,', '', array_pop($s['champ'])); // securite
295
					$jointures = array();
296
					// indiquer la description de chaque table dans le tableau de jointures,
297
					// ce qui permet d'eviter certains GROUP BY inutiles.
298
					$trouver_table = charger_fonction('trouver_table', 'base');
299
					foreach ($s['champ'] as $j) {
300
						$id = reset($j);
301
						$def = $trouver_table($id);
302
						$jointures[] = array('', array($id, $def), end($j));
303
					}
304
					$jointures[0][0] = $id_table;
305
					if (!array_search($id, $boucle->from)) {
0 ignored issues
show
Bug introduced by
The variable $id 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...
306
						include_spip('public/jointures');
307
						fabrique_jointures($boucle, $jointures, true, $boucle->show, $id_table, '', $echapper);
308
					}
309
					// trouver l'alias de la table d'arrivee qui porte le statut
310
					$id = array_search($id, $boucle->from);
311
				} else {
312
					$id = $id_table;
313
					$statut = preg_replace(',\W,', '', $s['champ']); // securite
314
				}
315
				$mstatut = $id . '.' . $statut;
316
317
				$arg_ignore_previsu = ($ignore_previsu ? ",true" : '');
318
				include_spip('public/quete');
319
				if (isset($s['post_date']) and $s['post_date']
320
					and $GLOBALS['meta']["post_dates"] == 'non'
321
				) {
322
					$date = $id . '.' . preg_replace(',\W,', '', $s['post_date']); // securite
323
					array_unshift($boucle->where,
324
						$echapper ?
325
							"\nquete_condition_postdates('$date'," . _q($boucle->sql_serveur) . "$arg_ignore_previsu)"
326
							:
327
							quete_condition_postdates($date, $boucle->sql_serveur, $ignore_previsu)
328
					);
329
				}
330
				array_unshift($boucle->where,
331
					$echapper ?
332
						"\nquete_condition_statut('$mstatut',"
333
						. _q($s['previsu']) . ","
334
						. _q($s['publie']) . ","
335
						. _q($boucle->sql_serveur) . "$arg_ignore_previsu)"
336
						:
337
						quete_condition_statut($mstatut, $s['previsu'], $s['publie'], $boucle->sql_serveur, $ignore_previsu)
338
				);
339
			}
340
		}
341
	}
342
}
343
344
/**
345
 * Produit le corps PHP d'une boucle Spip.
346
 *
347
 * Ce corps remplit une variable $t0 retournée en valeur.
348
 * Ici on distingue boucles recursives et boucle à requête SQL
349
 * et on insère le code d'envoi au debusqueur du resultat de la fonction.
350
 *
351
 * @param string $id_boucle
352
 *    Identifiant de la boucle
353
 * @param array $boucles
354
 *    AST du squelette
355
 * @return string
356
 *    Code PHP compilé de la boucle
357
 */
358
function calculer_boucle($id_boucle, &$boucles) {
359
360
	$boucle = &$boucles[$id_boucle];
361
	instituer_boucle($boucle);
362
	$boucles[$id_boucle] = pipeline('post_boucle', $boucles[$id_boucle]);
363
364
	// en mode debug memoriser les premiers passages dans la boucle,
365
	// mais pas tous, sinon ca pete.
366
	if (_request('var_mode_affiche') != 'resultat') {
367
		$trace = '';
368
	} else {
369
		$_trace = $boucles[$id_boucle]->descr['nom'] . $id_boucle;
370
		$_trace = "\$GLOBALS['debug_objets']['resultat']['$_trace']";
371
		$trace = "
372
		if (empty($_trace)) { 
373
			$_trace = []; 
374
		}
375
		if (count($_trace) < 3) { 
376
			$_trace" . "[] = \$t0; 
377
		}";
378
	}
379
380
	return ($boucles[$id_boucle]->type_requete == TYPE_RECURSIF)
381
		? calculer_boucle_rec($id_boucle, $boucles, $trace)
382
		: calculer_boucle_nonrec($id_boucle, $boucles, $trace);
383
}
384
385
386
/**
387
 * Compilation d'une boucle recursive.
388
 *
389
 * @internal
390
 *    Il suffit (ET IL FAUT) sauvegarder les valeurs des arguments passes par
391
 *    reference, car par definition un tel passage ne les sauvegarde pas
392
 *
393
 * @param string $id_boucle
394
 *    Identifiant de la boucle
395
 * @param array $boucles
396
 *    AST du squelette
397
 * @param string $trace
398
 *    Code PHP (en mode debug uniquement) servant à conserver une
399
 *    trace des premières valeurs de la boucle afin de pouvoir
400
 *    les afficher dans le débugueur ultérieurement
401
 * @return string
402
 *    Code PHP compilé de la boucle récursive
403
 **/
404
function calculer_boucle_rec($id_boucle, &$boucles, $trace) {
405
	$nom = $boucles[$id_boucle]->param[0];
406
407
	return
408
		// Numrows[$nom] peut ne pas être encore defini
409
		"\n\t\$save_numrows = (isset(\$Numrows['$nom']) ? \$Numrows['$nom'] : array());"
410
		. "\n\t\$t0 = " . $boucles[$id_boucle]->return . ";"
411
		. "\n\t\$Numrows['$nom'] = (\$save_numrows);"
412
		. $trace
413
		. "\n\treturn \$t0;";
414
}
415
416
/**
417
 * Compilation d'une boucle non recursive.
418
 *
419
 * La constante donne le cadre systématique du code:
420
 *
421
 * - %s1: initialisation des arguments de calculer_select
422
 * - %s2: appel de calculer_select en donnant un contexte pour les cas d'erreur
423
 * - %s3: initialisation du sous-tableau Numrows[id_boucle]
424
 * - %s4: sauvegarde de la langue et calcul des invariants de boucle sur elle
425
 * - %s5: boucle while sql_fetch ou str_repeat si corps monotone
426
 * - %s6: restauration de la langue
427
 * - %s7: liberation de la ressource, en tenant compte du serveur SQL
428
 * - %s8: code de trace eventuel avant le retour
429
 **/
430
define('CODE_CORPS_BOUCLE', '%s
431
	if (defined("_BOUCLE_PROFILER")) $timer = time()+(float)microtime();
432
	$t0 = "";
433
	// REQUETE
434
	$iter = IterFactory::create(
435
		"%s",
436
		%s,
437
		array(%s)
438
	);
439
	if (!$iter->err()) {
440
	%s%s$SP++;
441
	// RESULTATS
442
	%s
443
	%s$iter->free();
444
	}%s
445
	if (defined("_BOUCLE_PROFILER")
446
	AND 1000*($timer = (time()+(float)microtime())-$timer) > _BOUCLE_PROFILER)
447
		spip_log(intval(1000*$timer)."ms %s","profiler"._LOG_AVERTISSEMENT);
448
	return $t0;'
449
);
450
451
/**
452
 * Compilation d'une boucle (non recursive).
453
 *
454
 * @param string $id_boucle
455
 *    Identifiant de la boucle
456
 * @param array $boucles
457
 *    AST du squelette
458
 * @param string $trace
459
 *    Code PHP (en mode debug uniquement) servant à conserver une
460
 *    trace des premières valeurs de la boucle afin de pouvoir
461
 *    les afficher dans le débugueur ultérieurement
462
 * @return string
463
 *    Code PHP compilé de la boucle récursive
464
 **/
465
function calculer_boucle_nonrec($id_boucle, &$boucles, $trace) {
466
467
	$boucle = &$boucles[$id_boucle];
468
	$return = $boucle->return;
469
	$type_boucle = $boucle->type_requete;
470
	$primary = $boucle->primary;
471
	$constant = preg_match(CODE_MONOTONE, str_replace("\\'", '', $return));
472
	$flag_cpt = $boucle->mode_partie || $boucle->cptrows;
473
	$corps = '';
474
475
	// faudrait expanser le foreach a la compil, car y en a souvent qu'un 
476
	// et puis faire un [] plutot qu'un "','."
477
	if ($boucle->doublons) {
478
		$corps .= "\n\t\t\tforeach(" . $boucle->doublons . ' as $k) $doublons[$k] .= "," . ' .
479
			index_pile($id_boucle, $primary, $boucles)
480
			. "; // doublons\n";
481
	}
482
483
	// La boucle doit-elle selectionner la langue ?
484
	// - par defaut, les boucles suivantes le font
485
	//    (sauf si forcer_lang==true ou si le titre contient <multi>).
486
	// - a moins d'une demande explicite via {!lang_select}
487
	if (!$constant && $boucle->lang_select != 'non' &&
488
		(($boucle->lang_select == 'oui') ||
489
			in_array($type_boucle, array(
490
				'articles',
491
				'rubriques',
492
				'hierarchie',
493
				'breves'
494
			)))
495
	) {
496
		// Memoriser la langue avant la boucle et la restituer apres
497
		// afin que le corps de boucle affecte la globale directement
498
		$init_lang = "lang_select(\$GLOBALS['spip_lang']);\n\t";
499
		$fin_lang = "lang_select();\n\t";
500
		$fin_lang_select_public = "\n\t\tlang_select();";
501
502
		$corps .=
503
			"\n\t\tlang_select_public("
504
			. index_pile($id_boucle, 'lang', $boucles)
505
			. ", '" . $boucle->lang_select . "'"
506
			. (in_array($type_boucle, array(
507
				'articles',
508
				'rubriques',
509
				'hierarchie',
510
				'breves'
511
			)) ? ', ' . index_pile($id_boucle, 'titre', $boucles) : '')
512
			. ');';
513
	} else {
514
		$init_lang = '';
515
		$fin_lang = '';
516
		$fin_lang_select_public = '';
517
		// sortir les appels au traducteur (invariants de boucle)
518
		if (strpos($return, '?php') === false
519
			and preg_match_all("/\W(_T[(]'[^']*'[)])/", $return, $r)
520
		) {
521
			$i = 1;
522
			foreach ($r[1] as $t) {
523
				$init_lang .= "\n\t\$l$i = $t;";
524
				$return = str_replace($t, "\$l$i", $return);
525
				$i++;
526
			}
527
		}
528
	}
529
530
	// gestion optimale des separateurs et des boucles constantes
531
	if (count($boucle->separateur)) {
532
		$code_sep = ("'" . str_replace("'", "\'", join('', $boucle->separateur)) . "'");
533
	}
534
535
	$corps .=
536
		((!$boucle->separateur) ?
537
			(($constant && !$corps && !$flag_cpt) ? $return :
538
				(($return === "''") ? '' :
539
					("\n\t\t" . '$t0 .= ' . $return . ";"))) :
540
			("\n\t\t\$t1 " .
541
				((strpos($return, '$t1.') === 0) ?
542
					(".=" . substr($return, 4)) :
543
					('= ' . $return)) .
544
				";\n\t\t" .
545
				'$t0 .= ((strlen($t1) && strlen($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...
546
547
	// Calculer les invalideurs si c'est une boucle non constante et si on
548
	// souhaite invalider ces elements
549
	if (!$constant and $primary) {
550
		include_spip('inc/invalideur');
551
		$corps = calcul_invalideurs($corps, $primary,$boucles, $id_boucle);
552
	}
553
554
	// gerer le compteur de boucle 
555
	// avec ou sans son utilisation par les criteres {1/3} {1,4} {n-2,1}...
556
557
	if ($boucle->partie or $boucle->cptrows) {
558
		$corps = "\n\t\t\$Numrows['$id_boucle']['compteur_boucle']++;"
559
			. $boucle->partie
560
			. $corps;
561
	}
562
563
	// depiler la lang de la boucle si besoin
564
	$corps .= $fin_lang_select_public;
565
566
	// si le corps est une constante, ne pas appeler le serveur N fois!
567
568
	if (preg_match(CODE_MONOTONE, str_replace("\\'", '', $corps), $r)) {
569
		if (!isset($r[2]) or (!$r[2])) {
570
			if (!$boucle->numrows) {
571
				return "\n\t\$t0 = '';";
572
			} else {
573
				$corps = "";
574
			}
575
		} else {
576
			$boucle->numrows = true;
577
			$corps = "\n\t\$t0 = str_repeat($corps, \$Numrows['$id_boucle']['total']);";
578
		}
579
	} else {
580
		$corps = "while (\$Pile[\$SP]=\$iter->fetch()) {\n$corps\n	}";
581
	}
582
583
	$count = '';
0 ignored issues
show
Unused Code introduced by
$count is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
584
	if (!$boucle->select) {
585
		if (!$boucle->numrows or $boucle->limit or $boucle->mode_partie or $boucle->group) {
586
			$count = '1';
587
		} else {
588
			$count = 'count(*)';
589
		}
590
		$boucles[$id_boucle]->select[] = $count;
591
	}
592
593
	if ($flag_cpt) {
594
		$nums = "\n\t// COMPTEUR\n\t"
595
			. "\$Numrows['$id_boucle']['compteur_boucle'] = 0;\n\t";
596
	} else {
597
		$nums = '';
598
	}
599
600
	if ($boucle->numrows or $boucle->mode_partie) {
601
		$nums .= "\$Numrows['$id_boucle']['total'] = @intval(\$iter->count());"
602
			. $boucle->mode_partie
603
			. "\n\t";
604
	}
605
606
	// Ne calculer la requete que maintenant
607
	// car ce qui precede appelle index_pile qui influe dessus
608
609
	$init = (($init = $boucles[$id_boucle]->doublons)
610
			? ("\n\t$init = array();") : '')
611
		. calculer_requete_sql($boucles[$id_boucle]);
612
613
	$contexte = memoriser_contexte_compil($boucle);
614
615
	$a = sprintf(CODE_CORPS_BOUCLE,
616
		$init,
617
		$boucle->iterateur,
618
		"\$command",
619
		$contexte,
620
		$nums,
621
		$init_lang,
622
		$corps,
623
		$fin_lang,
624
		$trace,
625
		'BOUCLE' . $id_boucle . ' @ ' . ($boucle->descr['sourcefile'])
626
	);
627
628
#	var_dump($a);exit;
629
	return $a;
630
}
631
632
633
/**
634
 * Calcule le code PHP d'une boucle contenant les informations qui produiront une requête SQL
635
 *
636
 * Le code produit est un tableau associatif $command contenant les informations
637
 * pour que la boucle produise ensuite sa requête, tel que `$command['from'] = 'spip_articles';`
638
 *
639
 * @param Boucle $boucle
640
 *     AST de la boucle
641
 * @return string
642
 *     Code PHP compilé définissant les informations de requête
643
 **/
644
function calculer_requete_sql($boucle) {
645
	$init = array();
646
	$init[] = calculer_dec('table', "'" . $boucle->id_table . "'");
647
	$init[] = calculer_dec('id', "'" . $boucle->id_boucle . "'");
648
	# En absence de champ c'est un decompte :
649
	$init[] = calculer_dec('from', calculer_from($boucle));
650
	$init[] = calculer_dec('type', calculer_from_type($boucle));
651
	$init[] = calculer_dec('groupby',
652
		'array(' . (($g = join("\",\n\t\t\"", $boucle->group)) ? '"' . $g . '"' : '') . ")");
653
	$init[] = calculer_dec('select', 'array("' . join("\",\n\t\t\"", $boucle->select) . "\")");
654
	$init[] = calculer_dec('orderby', 'array(' . calculer_order($boucle) . ")");
655
	$init[] = calculer_dec('where', calculer_dump_array($boucle->where));
656
	$init[] = calculer_dec('join', calculer_dump_join($boucle->join));
657
	$init[] = calculer_dec('limit',
658
		(strpos($boucle->limit, 'intval') === false ?
659
			"'" . $boucle->limit . "'"
660
			:
661
			$boucle->limit));
662
	$init[] = calculer_dec('having', calculer_dump_array($boucle->having));
663
	$s = $d = "";
664
	// l'index 0 de $i indique si l'affectation est statique (contenu)
665
	// ou recalculée à chaque passage (vide)
666
	foreach ($init as $i) {
667
		if (reset($i)) {
668
			$s .= "\n\t\t" . end($i);
669
		} # statique
670
		else {
671
			$d .= "\n\t" . end($i);
672
		} # dynamique
673
	}
674
675
	return ($boucle->hierarchie ? "\n\t$boucle->hierarchie" : '')
676
	. $boucle->in
677
	. $boucle->hash
678
	. "\n\t" . 'if (!isset($command[\'table\'])) {'
679
	. $s
680
	. "\n\t}"
681
	. $d;
682
}
683
684
/**
685
 * Retourne une chaîne des informations du contexte de compilation
686
 *
687
 * Retourne la source, le nom, l'identifiant de boucle, la ligne, la langue
688
 * de l'élément dans une chaîne.
689
 *
690
 * @see reconstruire_contexte_compil()
691
 *
692
 * @param Object $p
693
 *     Objet de l'AST dont on mémorise le contexte
694
 * @return string
695
 *     Informations du contexte séparés par des virgules,
696
 *     qui peut être utilisé pour la production d'un tableau array()
697
 **/
698
function memoriser_contexte_compil($p) {
699
	return join(',', array(
700
		_q(isset($p->descr['sourcefile']) ? $p->descr['sourcefile'] : ''),
701
		_q(isset($p->descr['nom']) ? $p->descr['nom'] : ''),
702
		_q(isset($p->id_boucle) ? $p->id_boucle : null),
703
		intval($p->ligne),
704
		'$GLOBALS[\'spip_lang\']'
705
	));
706
}
707
708
/**
709
 * Reconstruit un contexte de compilation
710
 *
711
 * Pour un tableau d'information de contexte donné,
712
 * retourne un objet Contexte (objet générique de l'AST)
713
 * avec ces informations
714
 *
715
 * @see memoriser_contexte_compil()
716
 *
717
 * @param array $context_compil
718
 *     Tableau des informations du contexte
719
 * @return Contexte
720
 *     Objet Contexte
721
 **/
722
function reconstruire_contexte_compil($context_compil) {
723
	if (!is_array($context_compil)) {
724
		return $context_compil;
725
	}
726
	$p = new Contexte;
727
	$p->descr = array(
728
		'sourcefile' => $context_compil[0],
729
		'nom' => $context_compil[1]
730
	);
731
	$p->id_boucle = $context_compil[2];
732
	$p->ligne = $context_compil[3];
733
	$p->lang = $context_compil[4];
734
735
	return $p;
736
}
737
738
/**
739
 * Calcule le code d'affectation d'une valeur à une commande de boucle
740
 *
741
 * Décrit le code qui complète le tableau $command qui servira entre autres
742
 * à l'itérateur. Pour un nom de commande donnée et un code PHP décrivant
743
 * ou récupérant une valeur, on retourne le code PHP qui fait l'affectation.
744
 *
745
 * L'index 0 du tableau retourné indique, lorsqu'il n'est pas vide, que l'affectation
746
 * de la variable pourra être statique (c'est à dire qu'il ne dépend
747
 * pas d'une quelconque variable PHP), et donc attribué une fois pour toutes
748
 * quelque soit le nombre d'appels de la boucle.
749
 *
750
 * @param string $nom
751
 *    Nom de la commande
752
 * @param string $val
753
 *    Code PHP décrivant la valeur à affecter
754
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
755
 *
756
 *    - index 0 : Code pour une affectation statique. Si non rempli, la propriété devra
757
 *                être ré-affectée à chaque appel de la boucle.
758
 *    - index 1 : Code de l'affectation
759
 **/
760
function calculer_dec($nom, $val) {
761
	$static = 'if (!isset($command[\'' . $nom . '\'])) ';
762
	// si une variable apparait dans le calcul de la clause
763
	// il faut la re-evaluer a chaque passage
764
	if (
765
		strpos($val, '$') !== false
766
		/*
767
		OR strpos($val, 'sql_') !== false
768
		OR (
769
			$test = str_replace(array("array(",'\"',"\'"),array("","",""),$val) // supprimer les array( et les echappements de guillemets
770
			AND strpos($test,"(")!==FALSE // si pas de parenthese ouvrante, pas de fonction, on peut sortir
771
			AND $test = preg_replace(",'[^']*',UimsS","",$test) // supprimer les chaines qui peuvent contenir des fonctions SQL qui ne genent pas
772
			AND preg_match(",\w+\s*\(,UimsS",$test,$regs) // tester la presence de fonctions restantes
773
		)*/
774
	) {
775
		$static = "";
776
	}
777
778
	return array($static, '$command[\'' . $nom . '\'] = ' . $val . ';');
779
}
780
781
/**
782
 * Calcule l'expression PHP décrivant un tableau complexe (ou une chaîne)
783
 *
784
 * Lorsqu'un tableau est transmis, reconstruit de quoi créer le tableau
785
 * en code PHP (une sorte de var_export) en appelant pour chaque valeur
786
 * cette fonction de manière récursive.
787
 *
788
 * Si le premier index (0) du tableau est "'?'", retourne un code
789
 * de test entre les 3 autres valeurs (v1 ? v2 : v3). Les valeurs
790
 * pouvant être des tableaux aussi.
791
 *
792
 * @param mixed $a
793
 *     Les données dont on veut construire un équivalent de var_export
794
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be object|integer|double|string|null|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
795
 *     Expression PHP décrivant un texte ou un tableau
796
 **/
797
function calculer_dump_array($a) {
798
	if (!is_array($a)) {
799
		return $a;
800
	}
801
	$res = "";
802
	if ($a and $a[0] == "'?'") {
803
		return ("(" . calculer_dump_array($a[1]) .
804
			" ? " . calculer_dump_array($a[2]) .
805
			" : " . calculer_dump_array($a[3]) .
806
			")");
807
	} else {
808
		foreach ($a as $v) {
809
			$res .= ", " . calculer_dump_array($v);
810
		}
811
812
		return "\n\t\t\tarray(" . substr($res, 2) . ')';
813
	}
814
}
815
816
// http://code.spip.net/@calculer_dump_join
817
function calculer_dump_join($a) {
818
	$res = "";
819
	foreach ($a as $k => $v) {
820
		$res .= ", '$k' => array(" . implode(',', $v) . ")";
821
	}
822
823
	return 'array(' . substr($res, 2) . ')';
824
}
825
826
/**
827
 * Calcule l'expression PHP décrivant les informations FROM d'une boucle
828
 *
829
 * @param Boucle $boucle
830
 *     Description de la boucle
831
 * @return string
832
 *     Code PHP construisant un tableau des alias et noms des tables du FROM
833
 **/
834 View Code Duplication
function calculer_from(&$boucle) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
835
	$res = "";
836
	foreach ($boucle->from as $k => $v) {
837
		$res .= ",'$k' => '$v'";
838
	}
839
840
	return 'array(' . substr($res, 1) . ')';
841
}
842
843
/**
844
 * Calcule l'expression PHP décrivant des informations de type de jointure
845
 * pour un alias de table connu dans le FROM
846
 *
847
 * @param Boucle $boucle
848
 *     Description de la boucle
849
 * @return string
850
 *     Code PHP construisant un tableau des alias et type de jointure du FROM
851
 **/
852 View Code Duplication
function calculer_from_type(&$boucle) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
853
	$res = "";
854
	foreach ($boucle->from_type as $k => $v) {
855
		$res .= ",'$k' => '$v'";
856
	}
857
858
	return 'array(' . substr($res, 1) . ')';
859
}
860
861
// http://code.spip.net/@calculer_order
862
function calculer_order(&$boucle) {
863
	if (!$order = $boucle->order
864
		and !$order = $boucle->default_order
865
	) {
866
		$order = array();
867
	}
868
869
	/*if (isset($boucle->modificateur['collate'])){
870
		$col = "." . $boucle->modificateur['collate'];
871
		foreach($order as $k=>$o)
872
			if (strpos($order[$k],'COLLATE')===false)
873
				$order[$k].= $col;
874
	}*/
875
876
	return join(', ', $order);
877
}
878
879
// Production du code PHP a partir de la sequence livree par le phraseur
880
// $boucles est passe par reference pour affectation par index_pile.
881
// Retourne une expression PHP,
882
// (qui sera argument d'un Return ou la partie droite d'une affectation).
883
884
// http://code.spip.net/@calculer_liste
885
function calculer_liste($tableau, $descr, &$boucles, $id_boucle = '') {
886
	if (!$tableau) {
887
		return "''";
888
	}
889
	if (is_string($descr)) {
890
		if (isset($boucles[$descr])) {
891
			$idb = $descr;
892
			$descr = [];
893
			if (isset($boucles[$idb]->descr['id_mere_contexte'])) {
894
				$descr['id_mere'] = $boucles[$idb]->descr['id_mere_contexte'];
895
			}
896
			if (isset($boucles[$idb]->descr['sourcefile'])) {
897
				$descr['sourcefile'] = $boucles[$idb]->descr['sourcefile'];
898
			}
899
		}
900
		else {
901
			$descr = array();
902
		}
903
	}
904
	if (!isset($descr['niv'])) {
905
		$descr['niv'] = 0;
906
	}
907
	$codes = compile_cas($tableau, $descr, $boucles, $id_boucle);
908
	if ($codes === false) {
909
		return false;
910
	}
911
	$n = count($codes);
912
	if (!$n) {
913
		return "''";
914
	}
915
	$tab = str_repeat("\t", $descr['niv']);
916
	if (_request('var_mode_affiche') != 'validation') {
917
		if ($n == 1) {
918
			return $codes[0];
919
		} else {
920
			$res = '';
921
			foreach ($codes as $code) {
922
				if (!preg_match("/^'[^']*'$/", $code)
923
					or substr($res, -1, 1) !== "'"
924
				) {
925
					$res .= " .\n$tab$code";
926
				} else {
927
					$res = substr($res, 0, -1) . substr($code, 1);
928
				}
929
			}
930
931
			return '(' . substr($res, 2 + $descr['niv']) . ')';
932
		}
933
	} else {
934
		$nom = $descr['nom'] . $id_boucle . ($descr['niv'] ? $descr['niv'] : '');
935
936
		return "join('', array_map('array_shift', \$GLOBALS['debug_objets']['sequence']['$nom'] = array(" . join(" ,\n$tab",
937
			$codes) . ")))";
938
	}
939
}
940
941
942
define('_REGEXP_COND_VIDE_NONVIDE', "/^[(](.*)[?]\s*''\s*:\s*('[^']+')\s*[)]$/");
943
define('_REGEXP_COND_NONVIDE_VIDE', "/^[(](.*)[?]\s*('[^']+')\s*:\s*''\s*[)]$/");
944
define('_REGEXP_CONCAT_NON_VIDE', "/^(.*)[.]\s*'[^']+'\s*$/");
945
946
// http://code.spip.net/@compile_cas
947
function compile_cas($tableau, $descr, &$boucles, $id_boucle) {
948
949
	$codes = array();
950
	// cas de la boucle recursive
951
	if (is_array($id_boucle)) {
952
		$id_boucle = $id_boucle[0];
953
	}
954
	$type = !$id_boucle ? '' : $boucles[$id_boucle]->type_requete;
955
	$tab = str_repeat("\t", ++$descr['niv']);
956
	$mode = _request('var_mode_affiche');
957
	$err_e_c = '';
958
	// chaque commentaire introduit dans le code doit commencer
959
	// par un caractere distinguant le cas, pour exploitation par debug.
960
	foreach ($tableau as $p) {
961
962
		switch ($p->type) {
963
			// texte seul
964
			case 'texte':
965
				$code = sandbox_composer_texte($p->texte, $p);
966
				$commentaire = strlen($p->texte) . " signes";
0 ignored issues
show
Bug introduced by
The property texte does not seem to exist in Champ.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
967
				$avant = '';
968
				$apres = '';
969
				$altern = "''";
970
				break;
971
972
			case 'polyglotte':
973
				$code = "";
974
				foreach ($p->traductions as $k => $v) {
975
					$code .= ",'" .
976
						str_replace(array("\\", "'"), array("\\\\", "\\'"), $k) .
977
						"' => '" .
978
						str_replace(array("\\", "'"), array("\\\\", "\\'"), $v) .
979
						"'";
980
				}
981
				$code = "choisir_traduction(array(" .
982
					substr($code, 1) .
983
					"))";
984
				$commentaire = '&';
985
				$avant = '';
986
				$apres = '';
987
				$altern = "''";
988
				break;
989
990
			// inclure
991
			case 'include':
992
				$p->descr = $descr;
993
				$code = calculer_inclure($p, $boucles, $id_boucle);
994
				if ($code === false) {
995
					$err_e_c = true;
996
					$code = "''";
997
				} else {
998
					$commentaire = '<INCLURE ' . addslashes(str_replace("\n", ' ', $code)) . '>';
999
					$avant = '';
1000
					$apres = '';
1001
					$altern = "''";
1002
				}
1003
				break;
1004
1005
			// boucle
1006
			case TYPE_RECURSIF:
1007
				$nom = $p->id_boucle;
1008
				$newdescr = $descr;
1009
				$newdescr['id_mere'] = $nom;
1010
				$newdescr['niv']++;
1011
				$preaff = calculer_liste($p->preaff, $newdescr, $boucles, $id_boucle);
1012
				$avant = calculer_liste($p->avant, $newdescr, $boucles, $id_boucle);
1013
				$apres = calculer_liste($p->apres, $newdescr, $boucles, $id_boucle);
1014
				$postaff = calculer_liste($p->postaff, $newdescr, $boucles, $id_boucle);
1015
				$newdescr['niv']--;
1016
				$altern = calculer_liste($p->altern, $newdescr, $boucles, $id_boucle);
1017
				if ($preaff === false
1018
					or $avant === false
1019
					or $apres === false
1020
					or $altern === false
1021
					or $postaff === false) {
1022
					$err_e_c = true;
1023
					$code = "''";
1024
				} else {
1025
					$code = 'BOUCLE' .
1026
						str_replace("-", "_", $nom) . $descr['nom'] .
1027
						'($Cache, $Pile, $doublons, $Numrows, $SP)';
1028
					$commentaire = "?$nom";
1029
					if (!$boucles[$nom]->milieu
1030
						and $boucles[$nom]->type_requete <> TYPE_RECURSIF
1031
					) {
1032
						if ($preaff != "''") {
1033
							$code .= "\n. $preaff";
1034
						}
1035
						if ($altern != "''") {
1036
							$code .= "\n. $altern";
1037
						}
1038
						if ($postaff != "''") {
1039
							$code .= "\n. $postaff";
1040
						}
1041
						if ($avant <> "''" or $apres <> "''") {
1042
							spip_log("boucle $nom toujours vide, code superflu dans $descr[sourcefile]");
1043
						}
1044
						$avant = $apres = $altern = "''";
1045
					} else {
1046
						if ($preaff != "''") {
1047
							$avant = compile_concatene_parties_codes($preaff, $avant);
1048
							$altern = compile_concatene_parties_codes($preaff, $altern);
1049
						}
1050
						if ($postaff != "''") {
1051
							$apres = compile_concatene_parties_codes($apres, $postaff);
1052
							$altern = compile_concatene_parties_codes($altern, $postaff);
1053
						}
1054
						if ($altern != "''") {
1055
							$altern = "($altern)";
1056
						}
1057
					}
1058
				}
1059
				break;
1060
1061
			case 'idiome':
1062
				$l = array();
1063
				$code = '';
1064
				foreach ($p->arg as $k => $v) {
1065
					$_v = calculer_liste($v, $descr, $boucles, $id_boucle);
1066
					if ($k) {
1067
						$l[] = _q($k) . ' => ' . $_v;
1068
					} else {
1069
						$code = $_v;
1070
					}
1071
				}
1072
				// Si le module n'est pas fourni, l'expliciter sauf si calculé
1073
				if ($p->module) {
1074
					$m = $p->module . ':' . $p->nom_champ;
1075
				} elseif ($p->nom_champ) {
1076
					$m = MODULES_IDIOMES . ':' . $p->nom_champ;
1077
				} else {
1078
					$m = '';
1079
				}
1080
1081
				$code = (!$code ? "'$m'" :
1082
						($m ? "'$m' . $code" :
1083
							("(strpos(\$x=$code, ':') ? \$x : ('" . MODULES_IDIOMES . ":' . \$x))")))
1084
					. (!$l ? '' : (", array(" . implode(",\n", $l) . ")"));
1085
				$code = "_T($code)";
1086
				if ($p->param) {
1087
					$p->id_boucle = $id_boucle;
1088
					$p->boucles = &$boucles;
1089
					$code = compose_filtres($p, $code);
1090
				}
1091
				$commentaire = ":";
1092
				$avant = '';
1093
				$apres = '';
1094
				$altern = "''";
1095
				break;
1096
1097
			case 'champ':
1098
1099
				// cette structure pourrait etre completee des le phrase' (a faire)
1100
				$p->id_boucle = $id_boucle;
1101
				$p->boucles = &$boucles;
1102
				$p->descr = $descr;
1103
				#$p->interdire_scripts = true;
1104
				$p->type_requete = $type;
1105
1106
				$code = calculer_champ($p);
1107
				$commentaire = '#' . $p->nom_champ . $p->etoile;
1108
				$avant = calculer_liste($p->avant,
1109
					$descr, $boucles, $id_boucle);
1110
				$apres = calculer_liste($p->apres,
1111
					$descr, $boucles, $id_boucle);
1112
				$altern = "''";
1113
				// Si la valeur est destinee a une comparaison a ''
1114
				// forcer la conversion en une chaine par strval
1115
				// si ca peut etre autre chose qu'une chaine
1116
				if (($avant != "''" or $apres != "''")
1117
					and $code[0] != "'"
1118
#			AND (strpos($code,'interdire_scripts') !== 0)
1119
					and !preg_match(_REGEXP_COND_VIDE_NONVIDE, $code)
1120
					and !preg_match(_REGEXP_COND_NONVIDE_VIDE, $code)
1121
					and !preg_match(_REGEXP_CONCAT_NON_VIDE, $code)
1122
				) {
1123
					$code = "strval($code)";
1124
				}
1125
				break;
1126
1127
			default:
1128
				// Erreur de construction de l'arbre de syntaxe abstraite
1129
				$code = "''";
1130
				$p->descr = $descr;
1131
				$err_e_c = _T('zbug_erreur_compilation');
1132
				erreur_squelette($err_e_c, $p);
1133
		} // switch
1134
1135
		if ($code != "''") {
1136
			$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...
1137
			$codes[] = (($mode == 'validation') ?
1138
				"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...
1139
				: (($mode == 'code') ?
1140
					"\n// $commentaire\n$code" :
1141
					$code));
1142
		}
1143
	} // foreach
1144
1145
	return $err_e_c ? false : $codes;
1146
}
1147
1148
/**
1149
 * Concatene 2 parties de code, en simplifiant si l'une des 2 est vides
1150
 * @param $partie1
1151
 * @param $partie2
1152
 * @return string
1153
 */
1154
function compile_concatene_parties_codes($partie1, $partie2) {
1155
	if ($partie1 === "''") {
1156
		return $partie2;
1157
	}
1158
	if ($partie2 === "''") {
1159
		return $partie1;
1160
	}
1161
	return "$partie1\n. $partie2";
1162
}
1163
1164
1165
/**
1166
 * production d'une expression conditionnelle ((v=EXP) ? (p . v .s) : a)
1167
 * mais si EXP est de la forme (t ? 'C' : '') on produit (t ? (p . C . s) : a)
1168
 * de meme si EXP est de la forme (t ? '' : 'C')
1169
 * http://code.spip.net/@compile_retour
1170
 *
1171
 * @param string $code
1172
 *   le code principal, dont le resultat conditionnera le reste
1173
 * @param string $avant
1174
 *   la partie conditionnelle avant, qui est calculee apres le code, mais s'affiche avant si le code produit un resultat
1175
 * @param string $apres
1176
 *   la partie conditionnelle apres, qui est calculee apres le code, et s'affiche apres si le code produit un resultat
1177
 * @param string $altern
1178
 *   la partie alternative apres, qui est calculee apres le code, et s'affiche apres, si le code ne produit pas de resultat
1179
 * @param string $tab
1180
 *   tabulation
1181
 * @param int $n
1182
 *   compteur
1183
 * @return mixed|string
1184
 */
1185
function compile_retour($code, $avant, $apres, $altern, $tab, $n) {
1186
	if ($avant === "''") {
1187
		$avant = '';
1188
	}
1189
	if ($apres === "''") {
1190
		$apres = '';
1191
	}
1192
	if ($avant or $apres or ($altern !== "''")){
1193
		if (preg_match(_REGEXP_CONCAT_NON_VIDE, $code)){
1194
			$t = $code;
1195
			$cond = '';
1196
		} elseif (preg_match(_REGEXP_COND_VIDE_NONVIDE, $code, $r)) {
1197
			$t = $r[2];
1198
			$cond = '!' . $r[1];
1199
		} else {
1200
			if (preg_match(_REGEXP_COND_NONVIDE_VIDE, $code, $r)){
1201
				$t = $r[2];
1202
				$cond = $r[1];
1203
			} else {
1204
				$t = '$t' . $n;
1205
				$cond = "($t = $code)!==''";
1206
			}
1207
		}
1208
1209
		$res = (!$avant ? "" : "$avant . ") .
1210
			$t .
1211
			(!$apres ? "" : " . $apres");
1212
1213
		if ($res!==$t){
1214
			$res = "($res)";
1215
		}
1216
1217
		$code = (!$cond ? $res : "($cond ?\n\t$tab$res :\n\t$tab$altern)");
1218
	}
1219
1220
	return $code;
1221
1222
}
1223
1224
1225
function compile_inclure_doublons($lexemes) {
1226
	foreach ($lexemes as $v) {
1227
		if ($v->type === 'include' and $v->param) {
1228
			foreach ($v->param as $r) {
1229
				if (trim($r[0]) === 'doublons') {
1230
					return true;
1231
				}
1232
			}
1233
		}
1234
	}
1235
1236
	return false;
1237
}
1238
1239
// Prend en argument le texte d'un squelette, le nom de son fichier d'origine,
1240
// sa grammaire et un nom. Retourne False en cas d'erreur,
1241
// sinon retourne un tableau de fonctions PHP compilees a evaluer,
1242
// notamment une fonction portant ce nom et calculant une page.
1243
// Pour appeler la fonction produite, lui fournir 2 tableaux de 1 e'le'ment:
1244
// - 1er: element 'cache' => nom (du fichier ou` mettre la page)
1245
// - 2e: element 0 contenant un environnement ('id_article => $id_article, etc)
1246
// Elle retournera alors un tableau de 5 e'le'ments:
1247
// - 'texte' => page HTML, application du squelette a` l'environnement;
1248
// - 'squelette' => le nom du squelette
1249
// - 'process_ins' => 'html' ou 'php' selon la pre'sence de PHP dynamique
1250
// - 'invalideurs' =>  de'pendances de cette page, pour invalider son cache.
1251
// - 'entetes' => tableau des entetes http
1252
// En cas d'erreur, elle retournera un tableau des 2 premiers elements seulement
1253
1254
// http://code.spip.net/@public_compiler_dist
1255
function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect = '') {
1256
	// Pre-traitement : reperer le charset du squelette, et le convertir
1257
	// Bonus : supprime le BOM
1258
	include_spip('inc/charsets');
1259
	$squelette = transcoder_page($squelette);
1260
1261
	// rendre inertes les echappements de #[](){}<>
1262
	$i = 0;
1263
	while (false !== strpos($squelette, $inerte = '-INERTE' . $i)) {
1264
		$i++;
1265
	}
1266
	$squelette = preg_replace_callback(',\\\\([#[()\]{}<>]),',
1267
		function($a) use ($inerte) {
1268
			return "$inerte-" . ord($a[1]) . '-';
1269
		},
1270
		$squelette,
1271
		-1,
1272
		$esc
1273
	);
1274
1275
	$descr = array(
1276
		'nom' => $nom,
1277
		'gram' => $gram,
1278
		'sourcefile' => $sourcefile,
1279
		'squelette' => $squelette
1280
	);
1281
1282
	// Phraser le squelette, selon sa grammaire
1283
1284
	$boucles = array();
1285
	$f = charger_fonction('phraser_' . $gram, 'public');
1286
1287
	$squelette = $f($squelette, '', $boucles, $descr);
1288
1289
	$boucles = compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect);
1290
1291
	// restituer les echappements
1292
	if ($esc) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $esc of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1293
		foreach ($boucles as $i => $boucle) {
0 ignored issues
show
Bug introduced by
The expression $boucles of type false|array<string,objec...,{"":"object<Boucle>"}> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1294
			$boucles[$i]->return = preg_replace_callback(
1295
				",$inerte-(\d+)-,",
1296
				function($a) {
1297
					return chr($a[1]);
1298
				},
1299
				$boucle->return
1300
			);
1301
			$boucles[$i]->descr['squelette'] = preg_replace_callback(
1302
				",$inerte-(\d+)-,",
1303
				function($a) {
1304
					return "\\\\" . chr($a[1]);
1305
				},
1306
				$boucle->descr['squelette']
1307
			);
1308
		}
1309
	}
1310
1311
	$debug = ($boucles and defined('_VAR_MODE') and _VAR_MODE == 'debug');
1312
	if ($debug) {
1313
		include_spip('public/decompiler');
1314
		foreach ($boucles as $id => $boucle) {
1315
			if ($id) {
1316
				$decomp = "\n/* BOUCLE " .
1317
					$boucle->type_requete .
1318
					" " .
1319
					str_replace('*/', '* /', public_decompiler($boucle, $gram, 0, 'criteres')) .
1320
					($boucle->debug ? "\n *\n * " . implode("\n * ", $boucle->debug) . "\n" : '') .
1321
					" */\n";
1322
			} else {
1323
				$decomp = ("\n/*\n" .
1324
					str_replace('*/', '* /', public_decompiler($squelette, $gram))
1325
					. "\n*/");
1326
			}
1327
			$boucles[$id]->return = $decomp . $boucle->return;
1328
			$GLOBALS['debug_objets']['code'][$nom . $id] = $boucle->return;
1329
		}
1330
	}
1331
1332
	return $boucles;
1333
}
1334
1335
// Point d'entree pour arbre de syntaxe abstraite fourni en premier argument
1336
// Autres specifications comme ci-dessus
1337
1338
function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect = '') {
1339
	static $trouver_table;
1340
	spip_timer('calcul_skel');
1341
1342
	if (defined('_VAR_MODE') and _VAR_MODE == 'debug') {
1343
		$GLOBALS['debug_objets']['squelette'][$nom] = $descr['squelette'];
1344
		$GLOBALS['debug_objets']['sourcefile'][$nom] = $sourcefile;
1345
1346
		if (!isset($GLOBALS['debug_objets']['principal'])) {
1347
			$GLOBALS['debug_objets']['principal'] = $nom;
1348
		}
1349
	}
1350
	foreach ($boucles as $id => $boucle) {
1351
		$GLOBALS['debug_objets']['boucle'][$nom . $id] = $boucle;
1352
	}
1353
	$descr['documents'] = compile_inclure_doublons($squelette);
1354
1355
	// Demander la description des tables une fois pour toutes
1356
	if (!$trouver_table) {
1357
		$trouver_table = charger_fonction('trouver_table', 'base');
1358
	}
1359
1360
	// reperer si les doublons sont demandes
1361
	// pour un inclure ou une boucle document
1362
	// c'est utile a la fonction champs_traitements
1363
	foreach ($boucles as $id => $boucle) {
1364
		if (!($type = $boucle->type_requete)) {
1365
			continue;
1366
		}
1367
		if (!$descr['documents'] and (
1368
				(($type == 'documents') and $boucle->doublons) or
1369
				compile_inclure_doublons($boucle->avant) or
1370
				compile_inclure_doublons($boucle->apres) or
1371
				compile_inclure_doublons($boucle->milieu) or
1372
				compile_inclure_doublons($boucle->altern))
1373
		) {
1374
			$descr['documents'] = true;
1375
		}
1376
		if ($type != TYPE_RECURSIF) {
1377
			if (!$boucles[$id]->sql_serveur and $connect) {
1378
				$boucles[$id]->sql_serveur = $connect;
1379
			}
1380
1381
			// chercher dans les iterateurs du repertoire iterateur/
1382
			if ($g = charger_fonction(
1383
				preg_replace('/\W/', '_', $boucle->type_requete), 'iterateur', true)
1384
			) {
1385
				$boucles[$id] = $g($boucle);
1386
1387
				// sinon, en cas de requeteur d'un type predefini,
1388
				// utiliser les informations donnees par le requeteur
1389
				// cas "php:xx" et "data:xx".
1390
			} else {
1391
				if ($boucle->sql_serveur and $requeteur = charger_fonction($boucle->sql_serveur, 'requeteur', true)) {
1392
					$requeteur($boucles, $boucle, $id);
1393
1394
					// utiliser la description des champs transmis
1395
				} else {
1396
					$show = $trouver_table($type, $boucles[$id]->sql_serveur);
1397
					// si la table n'existe pas avec le connecteur par defaut,
1398
					// c'est peut etre une table qui necessite son connecteur dedie fourni
1399
					// permet une ecriture allegee (GEO) -> (geo:GEO)
1400
					if (!$show
1401
						and $show = $trouver_table($type, strtolower($type))
1402
					) {
1403
						$boucles[$id]->sql_serveur = strtolower($type);
1404
					}
1405
					if ($show) {
1406
						$boucles[$id]->show = $show;
1407
						// recopie les infos les plus importantes
1408
						$boucles[$id]->primary = isset($show['key']["PRIMARY KEY"]) ? $show['key']["PRIMARY KEY"] : '';
1409
						$boucles[$id]->id_table = $x = preg_replace(",^spip_,", "", $show['id_table']);
1410
						$boucles[$id]->from[$x] = $nom_table = $show['table'];
0 ignored issues
show
Unused Code introduced by
$nom_table is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1411
						$boucles[$id]->iterateur = 'SQL';
1412
1413
						if (empty($boucles[$id]->descr)) {
1414
							$boucles[$id]->descr = &$descr;
1415
						}
1416
						if ((!$boucles[$id]->jointures)
1417
							and is_array($show['tables_jointures'])
1418
							and count($x = $show['tables_jointures'])
1419
						) {
1420
							$boucles[$id]->jointures = $x;
1421
						}
1422
						if ($boucles[$id]->jointures_explicites) {
1423
							$jointures = preg_split("/\s+/", $boucles[$id]->jointures_explicites);
1424
							while ($j = array_pop($jointures)) {
1425
								array_unshift($boucles[$id]->jointures, $j);
1426
							}
1427
						}
1428
					} else {
1429
						// Pas une erreur si la table est optionnelle
1430
						if ($boucles[$id]->table_optionnelle) {
1431
							$boucles[$id]->type_requete = '';
1432
						} else {
1433
							$boucles[$id]->type_requete = false;
1434
							$boucle = $boucles[$id];
1435
							$x = (!$boucle->sql_serveur ? '' :
1436
									($boucle->sql_serveur . ":")) .
1437
								$type;
1438
							$msg = array(
1439
								'zbug_table_inconnue',
1440
								array('table' => $x)
1441
							);
1442
							erreur_squelette($msg, $boucle);
1443
						}
1444
					}
1445
				}
1446
			}
1447
		}
1448
	}
1449
1450
	// Commencer par reperer les boucles appelees explicitement 
1451
	// car elles indexent les arguments de maniere derogatoire
1452
	foreach ($boucles as $id => $boucle) {
1453
		if ($boucle->type_requete == TYPE_RECURSIF and $boucle->param) {
1454
			$boucles[$id]->descr = &$descr;
1455
			$rec = &$boucles[$boucle->param[0]];
1456
			if (!$rec) {
1457
				$msg = array(
1458
					'zbug_boucle_recursive_undef',
1459
					array('nom' => $boucle->param[0])
1460
				);
1461
				erreur_squelette($msg, $boucle);
1462
				$boucles[$id]->type_requete = false;
1463
			} else {
1464
				$rec->externe = $id;
1465
				$descr['id_mere'] = $id;
1466
				$boucles[$id]->return =
1467
					calculer_liste(array($rec),
1468
						$descr,
1469
						$boucles,
1470
						$boucle->param);
1471
			}
1472
		}
1473
	}
1474
	foreach ($boucles as $id => $boucle) {
1475
		$id = strval($id); // attention au type dans index_pile
1476
		$type = $boucle->type_requete;
1477
		if ($type and $type != TYPE_RECURSIF) {
1478
			$res = '';
1479
			if ($boucle->param) {
1480
				// retourne un tableau en cas d'erreur
1481
				$res = calculer_criteres($id, $boucles);
1482
			}
1483
			$descr['id_mere'] = $id;
1484
			$boucles[$id]->return =
1485
				calculer_liste($boucle->milieu,
1486
					$descr,
1487
					$boucles,
1488
					$id);
1489
			// Si les criteres se sont mal compiles
1490
			// ne pas tenter d'assembler le code final
1491
			// (mais compiler le corps pour detection d'erreurs)
1492
			if (is_array($res)) {
1493
				$boucles[$id]->type_requete = false;
1494
			}
1495
		}
1496
	}
1497
1498
	// idem pour la racine
1499
	$descr['id_mere'] = '';
1500
	$corps = calculer_liste($squelette, $descr, $boucles);
1501
1502
1503
	// Calcul du corps de toutes les fonctions PHP,
1504
	// en particulier les requetes SQL et TOTAL_BOUCLE
1505
	// de'terminables seulement maintenant
1506
1507
	foreach ($boucles as $id => $boucle) {
1508
		$boucle = $boucles[$id] = pipeline('pre_boucle', $boucle);
1509
		if ($boucle->return === false) {
1510
			$corps = false;
1511
			continue;
1512
		}
1513
		// appeler la fonction de definition de la boucle
1514
1515
		if ($req = $boucle->type_requete) {
0 ignored issues
show
Unused Code introduced by
$req is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1516
			// boucle personnalisée ?
1517
			$table = strtoupper($boucle->type_requete);
1518
			$serveur = strtolower($boucle->sql_serveur);
1519
			if (
1520
				// fonction de boucle avec serveur & table
1521
				(!$serveur or
1522
					((!function_exists($f = "boucle_" . $serveur . "_" . $table))
1523
						and (!function_exists($f = $f . "_dist"))
1524
					)
1525
				)
1526
				// fonction de boucle avec table
1527
				and (!function_exists($f = "boucle_" . $table))
1528
				and (!function_exists($f = $f . "_dist"))
1529
			) {
1530
				// fonction de boucle standard 
1531
				if (!function_exists($f = 'boucle_DEFAUT')) {
1532
					$f = 'boucle_DEFAUT_dist';
1533
				}
1534
			}
1535
1536
			$req = "\n\n\tstatic \$command = array();\n\t" .
1537
				"static \$connect;\n\t" .
1538
				"\$command['connect'] = \$connect = " .
1539
				_q($boucle->sql_serveur) .
1540
				";" .
1541
				$f($id, $boucles);
1542
		} else {
1543
			$req = ("\n\treturn '';");
1544
		}
1545
1546
		$boucles[$id]->return =
1547
			"\n\nfunction BOUCLE" . strtr($id, "-", "_") . $nom .
1548
			'(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' .
1549
			$req .
1550
			"\n}\n";
1551
	}
1552
1553
	// Au final, si le corps ou un critere au moins s'est mal compile
1554
	// retourner False, sinon inserer leur decompilation
1555
	if (is_bool($corps)) {
1556
		return false;
1557
	}
1558
1559
	$principal = "\nfunction " . $nom . '($Cache, $Pile, $doublons = array(), $Numrows = array(), $SP = 0) {
1560
'
1561
		// reporter de maniere securisee les doublons inclus
1562
		. '
1563
	if (isset($Pile[0]["doublons"]) AND is_array($Pile[0]["doublons"]))
1564
		$doublons = nettoyer_env_doublons($Pile[0]["doublons"]);
1565
1566
	$connect = ' .
1567
		_q($connect) . ';
1568
	$page = ' .
1569
		// ATTENTION, le calcul de l'expression $corps affectera $Cache
1570
		// c'est pourquoi on l'affecte a la variable auxiliaire $page.
1571
		// avant de referencer $Cache
1572
		$corps . ";
1573
1574
	return analyse_resultat_skel(" . var_export($nom, true)
1575
		. ", \$Cache, \$page, " . var_export($sourcefile, true) . ");
1576
}";
1577
1578
	$secondes = spip_timer('calcul_skel');
1579
	spip_log("COMPIL ($secondes) [$sourcefile] $nom.php");
1580
	// $connect n'est pas sûr : on nettoie
1581
	$connect = preg_replace(',[^\w],', '', $connect);
1582
1583
	// Assimiler la fct principale a une boucle anonyme, pour retourner un resultat simple
1584
	$code = new Boucle;
1585
	$code->descr = $descr;
1586
	$code->return = '
1587
//
1588
// Fonction principale du squelette ' .
1589
		$sourcefile .
1590
		($connect ? " pour $connect" : '') .
1591
		(!CODE_COMMENTE ? '' : "\n// Temps de compilation total: $secondes") .
1592
		"\n//\n" .
1593
		$principal;
1594
1595
	$boucles[''] = $code;
1596
1597
	return $boucles;
1598
}
1599
1600
1601
/**
1602
 * Requeteur pour les boucles (php:nom_iterateur)
1603
 *
1604
 * Analyse si le nom d'iterateur correspond bien a une classe PHP existante
1605
 * et dans ce cas charge la boucle avec cet iterateur.
1606
 * Affichera une erreur dans le cas contraire.
1607
 *
1608
 * @param $boucles Liste des boucles
1609
 * @param $boucle  La boucle parcourue
1610
 * @param $id      L'identifiant de la boucle parcourue
1611
 *
1612
 **/
1613
function requeteur_php_dist(&$boucles, &$boucle, &$id) {
1614
	if (class_exists($boucle->type_requete)) {
1615
		$g = charger_fonction('php', 'iterateur');
1616
		$boucles[$id] = $g($boucle, $boucle->type_requete);
1617 View Code Duplication
	} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1618
		$x = $boucle->type_requete;
1619
		$boucle->type_requete = false;
1620
		$msg = array(
1621
			'zbug_iterateur_inconnu',
1622
			array('iterateur' => $x)
1623
		);
1624
		erreur_squelette($msg, $boucle);
1625
	}
1626
}
1627
1628
1629
/**
1630
 * Requeteur pour les boucles (data:type de donnee)
1631
 * note: (DATA) tout court ne passe pas par ici.
1632
 *
1633
 * Analyse si le type de donnee peut etre traite
1634
 * et dans ce cas charge la boucle avec cet iterateur.
1635
 * Affichera une erreur dans le cas contraire.
1636
 *
1637
 * @param $boucles Liste des boucles
1638
 * @param $boucle  La boucle parcourue
1639
 * @param $id      L'identifiant de la boucle parcourue
1640
 *
1641
 **/
1642
function requeteur_data_dist(&$boucles, &$boucle, &$id) {
1643
	include_spip('iterateur/data');
1644
	if ($h = charger_fonction($boucle->type_requete . '_to_array', 'inc', true)) {
0 ignored issues
show
Unused Code introduced by
$h is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1645
		$g = charger_fonction('data', 'iterateur');
1646
		$boucles[$id] = $g($boucle);
1647
		// from[0] stocke le type de data (rss, yql, ...)
1648
		$boucles[$id]->from[] = $boucle->type_requete;
1649
1650 View Code Duplication
	} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1651
		$x = $boucle->type_requete;
1652
		$boucle->type_requete = false;
1653
		$msg = array(
1654
			'zbug_requeteur_inconnu',
1655
			array(
1656
				'requeteur' => 'data',
1657
				'type' => $x
1658
			)
1659
		);
1660
		erreur_squelette($msg, $boucle);
1661
	}
1662
}
1663