Completed
Push — master ( e0eae3...380ee5 )
by cam
05:25
created

phraser_html.php ➔ phraser_args()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 6
nop 6
dl 0
loc 17
rs 8.8333
c 0
b 0
f 0
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
 * Phraseur d'un squelette ayant une syntaxe SPIP/HTML
15
 *
16
 * Ce fichier transforme un squelette en un tableau d'objets de classe Boucle
17
 * il est chargé par un include calculé pour permettre différentes syntaxes en entrée
18
 *
19
 * @package SPIP\Core\Compilateur\Phraseur
20
 **/
21
22
if (!defined('_ECRIRE_INC_VERSION')) {
23
	return;
24
}
25
26
/** Début de la partie principale d'une boucle */
27
define('BALISE_BOUCLE', '<BOUCLE');
28
/** Fin de la partie principale d'une boucle */
29
define('BALISE_FIN_BOUCLE', '</BOUCLE');
30
/** Début de la partie avant non optionnelle d'une boucle (toujours affichee)*/
31
define('BALISE_PREAFF_BOUCLE', '<BB');
32
/** Début de la partie optionnelle avant d'une boucle */
33
define('BALISE_PRECOND_BOUCLE', '<B');
34
/** Fin de la partie optionnelle après d'une boucle */
35
define('BALISE_POSTCOND_BOUCLE', '</B');
36
/** Fin de la partie après non optionnelle d'une boucle (toujours affichee) */
37
define('BALISE_POSTAFF_BOUCLE', '</BB');
38
/** Fin de la partie alternative après d'une boucle */
39
define('BALISE_ALT_BOUCLE', '<//B');
40
41
/** Indique un début de boucle récursive */
42
define('TYPE_RECURSIF', 'boucle');
43
/** Expression pour trouver le type de boucle (TABLE autre_table ?) */
44
define('SPEC_BOUCLE', '/\s*\(\s*([^\s?)]+)(\s*[^)?]*)([?]?)\)/');
45
/** Expression pour trouver un identifiant de boucle */
46
define('NOM_DE_BOUCLE', "[0-9]+|[-_][-_.a-zA-Z0-9]*");
47
/**
48
 * Nom d'une balise #TOTO
49
 *
50
 * Écriture alambiquée pour rester compatible avec les hexadecimaux des vieux squelettes */
51
define('NOM_DE_CHAMP', "#((" . NOM_DE_BOUCLE . "):)?(([A-F]*[G-Z_][A-Z_0-9]*)|[A-Z_]+)\b(\*{0,2})");
52
/** Balise complète [...(#TOTO) ... ] */
53
define('CHAMP_ETENDU', '/\[([^]\[]*)\(' . NOM_DE_CHAMP . '([^[)]*\)[^]\[]*)\]/S');
54
55
define('BALISE_INCLURE', '/<INCLU[DR]E[[:space:]]*(\(([^)]*)\))?/S');
56
define('BALISE_POLYGLOTTE', ',<multi>(.*)</multi>,Uims');
57
define('BALISE_IDIOMES', ',<:(([a-z0-9_]+):)?([a-z0-9_]*)({([^\|=>]*=[^\|>]*)})?((\|[^>]*)?:/?>),iS');
58
define('BALISE_IDIOMES_ARGS', '@^\s*([^= ]*)\s*=\s*((' . NOM_DE_CHAMP . '[{][^}]*})?[^,]*)\s*,?\s*@s');
59
60
/** Champ sql dans parenthèse ex: (id_article) */
61
define('SQL_ARGS', '(\([^)]*\))');
62
/** Fonction SQL sur un champ ex: SUM(visites) */
63
define('CHAMP_SQL_PLUS_FONC', '`?([A-Z_\/][A-Z_\/0-9.]*)' . SQL_ARGS . '?`?');
64
65
// https://code.spip.net/@phraser_inclure
66
function phraser_inclure($texte, $ligne, $result) {
67
68
	while (preg_match(BALISE_INCLURE, $texte, $match)) {
69
		$match = array_pad($match, 3, null);
70
		$p = strpos($texte, $match[0]);
71
		$debut = substr($texte, 0, $p);
72
		if ($p) {
73
			$result = phraser_idiomes($debut, $ligne, $result);
74
		}
75
		$ligne += substr_count($debut, "\n");
76
		$champ = new Inclure;
77
		$champ->ligne = $ligne;
78
		$ligne += substr_count($match[0], "\n");
79
		$fichier = $match[2];
80
		# assurer ici la migration .php3 => .php
81
		# et de l'ancienne syntaxe INCLURE(page.php3) devenue surperflue
82
		if ($fichier and preg_match(',^(.*[.]php)3$,', $fichier, $r)) {
83
			$fichier = $r[1];
84
		}
85
		$champ->texte = ($fichier !== 'page.php') ? $fichier : '';
86
		$texte = substr($texte, $p + strlen($match[0]));
87
		// on assimile {var=val} a une liste de un argument sans fonction
88
		$pos_apres = 0;
89
		phraser_args($texte, "/>", "", $result, $champ, $pos_apres);
90
		if (!$champ->texte or count($champ->param) > 1) {
91
			if (!function_exists('normaliser_inclure')) {
92
				include_spip('public/normaliser');
93
			}
94
			normaliser_inclure($champ);
95
		}
96
		$texte = substr($texte, strpos($texte, '>', $pos_apres) + 1);
97
		$texte = preg_replace(',^</INCLU[DR]E>,', '', $texte);
98
		$result[] = $champ;
99
	}
100
101
	return (($texte === "") ? $result : phraser_idiomes($texte, $ligne, $result));
102
}
103
104
// https://code.spip.net/@phraser_polyglotte
105
function phraser_polyglotte($texte, $ligne, $result) {
106
107
	if (preg_match_all(BALISE_POLYGLOTTE, $texte, $m, PREG_SET_ORDER)) {
108
		foreach ($m as $match) {
109
			$p = strpos($texte, $match[0]);
110
			$debut = substr($texte, 0, $p);
111
			if ($p) {
112
				$champ = new Texte;
113
				$champ->texte = $debut;
114
				$champ->ligne = $ligne;
115
				$result[] = $champ;
116
				$ligne += substr_count($champ->texte, "\n");
117
			}
118
119
			$champ = new Polyglotte;
120
			$champ->ligne = $ligne;
121
			$ligne += substr_count($match[0], "\n");
122
			$lang = '';
123
			$bloc = $match[1];
124
			$texte = substr($texte, $p + strlen($match[0]));
125
			while (preg_match("/^[[:space:]]*([^[{]*)[[:space:]]*[[{]([a-z_]+)[]}](.*)$/si", $bloc, $regs)) {
126
				$trad = $regs[1];
127
				if ($trad or $lang) {
128
					$champ->traductions[$lang] = $trad;
129
				}
130
				$lang = $regs[2];
131
				$bloc = $regs[3];
132
			}
133
			$champ->traductions[$lang] = $bloc;
134
			$result[] = $champ;
135
		}
136
	}
137
	if ($texte !== "") {
138
		$champ = new Texte;
139
		$champ->texte = $texte;
140
		$champ->ligne = $ligne;
141
		$result[] = $champ;
142
	}
143
144
	return $result;
145
}
146
147
148
/**
149
 * Repérer les balises de traduction (idiomes)
150
 *
151
 * Phrase les idiomes tel que
152
 * - `<:chaine:>`
153
 * - `<:module:chaine:>`
154
 * - `<:module:chaine{arg1=texte1,arg2=#BALISE}|filtre1{texte2,#BALISE}|filtre2:>`
155
 *
156
 * @note
157
 *    `chaine` peut etre vide si `=texte1` est present et `arg1` est vide
158
 *    sinon ce n'est pas un idiome
159
 *
160
 * @param string $texte
161
 * @param int $ligne
162
 * @param array $result
163
 * @return array
164
 **/
165
function phraser_idiomes($texte, $ligne, $result) {
166
	while (preg_match(BALISE_IDIOMES, $texte, $match)) {
167
		$match = array_pad($match, 8, null);
168
		$p = strpos($texte, $match[0]);
169
		$ko = (!$match[3] && ($match[5][0] !== '='));
170
		$debut = substr($texte, 0, $p + ($ko ? strlen($match[0]) : 0));
171
		if ($debut) {
172
			$result = phraser_champs($debut, $ligne, $result);
173
		}
174
		$texte = substr($texte, $p + strlen($match[0]));
175
		$ligne += substr_count($debut, "\n");
176
		if ($ko) {
177
			continue;
178
		} // faux idiome
179
		$champ = new Idiome;
180
		$champ->ligne = $ligne;
181
		$ligne += substr_count($match[0], "\n");
182
		// Stocker les arguments de la balise de traduction
183
		$args = array();
184
		$largs = $match[5];
185
		while (preg_match(BALISE_IDIOMES_ARGS, $largs, $r)) {
186
			$args[$r[1]] = phraser_champs($r[2], 0, array());
187
			$largs = substr($largs, strlen($r[0]));
188
		}
189
		$champ->arg = $args;
190
		$champ->nom_champ = strtolower($match[3]);
191
		$champ->module = $match[2];
192
		// pas d'imbrication pour les filtres sur langue
193
		$pos_apres = 0;
194
		phraser_args($match[7], ":", '', array(), $champ, $pos_apres);
195
		$champ->apres = substr($match[7], $pos_apres);
196
		$result[] = $champ;
197
	}
198
	if ($texte !== "") {
199
		$result = phraser_champs($texte, $ligne, $result);
200
	}
201
202
	return $result;
203
}
204
205
/**
206
 * Repère et phrase les balises SPIP tel que `#NOM` dans un texte
207
 *
208
 * Phrase également ses arguments si la balise en a (`#NOM{arg, ...}`)
209
 *
210
 * @uses phraser_polyglotte()
211
 * @uses phraser_args()
212
 * @uses phraser_vieux()
213
 *
214
 * @param string $texte
215
 * @param int $ligne
216
 * @param array $result
217
 * @return array
218
 **/
219
function phraser_champs($texte, $ligne, $result) {
220
	while (preg_match("/" . NOM_DE_CHAMP . "/S", $texte, $match)) {
221
		$p = strpos($texte, $match[0]);
222
		// texte après la balise
223
		$suite = substr($texte, $p + strlen($match[0]));
224
225
		$debut = substr($texte, 0, $p);
226
		if ($p) {
227
			$result = phraser_polyglotte($debut, $ligne, $result);
228
		}
229
		$ligne += substr_count($debut, "\n");
230
		$champ = new Champ;
231
		$champ->ligne = $ligne;
232
		$ligne += substr_count($match[0], "\n");
233
		$champ->nom_boucle = $match[2];
234
		$champ->nom_champ = $match[3];
235
		$champ->etoile = $match[5];
236
237
		if ($suite and $suite[0] == '{') {
238
			phraser_arg($suite, '', array(), $champ);
239
			// ce ltrim est une ereur de conception
240
			// mais on le conserve par souci de compatibilite
241
			$texte = ltrim($suite);
242
			// Il faudrait le normaliser dans l'arbre de syntaxe abstraite
243
			// pour faire sauter ce cas particulier a la decompilation.
244
			/* Ce qui suit est malheureusement incomplet pour cela:
245
			if ($n = (strlen($suite) - strlen($texte))) {
246
				$champ->apres = array(new Texte);
247
				$champ->apres[0]->texte = substr($suite,0,$n);
248
			}
249
			*/
250
		} else {
251
			$texte = $suite;
252
		}
253
		phraser_vieux($champ);
254
		$result[] = $champ;
255
	}
256
	if ($texte !== "") {
257
		$result = phraser_polyglotte($texte, $ligne, $result);
258
	}
259
260
	return $result;
261
}
262
263
// Gestion des imbrications:
264
// on cherche les [..] les plus internes et on les remplace par une chaine
265
// %###N@ ou N indexe un tableau comportant le resultat de leur analyse
266
// on recommence tant qu'il y a des [...] en substituant a l'appel suivant
267
268
// https://code.spip.net/@phraser_champs_etendus
269
function phraser_champs_etendus($texte, $ligne, $result) {
270
	if ($texte === "") {
271
		return $result;
272
	}
273
	$sep = '##';
274
	while (strpos($texte, $sep) !== false) {
275
		$sep .= '#';
276
	}
277
278
	return array_merge($result, phraser_champs_interieurs($texte, $ligne, $sep, array()));
279
}
280
281
/**
282
 * Analyse les filtres d'un champ etendu et affecte le resultat
283
 * renvoie la liste des lexemes d'origine augmentee
284
 * de ceux trouves dans les arguments des filtres (rare)
285
 * sert aussi aux arguments des includes et aux criteres de boucles
286
 * Tres chevelu
287
 *
288
 * https://code.spip.net/@phraser_args
289
 *
290
 * @param string $texte
291
 * @param string $fin
292
 * @param string $sep
293
 * @param $result
294
 * @param $pointeur_champ
295
 * @param int $pos_debut
296
 * @return array
297
 */
298
function phraser_args($texte, $fin, $sep, $result, &$pointeur_champ, &$pos_debut) {
299
	$length = strlen($texte);
300
	while ($pos_debut < $length and trim($texte[$pos_debut]) === '') {
301
		$pos_debut++;
302
	}
303
	while (($pos_debut < $length) && strpos($fin, $texte[$pos_debut]) === false) {
304
		// phraser_arg modifie directement le $texte, on fait donc avec ici en passant par une sous chaine
305
		$st = substr($texte, $pos_debut);
306
		$result = phraser_arg($st, $sep, $result, $pointeur_champ);
307
		$pos_debut = $length - strlen($st);
308
		while ($pos_debut < $length and trim($texte[$pos_debut]) === '') {
309
			$pos_debut++;
310
		}
311
	}
312
313
	return $result;
314
}
315
316
// https://code.spip.net/@phraser_arg
317
function phraser_arg(&$texte, $sep, $result, &$pointeur_champ) {
318
	preg_match(",^(\|?[^}{)|]*)(.*)$,ms", $texte, $match);
319
	$suite = ltrim($match[2]);
320
	$fonc = trim($match[1]);
321
	if ($fonc && $fonc[0] == "|") {
322
		$fonc = ltrim(substr($fonc, 1));
323
	}
324
	$res = array($fonc);
325
	$err_f = '';
326
	// cas du filtre sans argument ou du critere /
327
	if (($suite && ($suite[0] != '{')) || ($fonc && $fonc[0] == '/')) {
328
		// si pas d'argument, alors il faut une fonction ou un double |
329
		if (!$match[1]) {
330
			$err_f = array('zbug_erreur_filtre', array('filtre' => $texte));
331
			erreur_squelette($err_f, $pointeur_champ);
332
			$texte = '';
333
		} else {
334
			$texte = $suite;
335
		}
336 View Code Duplication
		if ($err_f) {
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...
337
			$pointeur_champ->param = false;
338
		} elseif ($fonc !== '') {
339
			$pointeur_champ->param[] = $res;
340
		}
341
		// pour les balises avec faux filtres qui boudent ce dur larbeur
342
		$pointeur_champ->fonctions[] = array($fonc, '');
343
344
		return $result;
345
	}
346
	$args = ltrim(substr($suite, 1)); // virer le '(' initial
347
	$collecte = array();
348
	while ($args && $args[0] != '}') {
349
		if ($args[0] == '"') {
350
			preg_match('/^(")([^"]*)(")(.*)$/ms', $args, $regs);
351
		} elseif ($args[0] == "'") {
352
			preg_match("/^(')([^']*)(')(.*)$/ms", $args, $regs);
353
		} else {
354
			preg_match("/^([[:space:]]*)([^,([{}]*([(\[{][^])}]*[])}])?[^,}]*)([,}].*)$/ms", $args, $regs);
355
			if (!strlen($regs[2])) {
356
				$err_f = array('zbug_erreur_filtre', array('filtre' => $args));
357
				erreur_squelette($err_f, $pointeur_champ);
358
				$champ = new Texte;
359
				$champ->apres = $champ->avant = $args = "";
360
				break;
361
			}
362
		}
363
		$arg = $regs[2];
364
		if (trim($regs[1])) {
365
			$champ = new Texte;
366
			$champ->texte = $arg;
367
			$champ->apres = $champ->avant = $regs[1];
368
			$result[] = $champ;
369
			$collecte[] = $champ;
370
			$args = ltrim($regs[count($regs) - 1]);
371
		} else {
372
			if (!preg_match("/" . NOM_DE_CHAMP . "([{|])/", $arg, $r)) {
373
				// 0 est un aveu d'impuissance. A completer
374
				$arg = phraser_champs_exterieurs($arg, 0, $sep, $result);
375
376
				$args = ltrim($regs[count($regs) - 1]);
377
				$collecte = array_merge($collecte, $arg);
378
				$result = array_merge($result, $arg);
379
			} else {
380
				$n = strpos($args, $r[0]);
381
				$pred = substr($args, 0, $n);
382
				$par = ',}';
383
				if (preg_match('/^(.*)\($/', $pred, $m)) {
384
					$pred = $m[1];
385
					$par = ')';
386
				}
387
				if ($pred) {
388
					$champ = new Texte;
389
					$champ->texte = $pred;
390
					$champ->apres = $champ->avant = "";
391
					$result[] = $champ;
392
					$collecte[] = $champ;
393
				}
394
				$rec = substr($args, $n + strlen($r[0]) - 1);
395
				$champ = new Champ;
396
				$champ->nom_boucle = $r[2];
397
				$champ->nom_champ = $r[3];
398
				$champ->etoile = $r[5];
399
				$next = $r[6];
400
				while ($next == '{') {
401
					phraser_arg($rec, $sep, array(), $champ);
402
					$args = ltrim($rec);
403
					$next = isset($args[0]) ? $args[0] : '';
404
				}
405
				while ($next == '|') {
406
					$pos_apres = 0;
407
					phraser_args($rec, $par, $sep, array(), $champ, $pos_apres);
408
					$args = substr($rec, $pos_apres);
409
					$next = isset($args[0]) ? $args[0] : '';
410
				}
411
				// Si erreur de syntaxe dans un sous-argument, propager.
412
				if ($champ->param === false) {
413
					$err_f = true;
414
				} else {
415
					phraser_vieux($champ);
416
				}
417
				if ($par == ')') {
418
					$args = substr($args, 1);
419
				}
420
				$collecte[] = $champ;
421
				$result[] = $champ;
422
			}
423
		}
424
		if (isset($args[0]) and $args[0] == ',') {
425
			$args = ltrim(substr($args, 1));
426
			if ($collecte) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collecte of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
427
				$res[] = $collecte;
428
				$collecte = array();
429
			}
430
		}
431
	}
432
	if ($collecte) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $collecte of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
433
		$res[] = $collecte;
434
		$collecte = array();
0 ignored issues
show
Unused Code introduced by
$collecte 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...
435
	}
436
	$texte = substr($args, 1);
437
	$source = substr($suite, 0, strlen($suite) - strlen($texte));
438
	// propager les erreurs, et ignorer les param vides
439
	if ($pointeur_champ->param !== false) {
440 View Code Duplication
		if ($err_f) {
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...
441
			$pointeur_champ->param = false;
442
		} elseif ($fonc !== '' || count($res) > 1) {
443
			$pointeur_champ->param[] = $res;
444
		}
445
	}
446
	// pour les balises avec faux filtres qui boudent ce dur larbeur
447
	$pointeur_champ->fonctions[] = array($fonc, $source);
448
449
	return $result;
450
}
451
452
453
// https://code.spip.net/@phraser_champs_exterieurs
454
function phraser_champs_exterieurs($texte, $ligne, $sep, $nested) {
455
	$res = array();
456
	while (($p = strpos($texte, "%$sep")) !== false) {
457
		if (!preg_match(',^%' . preg_quote($sep) . '([0-9]+)@,', substr($texte, $p), $m)) {
458
			break;
459
		}
460
		$debut = substr($texte, 0, $p);
461
		$texte = substr($texte, $p + strlen($m[0]));
462
		if ($p) {
463
			$res = phraser_inclure($debut, $ligne, $res);
464
		}
465
		$ligne += substr_count($debut, "\n");
466
		$res[] = $nested[$m[1]];
467
	}
468
469
	return (($texte === '') ? $res : phraser_inclure($texte, $ligne, $res));
470
}
471
472
// https://code.spip.net/@phraser_champs_interieurs
473
function phraser_champs_interieurs($texte, $ligne, $sep, $result) {
474
	$i = 0; // en fait count($result)
475
	$x = "";
476
477
	while (true) {
478
		$j = $i;
479
		$n = $ligne;
480
		while (preg_match(CHAMP_ETENDU, $texte, $match)) {
481
			$p = strpos($texte, $match[0]);
482
			$debut = substr($texte, 0, $p);
483
			if ($p) {
484
				$result[$i] = $debut;
485
				$i++;
486
			}
487
			$nom = $match[4];
488
			$champ = new Champ;
489
			// ca ne marche pas encore en cas de champ imbrique
490
			$champ->ligne = $x ? 0 : ($n + substr_count($debut, "\n"));
491
			$champ->nom_boucle = $match[3];
492
			$champ->nom_champ = $nom;
493
			$champ->etoile = $match[6];
494
			// phraser_args indiquera ou commence apres
495
			$pos_apres = 0;
496
			$result = phraser_args($match[7], ")", $sep, $result, $champ, $pos_apres);
497
			phraser_vieux($champ);
498
			$champ->avant =	phraser_champs_exterieurs($match[1], $n, $sep, $result);
499
			$debut = substr($match[7], $pos_apres + 1);
500
			if (!empty($debut)) {
501
				$n += substr_count(substr($texte, 0, strpos($texte, $debut)), "\n");
502
			}
503
			$champ->apres = phraser_champs_exterieurs($debut, $n, $sep, $result);
504
505
			// reinjecter la boucle si c'en est une
506
			phraser_boucle_placeholder($champ);
507
508
			$result[$i] = $champ;
509
			$i++;
510
			$texte = substr($texte, $p + strlen($match[0]));
511
		}
512
		if ($texte !== "") {
513
			$result[$i] = $texte;
514
			$i++;
515
		}
516
		$x = '';
517
518
		while ($j < $i) {
519
			$z = $result[$j];
520
			// j'aurais besoin de connaitre le nombre de lignes...
521
			if (is_object($z)) {
522
				$x .= "%$sep$j@";
523
			} else {
524
				$x .= $z;
525
			}
526
			$j++;
527
		}
528
		if (preg_match(CHAMP_ETENDU, $x)) {
529
			$texte = $x;
530
		} else {
531
			return phraser_champs_exterieurs($x, $ligne, $sep, $result);
532
		}
533
	}
534
}
535
536
function phraser_vieux(&$champ) {
537
	$nom = $champ->nom_champ;
538
	if ($nom == 'EMBED_DOCUMENT') {
539
		if (!function_exists('phraser_vieux_emb')) {
540
			include_spip('public/normaliser');
541
		}
542
		phraser_vieux_emb($champ);
543
	} elseif ($nom == 'EXPOSER') {
544
		if (!function_exists('phraser_vieux_exposer')) {
545
			include_spip('public/normaliser');
546
		}
547
		phraser_vieux_exposer($champ);
548
	} elseif ($champ->param) {
549
		if ($nom == 'FORMULAIRE_RECHERCHE') {
550
			if (!function_exists('phraser_vieux_recherche')) {
551
				include_spip('public/normaliser');
552
			}
553
			phraser_vieux_recherche($champ);
554
		} elseif (preg_match(",^LOGO_[A-Z]+,", $nom)) {
555
			if (!function_exists('phraser_vieux_logos')) {
556
				include_spip('public/normaliser');
557
			}
558
			phraser_vieux_logos($champ);
559
		} elseif ($nom == 'MODELE') {
560
			if (!function_exists('phraser_vieux_modele')) {
561
				include_spip('public/normaliser');
562
			}
563
			phraser_vieux_modele($champ);
564
		} elseif ($nom == 'INCLURE' or $nom == 'INCLUDE') {
565
			if (!function_exists('phraser_vieux_inclu')) {
566
				include_spip('public/normaliser');
567
			}
568
			phraser_vieux_inclu($champ);
569
		}
570
	}
571
}
572
573
574
/**
575
 * Analyse les critères de boucle
576
 *
577
 * Chaque paramètre de la boucle (tel que {id_article>3}) est analysé
578
 * pour construire un critère (objet Critere) de boucle.
579
 *
580
 * Un critère a une description plus fine que le paramètre original
581
 * car on en extrait certaines informations tel que la négation et l'opérateur
582
 * utilisé s'il y a.
583
 *
584
 * La fonction en profite pour déclarer des modificateurs de boucles
585
 * en présence de certains critères (tout, plat) ou initialiser des
586
 * variables de compilation (doublons)...
587
 *
588
 * @param array $params
589
 *     Tableau de description des paramètres passés à la boucle.
590
 *     Chaque paramètre deviendra un critère
591
 * @param Boucle $result
592
 *     Description de la boucle
593
 *     Elle sera complété de la liste de ses critères
594
 * @return void
595
 **/
596
function phraser_criteres($params, &$result) {
597
598
	$err_ci = ''; // indiquera s'il y a eu une erreur
599
	$args = array();
600
	$type = $result->type_requete;
601
	$doublons = array();
602
	foreach ($params as $v) {
603
		$var = $v[1][0];
604
		$param = ($var->type != 'texte') ? "" : $var->texte;
605
		if ((count($v) > 2) && (!preg_match(",[^A-Za-z]IN[^A-Za-z],i", $param))) {
606
			// plus d'un argument et pas le critere IN:
607
			// detecter comme on peut si c'est le critere implicite LIMIT debut, fin
608
			if ($var->type != 'texte'
609
				or preg_match("/^(n|n-|(n-)?\d+)$/S", $param)
610
			) {
611
				$op = ',';
612
				$not = "";
613
				$cond = false;
614
			} else {
615
				// Le debut du premier argument est l'operateur
616
				preg_match("/^([!]?)([a-zA-Z][a-zA-Z0-9_]*)[[:space:]]*(\??)[[:space:]]*(.*)$/ms", $param, $m);
617
				$op = $m[2];
618
				$not = $m[1];
619
				$cond = $m[3];
620
				// virer le premier argument,
621
				// et mettre son reliquat eventuel
622
				// Recopier pour ne pas alterer le texte source
623
				// utile au debusqueur
624
				if ($m[4]) {
625
					// une maniere tres sale de supprimer les "' autour de {critere "xxx","yyy"}
626
					if (preg_match(',^(["\'])(.*)\1$,', $m[4])) {
627
						$c = null;
628
						eval('$c = ' . $m[4] . ';');
629
						if (isset($c)) {
630
							$m[4] = $c;
631
						}
632
					}
633
					$texte = new Texte;
634
					$texte->texte = $m[4];
635
					$v[1][0] = $texte;
636
				} else {
637
					array_shift($v[1]);
638
				}
639
			}
640
			array_shift($v); // $v[O] est vide
641
			$crit = new Critere;
642
			$crit->op = $op;
643
			$crit->not = $not;
644
			$crit->cond = $cond;
0 ignored issues
show
Documentation Bug introduced by
It seems like $cond can also be of type string. However, the property $cond is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
645
			$crit->exclus = "";
646
			$crit->param = $v;
647
			$args[] = $crit;
648
		} else {
649
			if ($var->type != 'texte') {
650
				// cas 1 seul arg ne commencant pas par du texte brut: 
651
				// erreur ou critere infixe "/"
652
				if (($v[1][1]->type != 'texte') || (trim($v[1][1]->texte) != '/')) {
653
					$err_ci = array(
654
						'zbug_critere_inconnu',
655
						array('critere' => $var->nom_champ)
656
					);
657
					erreur_squelette($err_ci, $result);
658
				} else {
659
					$crit = new Critere;
660
					$crit->op = '/';
661
					$crit->not = "";
662
					$crit->exclus = "";
663
					$crit->param = array(array($v[1][0]), array($v[1][2]));
664
					$args[] = $crit;
665
				}
666
			} else {
667
				// traiter qq lexemes particuliers pour faciliter la suite
668
				// les separateurs
669
				if ($var->apres) {
670
					$result->separateur[] = $param;
671
				} elseif (($param == 'tout') or ($param == 'tous')) {
672
					$result->modificateur['tout'] = true;
673
				} elseif ($param == 'plat') {
674
					$result->modificateur['plat'] = true;
675
				}
676
677
				// Boucle hierarchie, analyser le critere id_rubrique
678
				// et les autres critères {id_x} pour forcer {tout} sur
679
				// ceux-ci pour avoir la rubrique mere...
680
				// Les autres critères de la boucle hierarchie doivent être
681
				// traités normalement.
682
				elseif (strcasecmp($type, 'hierarchie') == 0
683
					and !preg_match(",^id_rubrique\b,", $param)
684
					and preg_match(",^id_\w+\s*$,", $param)
685
				) {
686
					$result->modificateur['tout'] = true;
687
				} elseif (strcasecmp($type, 'hierarchie') == 0 and $param == "id_rubrique") {
688
					// rien a faire sur {id_rubrique} tout seul
689
				} else {
690
					// pas d'emplacement statique, faut un dynamique
691
					// mais il y a 2 cas qui ont les 2 !
692
					if (($param == 'unique') || (preg_match(',^!?doublons *,', $param))) {
693
						// cette variable sera inseree dans le code
694
						// et son nom sert d'indicateur des maintenant
695
						$result->doublons = '$doublons_index';
696
						if ($param == 'unique') {
697
							$param = 'doublons';
698
						}
699
					} elseif ($param == 'recherche') {
700
						// meme chose (a cause de #nom_de_boucle:URL_*)
701
						$result->hash = ' ';
702
					}
703
704
					if (preg_match(',^ *([0-9-]+) *(/) *(.+) *$,', $param, $m)) {
705
						$crit = phraser_critere_infixe($m[1], $m[3], $v, '/', '', '');
706
					} elseif (preg_match(',^([!]?)(' . CHAMP_SQL_PLUS_FONC .
707
						')[[:space:]]*(\??)(!?)(<=?|>=?|==?|\b(?:IN|LIKE)\b)(.*)$,is', $param, $m)) {
708
						$a2 = trim($m[8]);
709
						if ($a2 and ($a2[0] == "'" or $a2[0] == '"') and ($a2[0] == substr($a2, -1))) {
710
							$a2 = substr($a2, 1, -1);
711
						}
712
						$crit = phraser_critere_infixe($m[2], $a2, $v,
713
							(($m[2] == 'lang_select') ? $m[2] : $m[7]),
714
							$m[6], $m[5]);
715
						$crit->exclus = $m[1];
716
					} elseif (preg_match("/^([!]?)\s*(" .
717
						CHAMP_SQL_PLUS_FONC .
718
						")\s*(\??)(.*)$/is", $param, $m)) {
719
						// contient aussi les comparaisons implicites !
720
						// Comme ci-dessus: 
721
						// le premier arg contient l'operateur
722
						array_shift($v);
723
						if ($m[6]) {
724
							$v[0][0] = new Texte;
725
							$v[0][0]->texte = $m[6];
726
						} else {
727
							array_shift($v[0]);
728
							if (!$v[0]) {
729
								array_shift($v);
730
							}
731
						}
732
						$crit = new Critere;
733
						$crit->op = $m[2];
734
						$crit->param = $v;
735
						$crit->not = $m[1];
736
						$crit->cond = $m[5];
0 ignored issues
show
Documentation Bug introduced by
The property $cond was declared of type boolean, but $m[5] is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
737
					} else {
738
						$err_ci = array(
739
							'zbug_critere_inconnu',
740
							array('critere' => $param)
741
						);
742
						erreur_squelette($err_ci, $result);
743
					}
744
745
					if ((!preg_match(',^!?doublons *,', $param)) || $crit->not) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $crit->not of type null|string is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
746
						$args[] = $crit;
0 ignored issues
show
Bug introduced by
The variable $crit 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...
747
					} else {
748
						$doublons[] = $crit;
749
					}
750
				}
751
			}
752
		}
753
	}
754
755
	// les doublons non nies doivent etre le dernier critere
756
	// pour que la variable $doublon_index ait la bonne valeur
757
	// cf critere_doublon
758
	if ($doublons) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $doublons of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
759
		$args = array_merge($args, $doublons);
760
	}
761
762
	// Si erreur, laisser la chaine dans ce champ pour le HTTP 503
763
	if (!$err_ci) {
764
		$result->criteres = $args;
0 ignored issues
show
Documentation Bug introduced by
It seems like $args of type array is incompatible with the declared type array<integer,object<Critere>> of property $criteres.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
765
	}
766
}
767
768
// https://code.spip.net/@phraser_critere_infixe
769
function phraser_critere_infixe($arg1, $arg2, $args, $op, $not, $cond) {
770
	$args[0] = new Texte;
771
	$args[0]->texte = $arg1;
772
	$args[0] = array($args[0]);
773
	$args[1][0] = new Texte;
774
	$args[1][0]->texte = $arg2;
775
	$crit = new Critere;
776
	$crit->op = $op;
777
	$crit->not = $not;
778
	$crit->cond = $cond;
779
	$crit->param = $args;
780
781
	return $crit;
782
}
783
784
/**
785
 * Compter le nombre de lignes dans une partie texte
786
 * @param $texte
787
 * @param int $debut
788
 * @param null $fin
789
 * @return int
790
 */
791
function public_compte_ligne($texte, $debut = 0, $fin = null) {
792
	if (is_null($fin)) {
793
		return substr_count($texte, "\n", $debut);
794
	}
795
	else {
796
		return substr_count($texte, "\n", $debut, $fin - $debut);
797
	}
798
}
799
800
801
/**
802
 * Trouver la boucle qui commence en premier dans un texte
803
 * On repere les boucles via <BOUCLE_xxx(
804
 * et ensuite on regarde son vrai debut soit <B_xxx> soit <BB_xxx>
805
 *
806
 * @param $texte
807
 * @param $id_parent
808
 * @param $descr
809
 * @param int $pos_debut_texte
810
 * @return array|null
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use null|array<string,integer|double|string|false>.

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...
811
 */
812
function public_trouver_premiere_boucle($texte, $id_parent, $descr, $pos_debut_texte = 0) {
813
	$premiere_boucle = null;
814
815
	$current_pos = $pos_debut_texte;
816
	while (($pos_boucle = strpos($texte, BALISE_BOUCLE, $current_pos)) !== false) {
817
		$current_pos = $pos_boucle + 1;
818
		$pos_parent = strpos($texte,'(', $pos_boucle);
819
		if ($pos_parent === false
820
		  or !$id_boucle = trim(substr($texte,$pos_boucle + strlen(BALISE_BOUCLE), $pos_parent - $pos_boucle - strlen(BALISE_BOUCLE)))
821
			or !(is_numeric($id_boucle) or strpos($id_boucle, '_') === 0)) {
822
823
			$result = new Boucle;
824
			$result->id_parent = $id_parent;
825
			$result->descr = $descr;
826
827
			// un id_boucle pour l'affichage de l'erreur
828
			if (!$id_boucle) {
0 ignored issues
show
Bug introduced by
The variable $id_boucle 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...
829
				$id_boucle = substr($texte,$pos_boucle + strlen(BALISE_BOUCLE), 15);
830
			}
831
			$result->id_boucle = $id_boucle;
0 ignored issues
show
Documentation Bug introduced by
It seems like $id_boucle can also be of type integer or double. However, the property $id_boucle is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
832
			$err_b = array('zbug_erreur_boucle_syntaxe', array('id' => $id_boucle));
833
			erreur_squelette($err_b, $result);
834
835
			continue;
836
		}
837
		else {
838
			$boucle = [
839
				'id_boucle' => $id_boucle,
840
				'debut_boucle' => $pos_boucle,
841
				'pos_boucle' => $pos_boucle,
842
				'pos_parent' => $pos_parent,
843
				'pos_precond' => false,
844
				'pos_precond_inside' => false,
845
				'pos_preaff' => false,
846
				'pos_preaff_inside' => false,
847
			];
848
849
			// trouver sa position de depart reelle : au <B_ ou au <BB_
850
			$precond_boucle = BALISE_PRECOND_BOUCLE . $id_boucle . '>';
851
			$pos_precond = strpos($texte, $precond_boucle, $pos_debut_texte);
852
			if ($pos_precond !== false and $pos_precond < $boucle['debut_boucle']) {
853
				$boucle['debut_boucle'] = $pos_precond;
854
				$boucle['pos_precond'] = $pos_precond;
855
				$boucle['pos_precond_inside'] = $pos_precond + strlen($precond_boucle);
856
			}
857
858
			$preaff_boucle = BALISE_PREAFF_BOUCLE . $id_boucle . '>';
859
			$pos_preaff = strpos($texte, $preaff_boucle, $pos_debut_texte);
860
			if ($pos_preaff !== false and $pos_preaff < $boucle['debut_boucle']) {
861
				$boucle['debut_boucle'] = $pos_preaff;
862
				$boucle['pos_preaff'] = $pos_preaff;
863
				$boucle['pos_preaff_inside'] = $pos_preaff + strlen($preaff_boucle);
864
			}
865
866
			if (is_null($premiere_boucle) or $premiere_boucle['debut_boucle'] > $boucle['debut_boucle']) {
867
				$premiere_boucle = $boucle;
868
			}
869
		}
870
	}
871
872
	return $premiere_boucle;
873
}
874
875
/**
876
 * @param object|string $champ
877
 * @param null|string $boucle_placeholder
878
 * @param null|object $boucle
879
 */
880
function phraser_boucle_placeholder(&$champ, $boucle_placeholder=null, $boucle = null) {
881
	static $boucles_connues = array();
882
	// si c'est un appel pour memoriser une boucle, memorisons la
883
	if (is_string($champ) and !empty($boucle_placeholder) and !empty($boucle)) {
884
		$boucles_connues[$boucle_placeholder][$champ] = &$boucle;
885
	}
886
	else {
887
		if (!empty($champ->nom_champ) and !empty($boucles_connues[$champ->nom_champ])) {
888
			$placeholder = $champ->nom_champ;
889
			$id = reset($champ->param[0][1]);
890
			$id = $id->texte;
891
			if (!empty($boucles_connues[$placeholder][$id])) {
892
				$champ = $boucles_connues[$placeholder][$id];
893
			}
894
		}
895
	}
896
}
897
898
899
/**
900
 * Generer une balise placeholder qui prend la place de la boucle pour continuer le parsing des balises
901
 * @param string $id_boucle
902
 * @param $boucle
903
 * @param string $boucle_placeholder
904
 * @param int $nb_lignes
905
 * @return string
906
 */
907
function public_generer_boucle_placeholder($id_boucle, &$boucle, $boucle_placeholder, $nb_lignes) {
908
	$placeholder = "[(#{$boucle_placeholder}{" . $id_boucle . '})' . str_pad("", $nb_lignes, "\n") . "]";
909
	//memoriser la boucle a reinjecter
910
	$id_boucle = "$id_boucle";
911
	phraser_boucle_placeholder($id_boucle, $boucle_placeholder, $boucle);
912
	return $placeholder;
913
}
914
915
function public_phraser_html_dist($texte, $id_parent, &$boucles, $descr, $ligne_debut_texte = 1, $boucle_placeholder = null) {
916
917
	$all_res = array();
918
	// definir un placholder pour les boucles dont on est sur d'avoir aucune occurence dans le squelette
919
	if (is_null($boucle_placeholder)) {
920
		do {
921
			$boucle_placeholder = "BOUCLE_PLACEHOLDER_" . strtoupper(md5(uniqid()));
922
		} while (strpos($texte, $boucle_placeholder) !== false);
923
	}
924
925
	$ligne_debut_initial = $ligne_debut_texte;
926
	$pos_debut_texte = 0;
927
	while ($boucle = public_trouver_premiere_boucle($texte, $id_parent, $descr, $pos_debut_texte)) {
928
		$err_b = ''; // indiquera s'il y a eu une erreur
929
		$result = new Boucle;
930
		$result->id_parent = $id_parent;
931
		$result->descr = $descr;
932
933
		$pos_courante = $boucle['pos_boucle'];
934
		$id_boucle = $boucle['id_boucle'];
935
		$pos_parent = $boucle['pos_parent'];
936
937
		$ligne_preaff = $ligne_avant = $ligne_milieu = $ligne_debut_texte + public_compte_ligne($texte, $pos_debut_texte, $pos_parent);
938
		$pos_debut_boucle = $pos_courante;
939
940
		$pos_milieu = $pos_parent;
941
942
		// Regarder si on a une partie conditionnelle avant <B_xxx>
943 View Code Duplication
		if ($boucle['pos_precond'] !== false) {
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...
944
945
			$pos_debut_boucle = $boucle['pos_precond'];
946
947
			$pos_avant = $boucle['pos_precond_inside'];
948
			$result->avant = substr($texte, $pos_avant, $pos_courante - $pos_avant);
949
			$ligne_avant = $ligne_debut_texte +  public_compte_ligne($texte, $pos_debut_texte, $pos_avant);
950
		}
951
952
		// Regarder si on a une partie inconditionnelle avant <BB_xxx>
953 View Code Duplication
		if ($boucle['pos_preaff'] !== false) {
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...
954
955
			$end_preaff = $pos_debut_boucle;
956
957
			$pos_preaff = $boucle['pos_preaff_inside'];
958
			$result->preaff = substr($texte, $pos_preaff, $end_preaff - $pos_preaff);
959
			$ligne_preaff = $ligne_debut_texte +  public_compte_ligne($texte, $pos_debut_texte, $pos_preaff);
960
		}
961
962
		$result->id_boucle = $id_boucle;
963
964
		if (!preg_match(SPEC_BOUCLE, $texte, $match, 0, $pos_milieu)
965
		  or ($pos_match = strpos($texte, $match[0], $pos_milieu)) === false
966
			or $pos_match > $pos_milieu
967
		) {
968
			$err_b = array('zbug_erreur_boucle_syntaxe', array('id' => $id_boucle));
969
			erreur_squelette($err_b, $result);
970
971
			$ligne_debut_texte += public_compte_ligne($texte, $pos_debut_texte, $pos_courante + 1);
972
			$pos_debut_texte = $pos_courante + 1;
973
			continue;
974
		}
975
976
		$result->type_requete = $match[0];
977
		$pos_milieu += strlen($match[0]);
978
		$pos_courante = $pos_milieu; // on s'en sert pour compter les lignes plus precisemment
0 ignored issues
show
Unused Code introduced by
$pos_courante 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...
979
980
		$type = $match[1];
981
		$jointures = trim($match[2]);
982
		$table_optionnelle = ($match[3]);
983
		if ($jointures) {
984
			// on affecte pas ici les jointures explicites, mais dans la compilation
985
			// ou elles seront completees des jointures declarees
986
			$result->jointures_explicites = $jointures;
987
		}
988
989
		if ($table_optionnelle) {
990
			$result->table_optionnelle = $type;
991
		}
992
993
		// 1ere passe sur les criteres, vu comme des arguments sans fct
994
		// Resultat mis dans result->param
995
		$pos_fin_criteres = $pos_milieu;
996
		phraser_args($texte, "/>", "", $all_res, $result, $pos_fin_criteres);
997
998
		// En 2e passe result->criteres contiendra un tableau
999
		// pour l'instant on met le source (chaine) :
1000
		// si elle reste ici au final, c'est qu'elle contient une erreur
1001
		$pos_courante = $pos_fin_criteres; // on s'en sert pour compter les lignes plus precisemment
1002
		$result->criteres = substr($texte, $pos_milieu, $pos_fin_criteres - $pos_milieu);
1003
		$pos_milieu = $pos_fin_criteres;
1004
1005
		//
1006
		// Recuperer la fin :
1007
		//
1008
		if ($texte[$pos_milieu] === '/') {
1009
			// boucle autofermante : pas de partie conditionnelle apres
1010
			$pos_courante += 2;
1011
			$result->milieu = '';
1012
		} else {
1013
			$pos_milieu += 1;
1014
1015
			$fin_boucle = BALISE_FIN_BOUCLE . $id_boucle . ">";
1016
			$pos_fin = strpos($texte, $fin_boucle, $pos_milieu);
1017
			if ($pos_fin === false) {
1018
				$err_b = array(
1019
					'zbug_erreur_boucle_fermant',
1020
					array('id' => $id_boucle)
1021
				);
1022
				erreur_squelette($err_b, $result);
1023
			}
1024
1025
			$pos_courante = $pos_fin + strlen($fin_boucle);
1026
			$result->milieu = substr($texte, $pos_milieu, $pos_fin - $pos_milieu);
1027
		}
1028
1029
		$ligne_suite = $ligne_apres = $ligne_debut_texte + public_compte_ligne($texte, $pos_debut_texte, $pos_courante);
1030
1031
		//
1032
		// 1. Recuperer la partie conditionnelle apres
1033
		//
1034
		$apres_boucle = BALISE_POSTCOND_BOUCLE . $id_boucle . ">";
1035
		$pos_apres = strpos($texte, $apres_boucle, $pos_courante);
1036 View Code Duplication
		if ($pos_apres !== false) {
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...
1037
			$result->apres = substr($texte, $pos_courante, $pos_apres - $pos_courante);
1038
			$pos_apres += strlen($apres_boucle);
1039
			$ligne_suite += public_compte_ligne($texte, $pos_courante, $pos_apres);
1040
			$pos_courante = $pos_apres ;
1041
		}
1042
1043
1044
		//
1045
		// 2. Recuperer la partie alternative
1046
		//
1047
		$ligne_altern = $ligne_suite;
1048
		$altern_boucle = BALISE_ALT_BOUCLE . $id_boucle . ">";
1049
		$pos_altern = strpos($texte, $altern_boucle, $pos_courante);
1050 View Code Duplication
		if ($pos_altern !== false) {
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...
1051
			$result->altern = substr($texte, $pos_courante, $pos_altern - $pos_courante);
1052
			$pos_altern += strlen($altern_boucle);
1053
			$ligne_suite += public_compte_ligne($texte, $pos_courante, $pos_altern);
1054
			$pos_courante = $pos_altern;
1055
		}
1056
1057
		//
1058
		// 3. Recuperer la partie footer non alternative
1059
		//
1060
		$ligne_postaff = $ligne_suite;
1061
		$postaff_boucle = BALISE_POSTAFF_BOUCLE . $id_boucle . ">";
1062
		$pos_postaff = strpos($texte, $postaff_boucle, $pos_courante);
1063 View Code Duplication
		if ($pos_postaff !== false) {
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...
1064
			$result->postaff = substr($texte, $pos_courante, $pos_postaff - $pos_courante);
1065
			$pos_postaff += strlen($postaff_boucle);
1066
			$ligne_suite += public_compte_ligne($texte, $pos_courante, $pos_postaff);
1067
			$pos_courante = $pos_postaff ;
1068
		}
1069
1070
		$result->ligne = $ligne_preaff;
1071
1072 View Code Duplication
		if ($p = strpos($type, ':')) {
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...
1073
			$result->sql_serveur = substr($type, 0, $p);
1074
			$type = substr($type, $p + 1);
1075
		}
1076
		$soustype = strtolower($type);
1077
1078
		if (!isset($GLOBALS["table_des_tables"][$soustype])) {
1079
			$soustype = $type;
1080
		}
1081
1082
		$result->type_requete = $soustype;
1083
		// Lancer la 2e passe sur les criteres si la 1ere etait bonne
1084
		if (!is_array($result->param)) {
1085
			$err_b = true;
1086
		} else {
1087
			phraser_criteres($result->param, $result);
1088
			if (strncasecmp($soustype, TYPE_RECURSIF, strlen(TYPE_RECURSIF)) == 0) {
1089
				$result->type_requete = TYPE_RECURSIF;
1090
				$args = $result->param;
1091
				array_unshift($args,
1092
					substr($type, strlen(TYPE_RECURSIF)));
1093
				$result->param = $args;
1094
			}
1095
		}
1096
1097
		$descr['id_mere_contexte'] = $id_boucle;
1098
		$result->milieu = public_phraser_html_dist($result->milieu, $id_boucle, $boucles, $descr, $ligne_milieu, $boucle_placeholder);
1099
		// reserver la place dans la pile des boucles pour compiler ensuite dans le bon ordre
1100
		// ie les boucles qui apparaissent dans les partie conditionnelles doivent etre compilees apres cette boucle
1101
		// si il y a deja une boucle de ce nom, cela declenchera une erreur ensuite
1102
		if (empty($boucles[$id_boucle])){
1103
			$boucles[$id_boucle] = null;
1104
		}
1105
		$result->preaff = public_phraser_html_dist($result->preaff, $id_parent, $boucles, $descr, $ligne_preaff, $boucle_placeholder);
1106
		$result->avant = public_phraser_html_dist($result->avant, $id_parent, $boucles, $descr, $ligne_avant, $boucle_placeholder);
1107
		$result->apres = public_phraser_html_dist($result->apres, $id_parent, $boucles, $descr, $ligne_apres, $boucle_placeholder);
1108
		$result->altern = public_phraser_html_dist($result->altern, $id_parent, $boucles, $descr, $ligne_altern, $boucle_placeholder);
1109
		$result->postaff = public_phraser_html_dist($result->postaff, $id_parent, $boucles, $descr, $ligne_postaff, $boucle_placeholder);
1110
1111
		// Prevenir le generateur de code que le squelette est faux
1112
		if ($err_b) {
1113
			$result->type_requete = false;
1114
		}
1115
1116
		// Verifier qu'il n'y a pas double definition
1117
		// apres analyse des sous-parties (pas avant).
1118
		if (!empty($boucles[$id_boucle])) {
1119
			$err_b_d = array(
1120
				'zbug_erreur_boucle_double',
1121
				array('id' => $id_boucle)
1122
			);
1123
			erreur_squelette($err_b_d, $result);
1124
			// Prevenir le generateur de code que le squelette est faux
1125
			$boucles[$id_boucle]->type_requete = false;
1126
		} else {
1127
			$boucles[$id_boucle] = $result;
1128
		}
1129
1130
		// remplacer la boucle par un placeholder qui compte le meme nombre de lignes
1131
		$placeholder = public_generer_boucle_placeholder($id_boucle, $boucles[$id_boucle], $boucle_placeholder, $ligne_suite - $ligne_debut_texte);
1132
		$longueur_boucle = $pos_courante - $boucle['debut_boucle'];
1133
		$texte = substr_replace($texte, $placeholder, $boucle['debut_boucle'], $longueur_boucle);
1134
		$pos_courante = $pos_courante - $longueur_boucle + strlen($placeholder);
1135
1136
		// phraser la partie avant le debut de la boucle
1137
		#$all_res = phraser_champs_etendus(substr($texte, $pos_debut_texte, $boucle['debut_boucle'] - $pos_debut_texte), $ligne_debut_texte, $all_res);
1138
		#$all_res[] = &$boucles[$id_boucle];
1139
1140
		$ligne_debut_texte = $ligne_suite;
1141
		$pos_debut_texte = $pos_courante;
1142
	}
1143
1144
	$all_res = phraser_champs_etendus($texte, $ligne_debut_initial, $all_res);
1145
1146
	return $all_res;
1147
}