Completed
Push — master ( e522b2...6ebb44 )
by cam
15:40
created

compiler.php ➔ calculer_inclure()   D

Complexity

Conditions 19
Paths 153

Size

Total Lines 74

Duplication

Lines 7
Ratio 9.46 %

Importance

Changes 0
Metric Value
cc 19
nc 153
nop 3
dl 7
loc 74
rs 4.075
c 0
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, Système de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright © avec tendresse depuis 2001                                 *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.     *
10
 *  Pour plus de détails 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
// https://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 string|false?

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 === '""' or $code === "''") {
185
		$trace = $p->fonctions;
0 ignored issues
show
Bug introduced by
The property fonctions 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...
186
		while (is_array($trace)
187
		  and $trace = array_filter($trace)
188
			and count($trace)==1) {
189
			$trace = reset($trace);
190
		}
191
		$erreur_p_i_i = array(
192
			'zbug_parametres_inclus_incorrects',
193
			array('param' => print_r($trace, true))
194
		);
195
		erreur_squelette($erreur_p_i_i, $p);
196
197
		return "''";
198
	}
199
	$compil = texte_script(memoriser_contexte_compil($p));
200
201
	if (is_array($_contexte)) {
202
		// Critere d'inclusion {env} (et {self} pour compatibilite ascendante)
203 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...
204
			unset($_contexte['env']);
205
		}
206
207
		// noter les doublons dans l'appel a public.php
208
		if (isset($_contexte['doublons'])) {
209
			$_contexte['doublons'] = "\\'doublons\\' => '.var_export(\$doublons,true).'";
210
		}
211
212 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...
213
			$ajax = preg_replace(",=>(.*)$,ims", '=> ($v=(\\1))?$v:true', $_contexte['ajax']);
214
			unset($_contexte['ajax']);
215
		}
216
217
		$_contexte = join(",\n\t", $_contexte);
218
	} else {
219
		return false;
220
	} // j'aurais voulu toucher le fond ...
221
222
	$contexte = 'array(' . $_contexte . ')';
223
224
	if ($env) {
225
		$contexte = "array_merge('.var_export(\$Pile[0],1).',$contexte)";
226
	}
227
228
	// s'il y a une extension .php, ce n'est pas un squelette
229
	if ($fichier and preg_match('/^.+[.]php$/s', $fichier)) {
230
		$code = sandbox_composer_inclure_php($fichier, $p, $contexte);
231
	} else {
232
		$_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...
233
		if ($ajax) {
234
			$_options[] = $ajax;
235
		}
236
		$code = " ' . argumenter_squelette($code) . '";
237
		$code = "echo " . sprintf(CODE_RECUPERER_FOND, $code, $contexte, implode(',', $_options),
238
				"_request(\"connect\")") . ';';
239
	}
240
241
	return "\n'<'.'" . "?php " . $code . "\n?'." . "'>'";
242
}
243
244
245
/**
246
 * Gérer les statuts declarés pour cette table
247
 *
248
 * S'il existe des statuts sur cette table, déclarés dans la description
249
 * d'un objet éditorial, applique leurs contraintes
250
 *
251
 * @param Boucle $boucle
252
 *     Descrition de la boucle
253
 * @param bool $echapper
254
 *     true pour échapper le code créé
255
 * @param bool $ignore_previsu
256
 *     true pour ne tester que le cas publie et ignorer l'eventuel var_mode=preview de la page
257
 */
258
function instituer_boucle(&$boucle, $echapper = true, $ignore_previsu = false) {
259
	/*
260
	$show['statut'][] = array(
261
		'champ'=>'statut',  // champ de la table sur lequel porte le filtrage par le statut
262
		'publie'=>'publie', // valeur ou liste de valeurs, qui definissent l'objet comme publie.
263
		'previsu'=>'publie,prop', // valeur ou liste de valeurs qui sont visibles en previsu
264
		'post_date'=>'date', // un champ de date pour la prise en compte des post_dates, ou rien sinon
265
	  'exception'=>'statut', // liste des modificateurs qui annulent le filtrage par statut
266
	                         // si plusieurs valeurs : array('statut','tout','lien')
267
	);
268
269
	Pour 'publier' ou 'previsu', si la chaine commence par un "!" on exclu au lieu de filtrer sur les valeurs donnees
270
	si la chaine est vide, on ne garde rien si elle est seulement "!" on n'exclu rien
271
272
	Si le statut repose sur une jointure, 'champ' est alors un tableau du format suivant :
273
	'champ'=>array(
274
	    array(table1, cle1),
275
	    ...
276
	    array(tablen, clen),
277
	    champstatut
278
	 )
279
280
	champstatut est alors le champ statut sur la tablen
281
	dans les jointures, clen peut etre un tableau pour une jointure complexe : array('id_objet','id_article','objet','article')
282
*/
283
	$id_table = $boucle->id_table;
284
	$show = $boucle->show;
285
	if (isset($show['statut']) and $show['statut']) {
286
		foreach ($show['statut'] as $k => $s) {
287
			// Restreindre aux elements publies si pas de {statut} ou autre dans les criteres
288
			$filtrer = true;
289
			if (isset($s['exception'])) {
290
				foreach (is_array($s['exception']) ? $s['exception'] : array($s['exception']) as $m) {
291
					if (isset($boucle->modificateur[$m]) or isset($boucle->modificateur['criteres'][$m])) {
292
						$filtrer = false;
293
						break;
294
					}
295
				}
296
			}
297
298
			if ($filtrer) {
299
				if (is_array($s['champ'])) {
300
					$statut = preg_replace(',\W,', '', array_pop($s['champ'])); // securite
301
					$jointures = array();
302
					// indiquer la description de chaque table dans le tableau de jointures,
303
					// ce qui permet d'eviter certains GROUP BY inutiles.
304
					$trouver_table = charger_fonction('trouver_table', 'base');
305
					foreach ($s['champ'] as $j) {
306
						$id = reset($j);
307
						$def = $trouver_table($id);
308
						$jointures[] = array('', array($id, $def), end($j));
309
					}
310
					$jointures[0][0] = $id_table;
311
					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...
312
						include_spip('public/jointures');
313
						fabrique_jointures($boucle, $jointures, true, $boucle->show, $id_table, '', $echapper);
314
					}
315
					// trouver l'alias de la table d'arrivee qui porte le statut
316
					$id = array_search($id, $boucle->from);
317
				} else {
318
					$id = $id_table;
319
					$statut = preg_replace(',\W,', '', $s['champ']); // securite
320
				}
321
				$mstatut = $id . '.' . $statut;
322
323
				$arg_ignore_previsu = ($ignore_previsu ? ",true" : '');
324
				include_spip('public/quete');
325
				if (isset($s['post_date']) and $s['post_date']
326
					and $GLOBALS['meta']["post_dates"] == 'non'
327
				) {
328
					$date = $id . '.' . preg_replace(',\W,', '', $s['post_date']); // securite
329
					array_unshift($boucle->where,
330
						$echapper ?
331
							"\nquete_condition_postdates('$date'," . _q($boucle->sql_serveur) . "$arg_ignore_previsu)"
332
							:
333
							quete_condition_postdates($date, $boucle->sql_serveur, $ignore_previsu)
334
					);
335
				}
336
				array_unshift($boucle->where,
337
					$echapper ?
338
						"\nquete_condition_statut('$mstatut',"
339
						. _q($s['previsu']) . ","
340
						. _q($s['publie']) . ","
341
						. _q($boucle->sql_serveur) . "$arg_ignore_previsu)"
342
						:
343
						quete_condition_statut($mstatut, $s['previsu'], $s['publie'], $boucle->sql_serveur, $ignore_previsu)
344
				);
345
			}
346
		}
347
	}
348
}
349
350
/**
351
 * Produit le corps PHP d'une boucle Spip.
352
 *
353
 * Ce corps remplit une variable $t0 retournée en valeur.
354
 * Ici on distingue boucles recursives et boucle à requête SQL
355
 * et on insère le code d'envoi au debusqueur du resultat de la fonction.
356
 *
357
 * @param string $id_boucle
358
 *    Identifiant de la boucle
359
 * @param array $boucles
360
 *    AST du squelette
361
 * @return string
362
 *    Code PHP compilé de la boucle
363
 */
364
function calculer_boucle($id_boucle, &$boucles) {
365
366
	$boucle = &$boucles[$id_boucle];
367
	instituer_boucle($boucle);
368
	$boucles[$id_boucle] = pipeline('post_boucle', $boucles[$id_boucle]);
369
370
	// en mode debug memoriser les premiers passages dans la boucle,
371
	// mais pas tous, sinon ca pete.
372
	if (_request('var_mode_affiche') != 'resultat') {
373
		$trace = '';
374
	} else {
375
		$_trace = $boucles[$id_boucle]->descr['nom'] . $id_boucle;
376
		$_trace = "\$GLOBALS['debug_objets']['resultat']['$_trace']";
377
		$trace = "
378
		if (empty($_trace)) { 
379
			$_trace = []; 
380
		}
381
		if (count($_trace) < 3) { 
382
			$_trace" . "[] = \$t0; 
383
		}";
384
	}
385
386
	return ($boucles[$id_boucle]->type_requete == TYPE_RECURSIF)
387
		? calculer_boucle_rec($id_boucle, $boucles, $trace)
388
		: calculer_boucle_nonrec($id_boucle, $boucles, $trace);
389
}
390
391
392
/**
393
 * Compilation d'une boucle recursive.
394
 *
395
 * @internal
396
 *    Il suffit (ET IL FAUT) sauvegarder les valeurs des arguments passes par
397
 *    reference, car par definition un tel passage ne les sauvegarde pas
398
 *
399
 * @param string $id_boucle
400
 *    Identifiant de la boucle
401
 * @param array $boucles
402
 *    AST du squelette
403
 * @param string $trace
404
 *    Code PHP (en mode debug uniquement) servant à conserver une
405
 *    trace des premières valeurs de la boucle afin de pouvoir
406
 *    les afficher dans le débugueur ultérieurement
407
 * @return string
408
 *    Code PHP compilé de la boucle récursive
409
 **/
410
function calculer_boucle_rec($id_boucle, &$boucles, $trace) {
411
	$nom = $boucles[$id_boucle]->param[0];
412
413
	return
414
		// Numrows[$nom] peut ne pas être encore defini
415
		"\n\t\$save_numrows = (isset(\$Numrows['$nom']) ? \$Numrows['$nom'] : array());"
416
		. "\n\t\$t0 = " . $boucles[$id_boucle]->return . ";"
417
		. "\n\t\$Numrows['$nom'] = (\$save_numrows);"
418
		. $trace
419
		. "\n\treturn \$t0;";
420
}
421
422
/**
423
 * Compilation d'une boucle non recursive.
424
 *
425
 * La constante donne le cadre systématique du code:
426
 *
427
 * - %s1: initialisation des arguments de calculer_select
428
 * - %s2: appel de calculer_select en donnant un contexte pour les cas d'erreur
429
 * - %s3: initialisation du sous-tableau Numrows[id_boucle]
430
 * - %s4: sauvegarde de la langue et calcul des invariants de boucle sur elle
431
 * - %s5: boucle while sql_fetch ou str_repeat si corps monotone
432
 * - %s6: restauration de la langue
433
 * - %s7: liberation de la ressource, en tenant compte du serveur SQL
434
 * - %s8: code de trace eventuel avant le retour
435
 **/
436
define('CODE_CORPS_BOUCLE', '%s
437
	if (defined("_BOUCLE_PROFILER")) $timer = time()+(float)microtime();
438
	$t0 = "";
439
	// REQUETE
440
	$iter = IterFactory::create(
441
		"%s",
442
		%s,
443
		array(%s)
444
	);
445
	if (!$iter->err()) {
446
	%s%s$SP++;
447
	// RESULTATS
448
	%s
449
	%s$iter->free();
450
	}%s
451
	if (defined("_BOUCLE_PROFILER")
452
	AND 1000*($timer = (time()+(float)microtime())-$timer) > _BOUCLE_PROFILER)
453
		spip_log(intval(1000*$timer)."ms %s","profiler"._LOG_AVERTISSEMENT);
454
	return $t0;'
455
);
456
457
/**
458
 * Compilation d'une boucle (non recursive).
459
 *
460
 * @param string $id_boucle
461
 *    Identifiant de la boucle
462
 * @param array $boucles
463
 *    AST du squelette
464
 * @param string $trace
465
 *    Code PHP (en mode debug uniquement) servant à conserver une
466
 *    trace des premières valeurs de la boucle afin de pouvoir
467
 *    les afficher dans le débugueur ultérieurement
468
 * @return string
469
 *    Code PHP compilé de la boucle récursive
470
 **/
471
function calculer_boucle_nonrec($id_boucle, &$boucles, $trace) {
472
473
	$boucle = &$boucles[$id_boucle];
474
	$return = $boucle->return;
475
	$type_boucle = $boucle->type_requete;
476
	$primary = $boucle->primary;
477
	$constant = preg_match(CODE_MONOTONE, str_replace("\\'", '', $return));
478
	$flag_cpt = $boucle->mode_partie || $boucle->cptrows;
479
	$corps = '';
480
481
	// faudrait expanser le foreach a la compil, car y en a souvent qu'un 
482
	// et puis faire un [] plutot qu'un "','."
483
	if ($boucle->doublons) {
484
		$corps .= "\n\t\t\tforeach(" . $boucle->doublons . ' as $k) $doublons[$k] .= "," . ' .
485
			index_pile($id_boucle, $primary, $boucles)
486
			. "; // doublons\n";
487
	}
488
489
	// La boucle doit-elle selectionner la langue ?
490
	// - par defaut, les boucles suivantes le font
491
	//    (sauf si forcer_lang==true ou si le titre contient <multi>).
492
	// - a moins d'une demande explicite via {!lang_select}
493
	if (!$constant && $boucle->lang_select != 'non' &&
494
		(($boucle->lang_select == 'oui') ||
495
			in_array($type_boucle, array(
496
				'articles',
497
				'rubriques',
498
				'hierarchie',
499
				'breves'
500
			)))
501
	) {
502
		// Memoriser la langue avant la boucle et la restituer apres
503
		// afin que le corps de boucle affecte la globale directement
504
		$init_lang = "lang_select(\$GLOBALS['spip_lang']);\n\t";
505
		$fin_lang = "lang_select();\n\t";
506
		$fin_lang_select_public = "\n\t\tlang_select();";
507
508
		$corps .=
509
			"\n\t\tlang_select_public("
510
			. index_pile($id_boucle, 'lang', $boucles)
511
			. ", '" . $boucle->lang_select . "'"
512
			. (in_array($type_boucle, array(
513
				'articles',
514
				'rubriques',
515
				'hierarchie',
516
				'breves'
517
			)) ? ', ' . index_pile($id_boucle, 'titre', $boucles) : '')
518
			. ');';
519
	} else {
520
		$init_lang = '';
521
		$fin_lang = '';
522
		$fin_lang_select_public = '';
523
		// sortir les appels au traducteur (invariants de boucle)
524
		if (strpos($return, '?php') === false
525
			and preg_match_all("/\W(_T[(]'[^']*'[)])/", $return, $r)
526
		) {
527
			$i = 1;
528
			foreach ($r[1] as $t) {
529
				$init_lang .= "\n\t\$l$i = $t;";
530
				$return = str_replace($t, "\$l$i", $return);
531
				$i++;
532
			}
533
		}
534
	}
535
536
	// gestion optimale des separateurs et des boucles constantes
537
	if (count($boucle->separateur)) {
538
		$code_sep = ("'" . str_replace("'", "\'", join('', $boucle->separateur)) . "'");
539
	}
540
541
	$corps .=
542
		((!$boucle->separateur) ?
543
			(($constant && !$corps && !$flag_cpt) ? $return :
544
				(($return === "''") ? '' :
545
					("\n\t\t" . '$t0 .= ' . $return . ";"))) :
546
			("\n\t\t\$t1 " .
547
				((strpos($return, '$t1.') === 0) ?
548
					(".=" . substr($return, 4)) :
549
					('= ' . $return)) .
550
				";\n\t\t" .
551
				'$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...
552
553
	// Calculer les invalideurs si c'est une boucle non constante et si on
554
	// souhaite invalider ces elements
555
	if (!$constant and $primary) {
556
		include_spip('inc/invalideur');
557
		$corps = calcul_invalideurs($corps, $primary,$boucles, $id_boucle);
558
	}
559
560
	// gerer le compteur de boucle 
561
	// avec ou sans son utilisation par les criteres {1/3} {1,4} {n-2,1}...
562
563
	if ($boucle->partie or $boucle->cptrows) {
564
		$corps = "\n\t\t\$Numrows['$id_boucle']['compteur_boucle']++;"
565
			. $boucle->partie
566
			. $corps;
567
	}
568
569
	// depiler la lang de la boucle si besoin
570
	$corps .= $fin_lang_select_public;
571
572
	// si le corps est une constante, ne pas appeler le serveur N fois!
573
574
	if (preg_match(CODE_MONOTONE, str_replace("\\'", '', $corps), $r)) {
575
		if (!isset($r[2]) or (!$r[2])) {
576
			if (!$boucle->numrows) {
577
				return "\n\t\$t0 = '';";
578
			} else {
579
				$corps = "";
580
			}
581
		} else {
582
			$boucle->numrows = true;
583
			$corps = "\n\t\$t0 = str_repeat($corps, \$Numrows['$id_boucle']['total']);";
584
		}
585
	} else {
586
		$corps = "while (\$Pile[\$SP]=\$iter->fetch()) {\n$corps\n	}";
587
	}
588
589
	$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...
590
	if (!$boucle->select) {
591
		if (!$boucle->numrows or $boucle->limit or $boucle->mode_partie or $boucle->group) {
592
			$count = '1';
593
		} else {
594
			$count = 'count(*)';
595
		}
596
		$boucles[$id_boucle]->select[] = $count;
597
	}
598
599
	if ($flag_cpt) {
600
		$nums = "\n\t// COMPTEUR\n\t"
601
			. "\$Numrows['$id_boucle']['compteur_boucle'] = 0;\n\t";
602
	} else {
603
		$nums = '';
604
	}
605
606
	if ($boucle->numrows or $boucle->mode_partie) {
607
		$nums .= "\$Numrows['$id_boucle']['command'] = \$command;\n\t"
608
			. "\$Numrows['$id_boucle']['total'] = @intval(\$iter->count());"
609
			. $boucle->mode_partie
610
			. "\n\t";
611
	}
612
613
	// Ne calculer la requete que maintenant
614
	// car ce qui precede appelle index_pile qui influe dessus
615
616
	$init = (($init = $boucles[$id_boucle]->doublons)
617
			? ("\n\t$init = array();") : '')
618
		. calculer_requete_sql($boucles[$id_boucle]);
619
620
	$contexte = memoriser_contexte_compil($boucle);
621
622
	$a = sprintf(CODE_CORPS_BOUCLE,
623
		$init,
624
		$boucle->iterateur,
625
		"\$command",
626
		$contexte,
627
		$nums,
628
		$init_lang,
629
		$corps,
630
		$fin_lang,
631
		$trace,
632
		'BOUCLE' . $id_boucle . ' @ ' . ($boucle->descr['sourcefile'])
633
	);
634
635
#	var_dump($a);exit;
636
	return $a;
637
}
638
639
640
/**
641
 * Calcule le code PHP d'une boucle contenant les informations qui produiront une requête SQL
642
 *
643
 * Le code produit est un tableau associatif $command contenant les informations
644
 * pour que la boucle produise ensuite sa requête, tel que `$command['from'] = 'spip_articles';`
645
 *
646
 * @param Boucle $boucle
647
 *     AST de la boucle
648
 * @return string
649
 *     Code PHP compilé définissant les informations de requête
650
 **/
651
function calculer_requete_sql($boucle) {
652
	$init = array();
653
	$init[] = calculer_dec('table', "'" . $boucle->id_table . "'");
654
	$init[] = calculer_dec('id', "'" . $boucle->id_boucle . "'");
655
	# En absence de champ c'est un decompte :
656
	$init[] = calculer_dec('from', calculer_from($boucle));
657
	$init[] = calculer_dec('type', calculer_from_type($boucle));
658
	$init[] = calculer_dec('groupby',
659
		'array(' . (($g = join("\",\n\t\t\"", $boucle->group)) ? '"' . $g . '"' : '') . ")");
660
	$init[] = calculer_dec('select', 'array("' . join("\",\n\t\t\"", $boucle->select) . "\")");
661
	$init[] = calculer_dec('orderby', 'array(' . calculer_order($boucle) . ")");
662
	$init[] = calculer_dec('where', calculer_dump_array($boucle->where));
663
	$init[] = calculer_dec('join', calculer_dump_join($boucle->join));
664
	$init[] = calculer_dec('limit',
665
		(strpos($boucle->limit, 'intval') === false ?
666
			"'" . $boucle->limit . "'"
667
			:
668
			$boucle->limit));
669
	$init[] = calculer_dec('having', calculer_dump_array($boucle->having));
670
	$s = $d = "";
671
	// l'index 0 de $i indique si l'affectation est statique (contenu)
672
	// ou recalculée à chaque passage (vide)
673
	foreach ($init as $i) {
674
		if (reset($i)) {
675
			$s .= "\n\t\t" . end($i);
676
		} # statique
677
		else {
678
			$d .= "\n\t" . end($i);
679
		} # dynamique
680
	}
681
682
	return ($boucle->hierarchie ? "\n\t$boucle->hierarchie" : '')
683
	. $boucle->in
684
	. $boucle->hash
685
	. "\n\t" . 'if (!isset($command[\'table\'])) {'
686
	. $s
687
	. "\n\t}"
688
	. $d;
689
}
690
691
/**
692
 * Retourne une chaîne des informations du contexte de compilation
693
 *
694
 * Retourne la source, le nom, l'identifiant de boucle, la ligne, la langue
695
 * de l'élément dans une chaîne.
696
 *
697
 * @see reconstruire_contexte_compil()
698
 *
699
 * @param Object $p
700
 *     Objet de l'AST dont on mémorise le contexte
701
 * @return string
702
 *     Informations du contexte séparés par des virgules,
703
 *     qui peut être utilisé pour la production d'un tableau array()
704
 **/
705
function memoriser_contexte_compil($p) {
706
	return join(',', array(
707
		_q(isset($p->descr['sourcefile']) ? $p->descr['sourcefile'] : ''),
708
		_q(isset($p->descr['nom']) ? $p->descr['nom'] : ''),
709
		_q(isset($p->id_boucle) ? $p->id_boucle : null),
710
		intval($p->ligne),
711
		'$GLOBALS[\'spip_lang\']'
712
	));
713
}
714
715
/**
716
 * Reconstruit un contexte de compilation
717
 *
718
 * Pour un tableau d'information de contexte donné,
719
 * retourne un objet Contexte (objet générique de l'AST)
720
 * avec ces informations
721
 *
722
 * @see memoriser_contexte_compil()
723
 *
724
 * @param array $context_compil
725
 *     Tableau des informations du contexte
726
 * @return Contexte
727
 *     Objet Contexte
728
 **/
729
function reconstruire_contexte_compil($context_compil) {
730
	if (!is_array($context_compil)) {
731
		return $context_compil;
732
	}
733
	$p = new Contexte;
734
	$p->descr = array(
735
		'sourcefile' => $context_compil[0],
736
		'nom' => $context_compil[1]
737
	);
738
	$p->id_boucle = $context_compil[2];
739
	$p->ligne = $context_compil[3];
740
	$p->lang = $context_compil[4];
741
742
	return $p;
743
}
744
745
/**
746
 * Calcule le code d'affectation d'une valeur à une commande de boucle
747
 *
748
 * Décrit le code qui complète le tableau $command qui servira entre autres
749
 * à l'itérateur. Pour un nom de commande donnée et un code PHP décrivant
750
 * ou récupérant une valeur, on retourne le code PHP qui fait l'affectation.
751
 *
752
 * L'index 0 du tableau retourné indique, lorsqu'il n'est pas vide, que l'affectation
753
 * de la variable pourra être statique (c'est à dire qu'il ne dépend
754
 * pas d'une quelconque variable PHP), et donc attribué une fois pour toutes
755
 * quelque soit le nombre d'appels de la boucle.
756
 *
757
 * @param string $nom
758
 *    Nom de la commande
759
 * @param string $val
760
 *    Code PHP décrivant la valeur à affecter
761
 * @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...
762
 *
763
 *    - index 0 : Code pour une affectation statique. Si non rempli, la propriété devra
764
 *                être ré-affectée à chaque appel de la boucle.
765
 *    - index 1 : Code de l'affectation
766
 **/
767
function calculer_dec($nom, $val) {
768
	$static = 'if (!isset($command[\'' . $nom . '\'])) ';
769
	// si une variable apparait dans le calcul de la clause
770
	// il faut la re-evaluer a chaque passage
771
	if (
772
		strpos($val, '$') !== false
773
		/*
774
		OR strpos($val, 'sql_') !== false
775
		OR (
776
			$test = str_replace(array("array(",'\"',"\'"),array("","",""),$val) // supprimer les array( et les echappements de guillemets
777
			AND strpos($test,"(")!==FALSE // si pas de parenthese ouvrante, pas de fonction, on peut sortir
778
			AND $test = preg_replace(",'[^']*',UimsS","",$test) // supprimer les chaines qui peuvent contenir des fonctions SQL qui ne genent pas
779
			AND preg_match(",\w+\s*\(,UimsS",$test,$regs) // tester la presence de fonctions restantes
780
		)*/
781
	) {
782
		$static = "";
783
	}
784
785
	return array($static, '$command[\'' . $nom . '\'] = ' . $val . ';');
786
}
787
788
/**
789
 * Calcule l'expression PHP décrivant un tableau complexe (ou une chaîne)
790
 *
791
 * Lorsqu'un tableau est transmis, reconstruit de quoi créer le tableau
792
 * en code PHP (une sorte de var_export) en appelant pour chaque valeur
793
 * cette fonction de manière récursive.
794
 *
795
 * Si le premier index (0) du tableau est "'?'", retourne un code
796
 * de test entre les 3 autres valeurs (v1 ? v2 : v3). Les valeurs
797
 * pouvant être des tableaux aussi.
798
 *
799
 * @param mixed $a
800
 *     Les données dont on veut construire un équivalent de var_export
801
 * @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...
802
 *     Expression PHP décrivant un texte ou un tableau
803
 **/
804
function calculer_dump_array($a) {
805
	if (!is_array($a)) {
806
		return $a;
807
	}
808
	$res = "";
809
	if ($a and $a[0] == "'?'") {
810
		return ("(" . calculer_dump_array($a[1]) .
811
			" ? " . calculer_dump_array($a[2]) .
812
			" : " . calculer_dump_array($a[3]) .
813
			")");
814
	} else {
815
		foreach ($a as $v) {
816
			$res .= ", " . calculer_dump_array($v);
817
		}
818
819
		return "\n\t\t\tarray(" . substr($res, 2) . ')';
820
	}
821
}
822
823
// https://code.spip.net/@calculer_dump_join
824
function calculer_dump_join($a) {
825
	$res = "";
826
	foreach ($a as $k => $v) {
827
		$res .= ", '$k' => array(" . implode(',', $v) . ")";
828
	}
829
830
	return 'array(' . substr($res, 2) . ')';
831
}
832
833
/**
834
 * Calcule l'expression PHP décrivant les informations FROM d'une boucle
835
 *
836
 * @param Boucle $boucle
837
 *     Description de la boucle
838
 * @return string
839
 *     Code PHP construisant un tableau des alias et noms des tables du FROM
840
 **/
841 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...
842
	$res = "";
843
	foreach ($boucle->from as $k => $v) {
844
		$res .= ",'$k' => '$v'";
845
	}
846
847
	return 'array(' . substr($res, 1) . ')';
848
}
849
850
/**
851
 * Calcule l'expression PHP décrivant des informations de type de jointure
852
 * pour un alias de table connu dans le FROM
853
 *
854
 * @param Boucle $boucle
855
 *     Description de la boucle
856
 * @return string
857
 *     Code PHP construisant un tableau des alias et type de jointure du FROM
858
 **/
859 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...
860
	$res = "";
861
	foreach ($boucle->from_type as $k => $v) {
862
		$res .= ",'$k' => '$v'";
863
	}
864
865
	return 'array(' . substr($res, 1) . ')';
866
}
867
868
// https://code.spip.net/@calculer_order
869
function calculer_order(&$boucle) {
870
	if (!$order = $boucle->order
871
		and !$order = $boucle->default_order
872
	) {
873
		$order = array();
874
	}
875
876
	/*if (isset($boucle->modificateur['collate'])){
877
		$col = "." . $boucle->modificateur['collate'];
878
		foreach($order as $k=>$o)
879
			if (strpos($order[$k],'COLLATE')===false)
880
				$order[$k].= $col;
881
	}*/
882
883
	return join(', ', $order);
884
}
885
886
// Production du code PHP a partir de la sequence livree par le phraseur
887
// $boucles est passe par reference pour affectation par index_pile.
888
// Retourne une expression PHP,
889
// (qui sera argument d'un Return ou la partie droite d'une affectation).
890
891
// https://code.spip.net/@calculer_liste
892
function calculer_liste($tableau, $descr, &$boucles, $id_boucle = '') {
893
	if (!$tableau) {
894
		return "''";
895
	}
896
	if (is_string($descr)) {
897
		if (isset($boucles[$descr])) {
898
			$idb = $descr;
899
			$descr = [];
900
			if (isset($boucles[$idb]->descr['id_mere_contexte'])) {
901
				$descr['id_mere'] = $boucles[$idb]->descr['id_mere_contexte'];
902
			}
903
			if (isset($boucles[$idb]->descr['sourcefile'])) {
904
				$descr['sourcefile'] = $boucles[$idb]->descr['sourcefile'];
905
			}
906
		}
907
		else {
908
			$descr = array();
909
		}
910
	}
911
	if (!isset($descr['niv'])) {
912
		$descr['niv'] = 0;
913
	}
914
	$codes = compile_cas($tableau, $descr, $boucles, $id_boucle);
915
	if ($codes === false) {
916
		return false;
917
	}
918
	$n = count($codes);
919
	if (!$n) {
920
		return "''";
921
	}
922
	$tab = str_repeat("\t", $descr['niv']);
923
	if (_request('var_mode_affiche') != 'validation') {
924
		if ($n == 1) {
925
			return $codes[0];
926
		} else {
927
			$res = '';
928
			foreach ($codes as $code) {
929
				if (!preg_match("/^'[^']*'$/", $code)
930
					or substr($res, -1, 1) !== "'"
931
				) {
932
					$res .= " .\n$tab$code";
933
				} else {
934
					$res = substr($res, 0, -1) . substr($code, 1);
935
				}
936
			}
937
938
			return '(' . substr($res, 2 + $descr['niv']) . ')';
939
		}
940
	} else {
941
		$nom = $descr['nom'] . $id_boucle . ($descr['niv'] ? $descr['niv'] : '');
942
943
		return "join('', array_map('array_shift', \$GLOBALS['debug_objets']['sequence']['$nom'] = array(" . join(" ,\n$tab",
944
			$codes) . ")))";
945
	}
946
}
947
948
949
define('_REGEXP_COND_VIDE_NONVIDE', "/^[(](.*)[?]\s*''\s*:\s*('[^']+')\s*[)]$/");
950
define('_REGEXP_COND_NONVIDE_VIDE', "/^[(](.*)[?]\s*('[^']+')\s*:\s*''\s*[)]$/");
951
define('_REGEXP_CONCAT_NON_VIDE', "/^(.*)[.]\s*'[^']+'\s*$/");
952
953
// https://code.spip.net/@compile_cas
954
function compile_cas($tableau, $descr, &$boucles, $id_boucle) {
955
956
	$codes = array();
957
	// cas de la boucle recursive
958
	if (is_array($id_boucle)) {
959
		$id_boucle = $id_boucle[0];
960
	}
961
	$type = !$id_boucle ? '' : $boucles[$id_boucle]->type_requete;
962
	$tab = str_repeat("\t", ++$descr['niv']);
963
	$mode = _request('var_mode_affiche');
964
	$err_e_c = '';
965
	// chaque commentaire introduit dans le code doit commencer
966
	// par un caractere distinguant le cas, pour exploitation par debug.
967
	foreach ($tableau as $p) {
968
969
		switch ($p->type) {
970
			// texte seul
971
			case 'texte':
972
				$code = sandbox_composer_texte($p->texte, $p);
973
				$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...
974
				$avant = '';
975
				$apres = '';
976
				$altern = "''";
977
				break;
978
979
			case 'polyglotte':
980
				$code = "";
981
				foreach ($p->traductions as $k => $v) {
982
					$code .= ",'" .
983
						str_replace(array("\\", "'"), array("\\\\", "\\'"), $k) .
984
						"' => '" .
985
						str_replace(array("\\", "'"), array("\\\\", "\\'"), $v) .
986
						"'";
987
				}
988
				$code = "choisir_traduction(array(" .
989
					substr($code, 1) .
990
					"))";
991
				$commentaire = '&';
992
				$avant = '';
993
				$apres = '';
994
				$altern = "''";
995
				break;
996
997
			// inclure
998
			case 'include':
999
				$p->descr = $descr;
1000
				$code = calculer_inclure($p, $boucles, $id_boucle);
1001
				if ($code === false) {
1002
					$err_e_c = true;
1003
					$code = "''";
1004
				} else {
1005
					$commentaire = '<INCLURE ' . addslashes(str_replace("\n", ' ', $code)) . '>';
1006
					$avant = '';
1007
					$apres = '';
1008
					$altern = "''";
1009
				}
1010
				break;
1011
1012
			// boucle
1013
			case TYPE_RECURSIF:
1014
				$nom = $p->id_boucle;
1015
				$newdescr = $descr;
1016
				$newdescr['id_mere'] = $nom;
1017
				$newdescr['niv']++;
1018
				$preaff = calculer_liste($p->preaff, $newdescr, $boucles, $id_boucle);
1019
				$avant = calculer_liste($p->avant, $newdescr, $boucles, $id_boucle);
1020
				$apres = calculer_liste($p->apres, $newdescr, $boucles, $id_boucle);
1021
				$postaff = calculer_liste($p->postaff, $newdescr, $boucles, $id_boucle);
1022
				$newdescr['niv']--;
1023
				$altern = calculer_liste($p->altern, $newdescr, $boucles, $id_boucle);
1024
				if ($preaff === false
1025
					or $avant === false
1026
					or $apres === false
1027
					or $altern === false
1028
					or $postaff === false) {
1029
					$err_e_c = true;
1030
					$code = "''";
1031
				} else {
1032
					$code = 'BOUCLE' .
1033
						str_replace("-", "_", $nom) . $descr['nom'] .
1034
						'($Cache, $Pile, $doublons, $Numrows, $SP)';
1035
					$commentaire = "?$nom";
1036
					if (!$boucles[$nom]->milieu
1037
						and $boucles[$nom]->type_requete <> TYPE_RECURSIF
1038
					) {
1039
						if ($preaff != "''") {
1040
							$code .= "\n. $preaff";
1041
						}
1042
						if ($altern != "''") {
1043
							$code .= "\n. $altern";
1044
						}
1045
						if ($postaff != "''") {
1046
							$code .= "\n. $postaff";
1047
						}
1048
						if ($avant <> "''" or $apres <> "''") {
1049
							spip_log("boucle $nom toujours vide, code superflu dans $descr[sourcefile]");
1050
						}
1051
						$avant = $apres = $altern = "''";
1052
					} else {
1053
						if ($preaff != "''") {
1054
							$avant = compile_concatene_parties_codes($preaff, $avant);
1055
							$altern = compile_concatene_parties_codes($preaff, $altern);
1056
						}
1057
						if ($postaff != "''") {
1058
							$apres = compile_concatene_parties_codes($apres, $postaff);
1059
							$altern = compile_concatene_parties_codes($altern, $postaff);
1060
						}
1061
						if ($altern != "''") {
1062
							$altern = "($altern)";
1063
						}
1064
					}
1065
				}
1066
				break;
1067
1068
			case 'idiome':
1069
				$l = array();
1070
				$code = '';
1071
				foreach ($p->arg as $k => $v) {
1072
					$_v = calculer_liste($v, $descr, $boucles, $id_boucle);
1073
					if ($k) {
1074
						$l[] = _q($k) . ' => ' . $_v;
1075
					} else {
1076
						$code = $_v;
1077
					}
1078
				}
1079
				// Si le module n'est pas fourni, l'expliciter sauf si calculé
1080
				if ($p->module) {
1081
					$m = $p->module . ':' . $p->nom_champ;
1082
				} elseif ($p->nom_champ) {
1083
					$m = MODULES_IDIOMES . ':' . $p->nom_champ;
1084
				} else {
1085
					$m = '';
1086
				}
1087
1088
				$code = (!$code ? "'$m'" :
1089
						($m ? "'$m' . $code" :
1090
							("(strpos(\$x=$code, ':') ? \$x : ('" . MODULES_IDIOMES . ":' . \$x))")))
1091
					. (!$l ? '' : (", array(" . implode(",\n", $l) . ")"));
1092
				$code = "_T($code)";
1093
				if ($p->param) {
1094
					$p->id_boucle = $id_boucle;
1095
					$p->boucles = &$boucles;
1096
					$code = compose_filtres($p, $code);
1097
				}
1098
				$commentaire = ":";
1099
				$avant = '';
1100
				$apres = '';
1101
				$altern = "''";
1102
				break;
1103
1104
			case 'champ':
1105
1106
				// cette structure pourrait etre completee des le phrase' (a faire)
1107
				$p->id_boucle = $id_boucle;
1108
				$p->boucles = &$boucles;
1109
				$p->descr = $descr;
1110
				#$p->interdire_scripts = true;
1111
				$p->type_requete = $type;
1112
1113
				$code = calculer_champ($p);
1114
				$commentaire = '#' . $p->nom_champ . $p->etoile;
1115
				$avant = calculer_liste($p->avant,
1116
					$descr, $boucles, $id_boucle);
1117
				$apres = calculer_liste($p->apres,
1118
					$descr, $boucles, $id_boucle);
1119
				$altern = "''";
1120
				// Si la valeur est destinee a une comparaison a ''
1121
				// forcer la conversion en une chaine par strval
1122
				// si ca peut etre autre chose qu'une chaine
1123
				if (($avant != "''" or $apres != "''")
1124
					and $code[0] != "'"
1125
#			AND (strpos($code,'interdire_scripts') !== 0)
1126
					and !preg_match(_REGEXP_COND_VIDE_NONVIDE, $code)
1127
					and !preg_match(_REGEXP_COND_NONVIDE_VIDE, $code)
1128
					and !preg_match(_REGEXP_CONCAT_NON_VIDE, $code)
1129
				) {
1130
					$code = "strval($code)";
1131
				}
1132
				break;
1133
1134
			default:
1135
				// Erreur de construction de l'arbre de syntaxe abstraite
1136
				$code = "''";
1137
				$p->descr = $descr;
1138
				$err_e_c = _T('zbug_erreur_compilation');
1139
				erreur_squelette($err_e_c, $p);
1140
		} // switch
1141
1142
		if ($code != "''") {
1143
			$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...
1144
			$codes[] = (($mode == 'validation') ?
1145
				"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...
1146
				: (($mode == 'code') ?
1147
					"\n// $commentaire\n$code" :
1148
					$code));
1149
		}
1150
	} // foreach
1151
1152
	return $err_e_c ? false : $codes;
1153
}
1154
1155
/**
1156
 * Concatene 2 parties de code, en simplifiant si l'une des 2 est vides
1157
 * @param $partie1
1158
 * @param $partie2
1159
 * @return string
1160
 */
1161
function compile_concatene_parties_codes($partie1, $partie2) {
1162
	if ($partie1 === "''") {
1163
		return $partie2;
1164
	}
1165
	if ($partie2 === "''") {
1166
		return $partie1;
1167
	}
1168
	return "$partie1\n. $partie2";
1169
}
1170
1171
1172
/**
1173
 * production d'une expression conditionnelle ((v=EXP) ? (p . v .s) : a)
1174
 * mais si EXP est de la forme (t ? 'C' : '') on produit (t ? (p . C . s) : a)
1175
 * de meme si EXP est de la forme (t ? '' : 'C')
1176
 * https://code.spip.net/@compile_retour
1177
 *
1178
 * @param string $code
1179
 *   le code principal, dont le resultat conditionnera le reste
1180
 * @param string $avant
1181
 *   la partie conditionnelle avant, qui est calculee apres le code, mais s'affiche avant si le code produit un resultat
1182
 * @param string $apres
1183
 *   la partie conditionnelle apres, qui est calculee apres le code, et s'affiche apres si le code produit un resultat
1184
 * @param string $altern
1185
 *   la partie alternative apres, qui est calculee apres le code, et s'affiche apres, si le code ne produit pas de resultat
1186
 * @param string $tab
1187
 *   tabulation
1188
 * @param int $n
1189
 *   compteur
1190
 * @return mixed|string
1191
 */
1192
function compile_retour($code, $avant, $apres, $altern, $tab, $n) {
1193
	if ($avant === "''") {
1194
		$avant = '';
1195
	}
1196
	if ($apres === "''") {
1197
		$apres = '';
1198
	}
1199
	if ($avant or $apres or ($altern !== "''")){
1200
		if (preg_match(_REGEXP_CONCAT_NON_VIDE, $code)){
1201
			$t = $code;
1202
			$cond = '';
1203
		} elseif (preg_match(_REGEXP_COND_VIDE_NONVIDE, $code, $r)) {
1204
			$t = $r[2];
1205
			$cond = '!' . $r[1];
1206
		} else {
1207
			if (preg_match(_REGEXP_COND_NONVIDE_VIDE, $code, $r)){
1208
				$t = $r[2];
1209
				$cond = $r[1];
1210
			} else {
1211
				$t = '$t' . $n;
1212
				$cond = "($t = $code)!==''";
1213
			}
1214
		}
1215
1216
		$res = (!$avant ? "" : "$avant . ") .
1217
			$t .
1218
			(!$apres ? "" : " . $apres");
1219
1220
		if ($res!==$t){
1221
			$res = "($res)";
1222
		}
1223
1224
		$code = (!$cond ? $res : "($cond ?\n\t$tab$res :\n\t$tab$altern)");
1225
	}
1226
1227
	return $code;
1228
1229
}
1230
1231
1232
function compile_inclure_doublons($lexemes) {
1233
	foreach ($lexemes as $v) {
1234
		if ($v->type === 'include' and $v->param) {
1235
			foreach ($v->param as $r) {
1236
				if (trim($r[0]) === 'doublons') {
1237
					return true;
1238
				}
1239
			}
1240
		}
1241
	}
1242
1243
	return false;
1244
}
1245
1246
// Prend en argument le texte d'un squelette, le nom de son fichier d'origine,
1247
// sa grammaire et un nom. Retourne False en cas d'erreur,
1248
// sinon retourne un tableau de fonctions PHP compilees a evaluer,
1249
// notamment une fonction portant ce nom et calculant une page.
1250
// Pour appeler la fonction produite, lui fournir 2 tableaux de 1 e'le'ment:
1251
// - 1er: element 'cache' => nom (du fichier ou` mettre la page)
1252
// - 2e: element 0 contenant un environnement ('id_article => $id_article, etc)
1253
// Elle retournera alors un tableau de 5 e'le'ments:
1254
// - 'texte' => page HTML, application du squelette a` l'environnement;
1255
// - 'squelette' => le nom du squelette
1256
// - 'process_ins' => 'html' ou 'php' selon la pre'sence de PHP dynamique
1257
// - 'invalideurs' =>  de'pendances de cette page, pour invalider son cache.
1258
// - 'entetes' => tableau des entetes http
1259
// En cas d'erreur, elle retournera un tableau des 2 premiers elements seulement
1260
1261
// https://code.spip.net/@public_compiler_dist
1262
function public_compiler_dist($squelette, $nom, $gram, $sourcefile, $connect = '') {
1263
	// Pre-traitement : reperer le charset du squelette, et le convertir
1264
	// Bonus : supprime le BOM
1265
	include_spip('inc/charsets');
1266
	$squelette = transcoder_page($squelette);
1267
1268
	// rendre inertes les echappements de #[](){}<>
1269
	$i = 0;
1270
	while (false !== strpos($squelette, $inerte = '-INERTE' . $i)) {
1271
		$i++;
1272
	}
1273
	$squelette = preg_replace_callback(',\\\\([#[()\]{}<>]),',
1274
		function($a) use ($inerte) {
1275
			return "$inerte-" . ord($a[1]) . '-';
1276
		},
1277
		$squelette,
1278
		-1,
1279
		$esc
1280
	);
1281
1282
	$descr = array(
1283
		'nom' => $nom,
1284
		'gram' => $gram,
1285
		'sourcefile' => $sourcefile,
1286
		'squelette' => $squelette
1287
	);
1288
1289
	// Phraser le squelette, selon sa grammaire
1290
1291
	$boucles = array();
1292
	$f = charger_fonction('phraser_' . $gram, 'public');
1293
1294
	$squelette = $f($squelette, '', $boucles, $descr);
1295
1296
	$boucles = compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect);
1297
1298
	// restituer les echappements
1299
	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...
1300
		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...
1301
			$boucles[$i]->return = preg_replace_callback(
1302
				",$inerte-(\d+)-,",
1303
				function($a) {
1304
					return chr($a[1]);
1305
				},
1306
				$boucle->return
1307
			);
1308
			$boucles[$i]->descr['squelette'] = preg_replace_callback(
1309
				",$inerte-(\d+)-,",
1310
				function($a) {
1311
					return "\\\\" . chr($a[1]);
1312
				},
1313
				$boucle->descr['squelette']
1314
			);
1315
		}
1316
	}
1317
1318
	$debug = ($boucles and defined('_VAR_MODE') and _VAR_MODE == 'debug');
1319
	if ($debug) {
1320
		include_spip('public/decompiler');
1321
		foreach ($boucles as $id => $boucle) {
1322
			if ($id) {
1323
				$decomp = "\n/* BOUCLE " .
1324
					$boucle->type_requete .
1325
					" " .
1326
					str_replace('*/', '* /', public_decompiler($boucle, $gram, 0, 'criteres')) .
1327
					($boucle->debug ? "\n *\n * " . implode("\n * ", $boucle->debug) . "\n" : '') .
1328
					" */\n";
1329
			} else {
1330
				$decomp = ("\n/*\n" .
1331
					str_replace('*/', '* /', public_decompiler($squelette, $gram))
1332
					. "\n*/");
1333
			}
1334
			$boucles[$id]->return = $decomp . $boucle->return;
1335
			$GLOBALS['debug_objets']['code'][$nom . $id] = $boucle->return;
1336
		}
1337
	}
1338
1339
	return $boucles;
1340
}
1341
1342
// Point d'entree pour arbre de syntaxe abstraite fourni en premier argument
1343
// Autres specifications comme ci-dessus
1344
1345
function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect = '') {
1346
	static $trouver_table;
1347
	spip_timer('calcul_skel');
1348
1349
	if (defined('_VAR_MODE') and _VAR_MODE == 'debug') {
1350
		$GLOBALS['debug_objets']['squelette'][$nom] = $descr['squelette'];
1351
		$GLOBALS['debug_objets']['sourcefile'][$nom] = $sourcefile;
1352
1353
		if (!isset($GLOBALS['debug_objets']['principal'])) {
1354
			$GLOBALS['debug_objets']['principal'] = $nom;
1355
		}
1356
	}
1357
	foreach ($boucles as $id => $boucle) {
1358
		$GLOBALS['debug_objets']['boucle'][$nom . $id] = $boucle;
1359
	}
1360
	$descr['documents'] = compile_inclure_doublons($squelette);
1361
1362
	// Demander la description des tables une fois pour toutes
1363
	if (!$trouver_table) {
1364
		$trouver_table = charger_fonction('trouver_table', 'base');
1365
	}
1366
1367
	// reperer si les doublons sont demandes
1368
	// pour un inclure ou une boucle document
1369
	// c'est utile a la fonction champs_traitements
1370
	foreach ($boucles as $id => $boucle) {
1371
		if (!($type = $boucle->type_requete)) {
1372
			continue;
1373
		}
1374
		if (!$descr['documents'] and (
1375
				(($type == 'documents') and $boucle->doublons) or
1376
				compile_inclure_doublons($boucle->avant) or
1377
				compile_inclure_doublons($boucle->apres) or
1378
				compile_inclure_doublons($boucle->milieu) or
1379
				compile_inclure_doublons($boucle->altern))
1380
		) {
1381
			$descr['documents'] = true;
1382
		}
1383
		if ($type != TYPE_RECURSIF) {
1384
			if (!$boucles[$id]->sql_serveur and $connect) {
1385
				$boucles[$id]->sql_serveur = $connect;
1386
			}
1387
1388
			// chercher dans les iterateurs du repertoire iterateur/
1389
			if ($g = charger_fonction(
1390
				preg_replace('/\W/', '_', $boucle->type_requete), 'iterateur', true)
1391
			) {
1392
				$boucles[$id] = $g($boucle);
1393
1394
				// sinon, en cas de requeteur d'un type predefini,
1395
				// utiliser les informations donnees par le requeteur
1396
				// cas "php:xx" et "data:xx".
1397
			} else {
1398
				if ($boucle->sql_serveur and $requeteur = charger_fonction($boucle->sql_serveur, 'requeteur', true)) {
1399
					$requeteur($boucles, $boucle, $id);
1400
1401
					// utiliser la description des champs transmis
1402
				} else {
1403
					$show = $trouver_table($type, $boucles[$id]->sql_serveur);
1404
					// si la table n'existe pas avec le connecteur par defaut,
1405
					// c'est peut etre une table qui necessite son connecteur dedie fourni
1406
					// permet une ecriture allegee (GEO) -> (geo:GEO)
1407
					if (!$show
1408
						and $show = $trouver_table($type, strtolower($type))
1409
					) {
1410
						$boucles[$id]->sql_serveur = strtolower($type);
1411
					}
1412
					if ($show) {
1413
						$boucles[$id]->show = $show;
1414
						// recopie les infos les plus importantes
1415
						$boucles[$id]->primary = isset($show['key']["PRIMARY KEY"]) ? $show['key']["PRIMARY KEY"] : '';
1416
						$boucles[$id]->id_table = $x = preg_replace(",^spip_,", "", $show['id_table']);
1417
						$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...
1418
						$boucles[$id]->iterateur = 'SQL';
1419
1420
						if (empty($boucles[$id]->descr)) {
1421
							$boucles[$id]->descr = &$descr;
1422
						}
1423
						if ((!$boucles[$id]->jointures)
1424
							and is_array($show['tables_jointures'])
1425
							and count($x = $show['tables_jointures'])
1426
						) {
1427
							$boucles[$id]->jointures = $x;
1428
						}
1429
						if ($boucles[$id]->jointures_explicites) {
1430
							$jointures = preg_split("/\s+/", $boucles[$id]->jointures_explicites);
1431
							while ($j = array_pop($jointures)) {
1432
								array_unshift($boucles[$id]->jointures, $j);
1433
							}
1434
						}
1435
					} else {
1436
						// Pas une erreur si la table est optionnelle
1437
						if ($boucles[$id]->table_optionnelle) {
1438
							$boucles[$id]->type_requete = '';
1439
						} else {
1440
							$boucles[$id]->type_requete = false;
1441
							$boucle = $boucles[$id];
1442
							$x = (!$boucle->sql_serveur ? '' :
1443
									($boucle->sql_serveur . ":")) .
1444
								$type;
1445
							$msg = array(
1446
								'zbug_table_inconnue',
1447
								array('table' => $x)
1448
							);
1449
							erreur_squelette($msg, $boucle);
1450
						}
1451
					}
1452
				}
1453
			}
1454
		}
1455
	}
1456
1457
	// Commencer par reperer les boucles appelees explicitement 
1458
	// car elles indexent les arguments de maniere derogatoire
1459
	foreach ($boucles as $id => $boucle) {
1460
		if ($boucle->type_requete == TYPE_RECURSIF and $boucle->param) {
1461
			$boucles[$id]->descr = &$descr;
1462
			$rec = &$boucles[$boucle->param[0]];
1463
			if (!$rec) {
1464
				$msg = array(
1465
					'zbug_boucle_recursive_undef',
1466
					array('nom' => $boucle->param[0])
1467
				);
1468
				erreur_squelette($msg, $boucle);
1469
				$boucles[$id]->type_requete = false;
1470
			} else {
1471
				$rec->externe = $id;
1472
				$descr['id_mere'] = $id;
1473
				$boucles[$id]->return =
1474
					calculer_liste(array($rec),
1475
						$descr,
1476
						$boucles,
1477
						$boucle->param);
1478
			}
1479
		}
1480
	}
1481
	foreach ($boucles as $id => $boucle) {
1482
		$id = strval($id); // attention au type dans index_pile
1483
		$type = $boucle->type_requete;
1484
		if ($type and $type != TYPE_RECURSIF) {
1485
			$res = '';
1486
			if ($boucle->param) {
1487
				// retourne un tableau en cas d'erreur
1488
				$res = calculer_criteres($id, $boucles);
1489
			}
1490
			$descr['id_mere'] = $id;
1491
			$boucles[$id]->return =
1492
				calculer_liste($boucle->milieu,
1493
					$descr,
1494
					$boucles,
1495
					$id);
1496
			// Si les criteres se sont mal compiles
1497
			// ne pas tenter d'assembler le code final
1498
			// (mais compiler le corps pour detection d'erreurs)
1499
			if (is_array($res)) {
1500
				$boucles[$id]->type_requete = false;
1501
			}
1502
		}
1503
	}
1504
1505
	// idem pour la racine
1506
	$descr['id_mere'] = '';
1507
	$corps = calculer_liste($squelette, $descr, $boucles);
1508
1509
1510
	// Calcul du corps de toutes les fonctions PHP,
1511
	// en particulier les requetes SQL et TOTAL_BOUCLE
1512
	// de'terminables seulement maintenant
1513
1514
	foreach ($boucles as $id => $boucle) {
1515
		$boucle = $boucles[$id] = pipeline('pre_boucle', $boucle);
1516
		if ($boucle->return === false) {
1517
			$corps = false;
1518
			continue;
1519
		}
1520
		// appeler la fonction de definition de la boucle
1521
1522
		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...
1523
			// boucle personnalisée ?
1524
			$table = strtoupper($boucle->type_requete);
1525
			$serveur = strtolower($boucle->sql_serveur);
1526
			if (
1527
				// fonction de boucle avec serveur & table
1528
				(!$serveur or
1529
					((!function_exists($f = "boucle_" . $serveur . "_" . $table))
1530
						and (!function_exists($f = $f . "_dist"))
1531
					)
1532
				)
1533
				// fonction de boucle avec table
1534
				and (!function_exists($f = "boucle_" . $table))
1535
				and (!function_exists($f = $f . "_dist"))
1536
			) {
1537
				// fonction de boucle standard 
1538
				if (!function_exists($f = 'boucle_DEFAUT')) {
1539
					$f = 'boucle_DEFAUT_dist';
1540
				}
1541
			}
1542
1543
			$req = "\n\n\tstatic \$command = array();\n\t" .
1544
				"static \$connect;\n\t" .
1545
				"\$command['connect'] = \$connect = " .
1546
				_q($boucle->sql_serveur) .
1547
				";" .
1548
				$f($id, $boucles);
1549
		} else {
1550
			$req = ("\n\treturn '';");
1551
		}
1552
1553
		$boucles[$id]->return =
1554
			"\n\nfunction BOUCLE" . strtr($id, "-", "_") . $nom .
1555
			'(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' .
1556
			$req .
1557
			"\n}\n";
1558
	}
1559
1560
	// Au final, si le corps ou un critere au moins s'est mal compile
1561
	// retourner False, sinon inserer leur decompilation
1562
	if (is_bool($corps)) {
1563
		return false;
1564
	}
1565
1566
	$principal = "\nfunction " . $nom . '($Cache, $Pile, $doublons = array(), $Numrows = array(), $SP = 0) {
1567
'
1568
		// reporter de maniere securisee les doublons inclus
1569
		. '
1570
	if (isset($Pile[0]["doublons"]) AND is_array($Pile[0]["doublons"]))
1571
		$doublons = nettoyer_env_doublons($Pile[0]["doublons"]);
1572
1573
	$connect = ' .
1574
		_q($connect) . ';
1575
	$page = ' .
1576
		// ATTENTION, le calcul de l'expression $corps affectera $Cache
1577
		// c'est pourquoi on l'affecte a la variable auxiliaire $page.
1578
		// avant de referencer $Cache
1579
		$corps . ";
1580
1581
	return analyse_resultat_skel(" . var_export($nom, true)
1582
		. ", \$Cache, \$page, " . var_export($sourcefile, true) . ");
1583
}";
1584
1585
	$secondes = spip_timer('calcul_skel');
1586
	spip_log("COMPIL ($secondes) [$sourcefile] $nom.php");
1587
	// $connect n'est pas sûr : on nettoie
1588
	$connect = preg_replace(',[^\w],', '', $connect);
1589
1590
	// Assimiler la fct principale a une boucle anonyme, pour retourner un resultat simple
1591
	$code = new Boucle;
1592
	$code->descr = $descr;
1593
	$code->return = '
1594
//
1595
// Fonction principale du squelette ' .
1596
		$sourcefile .
1597
		($connect ? " pour $connect" : '') .
1598
		(!CODE_COMMENTE ? '' : "\n// Temps de compilation total: $secondes") .
1599
		"\n//\n" .
1600
		$principal;
1601
1602
	$boucles[''] = $code;
1603
1604
	return $boucles;
1605
}
1606
1607
1608
/**
1609
 * Requeteur pour les boucles (php:nom_iterateur)
1610
 *
1611
 * Analyse si le nom d'iterateur correspond bien a une classe PHP existante
1612
 * et dans ce cas charge la boucle avec cet iterateur.
1613
 * Affichera une erreur dans le cas contraire.
1614
 *
1615
 * @param $boucles Liste des boucles
1616
 * @param $boucle  La boucle parcourue
1617
 * @param $id      L'identifiant de la boucle parcourue
1618
 *
1619
 **/
1620
function requeteur_php_dist(&$boucles, &$boucle, &$id) {
1621
	if (class_exists($boucle->type_requete)) {
1622
		$g = charger_fonction('php', 'iterateur');
1623
		$boucles[$id] = $g($boucle, $boucle->type_requete);
1624 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...
1625
		$x = $boucle->type_requete;
1626
		$boucle->type_requete = false;
1627
		$msg = array(
1628
			'zbug_iterateur_inconnu',
1629
			array('iterateur' => $x)
1630
		);
1631
		erreur_squelette($msg, $boucle);
1632
	}
1633
}
1634
1635
1636
/**
1637
 * Requeteur pour les boucles (data:type de donnee)
1638
 * note: (DATA) tout court ne passe pas par ici.
1639
 *
1640
 * Analyse si le type de donnee peut etre traite
1641
 * et dans ce cas charge la boucle avec cet iterateur.
1642
 * Affichera une erreur dans le cas contraire.
1643
 *
1644
 * @param $boucles Liste des boucles
1645
 * @param $boucle  La boucle parcourue
1646
 * @param $id      L'identifiant de la boucle parcourue
1647
 *
1648
 **/
1649
function requeteur_data_dist(&$boucles, &$boucle, &$id) {
1650
	include_spip('iterateur/data');
1651
	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...
1652
		$g = charger_fonction('data', 'iterateur');
1653
		$boucles[$id] = $g($boucle);
1654
		// from[0] stocke le type de data (rss, yql, ...)
1655
		$boucles[$id]->from[] = $boucle->type_requete;
1656
1657 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...
1658
		$x = $boucle->type_requete;
1659
		$boucle->type_requete = false;
1660
		$msg = array(
1661
			'zbug_requeteur_inconnu',
1662
			array(
1663
				'requeteur' => 'data',
1664
				'type' => $x
1665
			)
1666
		);
1667
		erreur_squelette($msg, $boucle);
1668
	}
1669
}
1670