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

phraser_html.php ➔ public_trouver_premiere_boucle()   C

Complexity

Conditions 13
Paths 11

Size

Total Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
nc 11
nop 3
dl 0
loc 61
rs 6.6166
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, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2019                                                *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * 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
// http://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
		phraser_args($texte, "/>", "", $result, $champ);
89
		if (!$champ->texte or count($champ->param) > 1) {
90
			if (!function_exists('normaliser_inclure')) {
91
				include_spip('public/normaliser');
92
			}
93
			normaliser_inclure($champ);
94
		}
95
		$texte = substr($champ->apres, strpos($champ->apres, '>') + 1);
96
		$champ->apres = "";
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
// http://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
		phraser_args($match[7], ":", '', array(), $champ);
194
		$result[] = $champ;
195
	}
196
	if ($texte !== "") {
197
		$result = phraser_champs($texte, $ligne, $result);
198
	}
199
200
	return $result;
201
}
202
203
/**
204
 * Repère et phrase les balises SPIP tel que `#NOM` dans un texte
205
 *
206
 * Phrase également ses arguments si la balise en a (`#NOM{arg, ...}`)
207
 *
208
 * @uses phraser_polyglotte()
209
 * @uses phraser_args()
210
 * @uses phraser_vieux()
211
 *
212
 * @param string $texte
213
 * @param int $ligne
214
 * @param array $result
215
 * @return array
216
 **/
217
function phraser_champs($texte, $ligne, $result) {
218
	while (preg_match("/" . NOM_DE_CHAMP . "/S", $texte, $match)) {
219
		$p = strpos($texte, $match[0]);
220
		// texte après la balise
221
		$suite = substr($texte, $p + strlen($match[0]));
222
223
		$debut = substr($texte, 0, $p);
224
		if ($p) {
225
			$result = phraser_polyglotte($debut, $ligne, $result);
226
		}
227
		$ligne += substr_count($debut, "\n");
228
		$champ = new Champ;
229
		$champ->ligne = $ligne;
230
		$ligne += substr_count($match[0], "\n");
231
		$champ->nom_boucle = $match[2];
232
		$champ->nom_champ = $match[3];
233
		$champ->etoile = $match[5];
234
235
		if ($suite and $suite[0] == '{') {
236
			phraser_arg($suite, '', array(), $champ);
237
			// ce ltrim est une ereur de conception
238
			// mais on le conserve par souci de compatibilite
239
			$texte = ltrim($suite);
240
			// Il faudrait le normaliser dans l'arbre de syntaxe abstraite
241
			// pour faire sauter ce cas particulier a la decompilation.
242
			/* Ce qui suit est malheureusement incomplet pour cela:
243
			if ($n = (strlen($suite) - strlen($texte))) {
244
				$champ->apres = array(new Texte);
245
				$champ->apres[0]->texte = substr($suite,0,$n);
246
			}
247
			*/
248
		} else {
249
			$texte = $suite;
250
		}
251
		phraser_vieux($champ);
252
		$result[] = $champ;
253
	}
254
	if ($texte !== "") {
255
		$result = phraser_polyglotte($texte, $ligne, $result);
256
	}
257
258
	return $result;
259
}
260
261
// Gestion des imbrications:
262
// on cherche les [..] les plus internes et on les remplace par une chaine
263
// %###N@ ou N indexe un tableau comportant le resultat de leur analyse
264
// on recommence tant qu'il y a des [...] en substituant a l'appel suivant
265
266
// http://code.spip.net/@phraser_champs_etendus
267
function phraser_champs_etendus($texte, $ligne, $result) {
268
	if ($texte === "") {
269
		return $result;
270
	}
271
	$sep = '##';
272
	while (strpos($texte, $sep) !== false) {
273
		$sep .= '#';
274
	}
275
276
	return array_merge($result, phraser_champs_interieurs($texte, $ligne, $sep, array()));
277
}
278
279
/**
280
 * Analyse les filtres d'un champ etendu et affecte le resultat
281
 * renvoie la liste des lexemes d'origine augmentee
282
 * de ceux trouves dans les arguments des filtres (rare)
283
 * sert aussi aux arguments des includes et aux criteres de boucles
284
 * Tres chevelu
285
 *
286
 * http://code.spip.net/@phraser_args
287
 *
288
 * @param $texte
289
 * @param $fin
290
 * @param $sep
291
 * @param $result
292
 * @param $pointeur_champ
293
 * @return array
294
 */
295
function phraser_args($texte, $fin, $sep, $result, &$pointeur_champ) {
296
	$texte = ltrim($texte);
297
	while (($texte !== "") && strpos($fin, $texte[0]) === false) {
298
		$result = phraser_arg($texte, $sep, $result, $pointeur_champ);
299
		$texte = ltrim($texte);
300
	}
301
# mettre ici la suite du texte, 
302
# notamment pour que l'appelant vire le caractere fermant si besoin
303
	$pointeur_champ->apres = $texte;
304
305
	return $result;
306
}
307
308
// http://code.spip.net/@phraser_arg
309
function phraser_arg(&$texte, $sep, $result, &$pointeur_champ) {
310
	preg_match(",^(\|?[^}{)|]*)(.*)$,ms", $texte, $match);
311
	$suite = ltrim($match[2]);
312
	$fonc = trim($match[1]);
313
	if ($fonc && $fonc[0] == "|") {
314
		$fonc = ltrim(substr($fonc, 1));
315
	}
316
	$res = array($fonc);
317
	$err_f = '';
318
	// cas du filtre sans argument ou du critere /
319
	if (($suite && ($suite[0] != '{')) || ($fonc && $fonc[0] == '/')) {
320
		// si pas d'argument, alors il faut une fonction ou un double |
321
		if (!$match[1]) {
322
			$err_f = array('zbug_erreur_filtre', array('filtre' => $texte));
323
			erreur_squelette($err_f, $pointeur_champ);
324
			$texte = '';
325
		} else {
326
			$texte = $suite;
327
		}
328 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...
329
			$pointeur_champ->param = false;
330
		} elseif ($fonc !== '') {
331
			$pointeur_champ->param[] = $res;
332
		}
333
		// pour les balises avec faux filtres qui boudent ce dur larbeur
334
		$pointeur_champ->fonctions[] = array($fonc, '');
335
336
		return $result;
337
	}
338
	$args = ltrim(substr($suite, 1)); // virer le '(' initial
339
	$collecte = array();
340
	while ($args && $args[0] != '}') {
341
		if ($args[0] == '"') {
342
			preg_match('/^(")([^"]*)(")(.*)$/ms', $args, $regs);
343
		} elseif ($args[0] == "'") {
344
			preg_match("/^(')([^']*)(')(.*)$/ms", $args, $regs);
345
		} else {
346
			preg_match("/^([[:space:]]*)([^,([{}]*([(\[{][^])}]*[])}])?[^,}]*)([,}].*)$/ms", $args, $regs);
347
			if (!strlen($regs[2])) {
348
				$err_f = array('zbug_erreur_filtre', array('filtre' => $args));
349
				erreur_squelette($err_f, $pointeur_champ);
350
				$champ = new Texte;
351
				$champ->apres = $champ->avant = $args = "";
352
				break;
353
			}
354
		}
355
		$arg = $regs[2];
356
		if (trim($regs[1])) {
357
			$champ = new Texte;
358
			$champ->texte = $arg;
359
			$champ->apres = $champ->avant = $regs[1];
360
			$result[] = $champ;
361
			$collecte[] = $champ;
362
			$args = ltrim($regs[count($regs) - 1]);
363
		} else {
364
			if (!preg_match("/" . NOM_DE_CHAMP . "([{|])/", $arg, $r)) {
365
				// 0 est un aveu d'impuissance. A completer
366
				$arg = phraser_champs_exterieurs($arg, 0, $sep, $result);
367
368
				$args = ltrim($regs[count($regs) - 1]);
369
				$collecte = array_merge($collecte, $arg);
370
				$result = array_merge($result, $arg);
371
			} else {
372
				$n = strpos($args, $r[0]);
373
				$pred = substr($args, 0, $n);
374
				$par = ',}';
375
				if (preg_match('/^(.*)\($/', $pred, $m)) {
376
					$pred = $m[1];
377
					$par = ')';
378
				}
379
				if ($pred) {
380
					$champ = new Texte;
381
					$champ->texte = $pred;
382
					$champ->apres = $champ->avant = "";
383
					$result[] = $champ;
384
					$collecte[] = $champ;
385
				}
386
				$rec = substr($args, $n + strlen($r[0]) - 1);
387
				$champ = new Champ;
388
				$champ->nom_boucle = $r[2];
389
				$champ->nom_champ = $r[3];
390
				$champ->etoile = $r[5];
391
				$next = $r[6];
392
				while ($next == '{') {
393
					phraser_arg($rec, $sep, array(), $champ);
394
					$args = ltrim($rec);
395
					$next = isset($args[0]) ? $args[0] : '';
396
				}
397
				while ($next == '|') {
398
					phraser_args($rec, $par, $sep, array(), $champ);
399
					$args = $champ->apres;
400
					$champ->apres = '';
401
					$next = isset($args[0]) ? $args[0] : '';
402
				}
403
				// Si erreur de syntaxe dans un sous-argument, propager.
404
				if ($champ->param === false) {
405
					$err_f = true;
406
				} else {
407
					phraser_vieux($champ);
408
				}
409
				if ($par == ')') {
410
					$args = substr($args, 1);
411
				}
412
				$collecte[] = $champ;
413
				$result[] = $champ;
414
			}
415
		}
416
		if (isset($args[0]) and $args[0] == ',') {
417
			$args = ltrim(substr($args, 1));
418
			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...
419
				$res[] = $collecte;
420
				$collecte = array();
421
			}
422
		}
423
	}
424
	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...
425
		$res[] = $collecte;
426
		$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...
427
	}
428
	$texte = substr($args, 1);
429
	$source = substr($suite, 0, strlen($suite) - strlen($texte));
430
	// propager les erreurs, et ignorer les param vides
431
	if ($pointeur_champ->param !== false) {
432 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...
433
			$pointeur_champ->param = false;
434
		} elseif ($fonc !== '' || count($res) > 1) {
435
			$pointeur_champ->param[] = $res;
436
		}
437
	}
438
	// pour les balises avec faux filtres qui boudent ce dur larbeur
439
	$pointeur_champ->fonctions[] = array($fonc, $source);
440
441
	return $result;
442
}
443
444
445
// http://code.spip.net/@phraser_champs_exterieurs
446
function phraser_champs_exterieurs($texte, $ligne, $sep, $nested) {
447
	$res = array();
448
	while (($p = strpos($texte, "%$sep")) !== false) {
449
		if (!preg_match(',^%' . preg_quote($sep) . '([0-9]+)@,', substr($texte, $p), $m)) {
450
			break;
451
		}
452
		$debut = substr($texte, 0, $p);
453
		$texte = substr($texte, $p + strlen($m[0]));
454
		if ($p) {
455
			$res = phraser_inclure($debut, $ligne, $res);
456
		}
457
		$ligne += substr_count($debut, "\n");
458
		$res[] = $nested[$m[1]];
459
	}
460
461
	return (($texte === '') ? $res : phraser_inclure($texte, $ligne, $res));
462
}
463
464
// http://code.spip.net/@phraser_champs_interieurs
465
function phraser_champs_interieurs($texte, $ligne, $sep, $result) {
466
	$i = 0; // en fait count($result)
467
	$x = "";
468
469
	while (true) {
470
		$j = $i;
471
		$n = $ligne;
472
		while (preg_match(CHAMP_ETENDU, $texte, $match)) {
473
			$p = strpos($texte, $match[0]);
474
			$debut = substr($texte, 0, $p);
475
			if ($p) {
476
				$result[$i] = $debut;
477
				$i++;
478
			}
479
			$nom = $match[4];
480
			$champ = new Champ;
481
			// ca ne marche pas encore en cas de champ imbrique
482
			$champ->ligne = $x ? 0 : ($n + substr_count($debut, "\n"));
483
			$champ->nom_boucle = $match[3];
484
			$champ->nom_champ = $nom;
485
			$champ->etoile = $match[6];
486
			// phraser_args indiquera ou commence apres
487
			$result = phraser_args($match[7], ")", $sep, $result, $champ);
488
			phraser_vieux($champ);
489
			$champ->avant =
490
				phraser_champs_exterieurs($match[1], $n, $sep, $result);
491
			$debut = substr($champ->apres, 1);
492
			if (!empty($debut)) {
493
				$n += substr_count(substr($texte, 0, strpos($texte, $debut)), "\n");
494
			}
495
			$champ->apres = phraser_champs_exterieurs($debut, $n, $sep, $result);
496
497
			$result[$i] = $champ;
498
			$i++;
499
			$texte = substr($texte, $p + strlen($match[0]));
500
		}
501
		if ($texte !== "") {
502
			$result[$i] = $texte;
503
			$i++;
504
		}
505
		$x = '';
506
507
		while ($j < $i) {
508
			$z = $result[$j];
509
			// j'aurais besoin de connaitre le nombre de lignes...
510
			if (is_object($z)) {
511
				$x .= "%$sep$j@";
512
			} else {
513
				$x .= $z;
514
			}
515
			$j++;
516
		}
517
		if (preg_match(CHAMP_ETENDU, $x)) {
518
			$texte = $x;
519
		} else {
520
			return phraser_champs_exterieurs($x, $ligne, $sep, $result);
521
		}
522
	}
523
}
524
525
function phraser_vieux(&$champ) {
526
	$nom = $champ->nom_champ;
527
	if ($nom == 'EMBED_DOCUMENT') {
528
		if (!function_exists('phraser_vieux_emb')) {
529
			include_spip('public/normaliser');
530
		}
531
		phraser_vieux_emb($champ);
532
	} elseif ($nom == 'EXPOSER') {
533
		if (!function_exists('phraser_vieux_exposer')) {
534
			include_spip('public/normaliser');
535
		}
536
		phraser_vieux_exposer($champ);
537
	} elseif ($champ->param) {
538
		if ($nom == 'FORMULAIRE_RECHERCHE') {
539
			if (!function_exists('phraser_vieux_recherche')) {
540
				include_spip('public/normaliser');
541
			}
542
			phraser_vieux_recherche($champ);
543
		} elseif (preg_match(",^LOGO_[A-Z]+,", $nom)) {
544
			if (!function_exists('phraser_vieux_logos')) {
545
				include_spip('public/normaliser');
546
			}
547
			phraser_vieux_logos($champ);
548
		} elseif ($nom == 'MODELE') {
549
			if (!function_exists('phraser_vieux_modele')) {
550
				include_spip('public/normaliser');
551
			}
552
			phraser_vieux_modele($champ);
553
		} elseif ($nom == 'INCLURE' or $nom == 'INCLUDE') {
554
			if (!function_exists('phraser_vieux_inclu')) {
555
				include_spip('public/normaliser');
556
			}
557
			phraser_vieux_inclu($champ);
558
		}
559
	}
560
}
561
562
563
/**
564
 * Analyse les critères de boucle
565
 *
566
 * Chaque paramètre de la boucle (tel que {id_article>3}) est analysé
567
 * pour construire un critère (objet Critere) de boucle.
568
 *
569
 * Un critère a une description plus fine que le paramètre original
570
 * car on en extrait certaines informations tel que la négation et l'opérateur
571
 * utilisé s'il y a.
572
 *
573
 * La fonction en profite pour déclarer des modificateurs de boucles
574
 * en présence de certains critères (tout, plat) ou initialiser des
575
 * variables de compilation (doublons)...
576
 *
577
 * @param array $params
578
 *     Tableau de description des paramètres passés à la boucle.
579
 *     Chaque paramètre deviendra un critère
580
 * @param Boucle $result
581
 *     Description de la boucle
582
 *     Elle sera complété de la liste de ses critères
583
 * @return void
584
 **/
585
function phraser_criteres($params, &$result) {
586
587
	$err_ci = ''; // indiquera s'il y a eu une erreur
588
	$args = array();
589
	$type = $result->type_requete;
590
	$doublons = array();
591
	foreach ($params as $v) {
592
		$var = $v[1][0];
593
		$param = ($var->type != 'texte') ? "" : $var->texte;
594
		if ((count($v) > 2) && (!preg_match(",[^A-Za-z]IN[^A-Za-z],i", $param))) {
595
			// plus d'un argument et pas le critere IN:
596
			// detecter comme on peut si c'est le critere implicite LIMIT debut, fin
597
			if ($var->type != 'texte'
598
				or preg_match("/^(n|n-|(n-)?\d+)$/S", $param)
599
			) {
600
				$op = ',';
601
				$not = "";
602
				$cond = false;
603
			} else {
604
				// Le debut du premier argument est l'operateur
605
				preg_match("/^([!]?)([a-zA-Z][a-zA-Z0-9_]*)[[:space:]]*(\??)[[:space:]]*(.*)$/ms", $param, $m);
606
				$op = $m[2];
607
				$not = $m[1];
608
				$cond = $m[3];
609
				// virer le premier argument,
610
				// et mettre son reliquat eventuel
611
				// Recopier pour ne pas alterer le texte source
612
				// utile au debusqueur
613
				if ($m[4]) {
614
					// une maniere tres sale de supprimer les "' autour de {critere "xxx","yyy"}
615
					if (preg_match(',^(["\'])(.*)\1$,', $m[4])) {
616
						$c = null;
617
						eval('$c = ' . $m[4] . ';');
618
						if (isset($c)) {
619
							$m[4] = $c;
620
						}
621
					}
622
					$texte = new Texte;
623
					$texte->texte = $m[4];
624
					$v[1][0] = $texte;
625
				} else {
626
					array_shift($v[1]);
627
				}
628
			}
629
			array_shift($v); // $v[O] est vide
630
			$crit = new Critere;
631
			$crit->op = $op;
632
			$crit->not = $not;
633
			$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...
634
			$crit->exclus = "";
635
			$crit->param = $v;
636
			$args[] = $crit;
637
		} else {
638
			if ($var->type != 'texte') {
639
				// cas 1 seul arg ne commencant pas par du texte brut: 
640
				// erreur ou critere infixe "/"
641
				if (($v[1][1]->type != 'texte') || (trim($v[1][1]->texte) != '/')) {
642
					$err_ci = array(
643
						'zbug_critere_inconnu',
644
						array('critere' => $var->nom_champ)
645
					);
646
					erreur_squelette($err_ci, $result);
647
				} else {
648
					$crit = new Critere;
649
					$crit->op = '/';
650
					$crit->not = "";
651
					$crit->exclus = "";
652
					$crit->param = array(array($v[1][0]), array($v[1][2]));
653
					$args[] = $crit;
654
				}
655
			} else {
656
				// traiter qq lexemes particuliers pour faciliter la suite
657
				// les separateurs
658
				if ($var->apres) {
659
					$result->separateur[] = $param;
660
				} elseif (($param == 'tout') or ($param == 'tous')) {
661
					$result->modificateur['tout'] = true;
662
				} elseif ($param == 'plat') {
663
					$result->modificateur['plat'] = true;
664
				}
665
666
				// Boucle hierarchie, analyser le critere id_rubrique
667
				// et les autres critères {id_x} pour forcer {tout} sur
668
				// ceux-ci pour avoir la rubrique mere...
669
				// Les autres critères de la boucle hierarchie doivent être
670
				// traités normalement.
671
				elseif (strcasecmp($type, 'hierarchie') == 0
672
					and !preg_match(",^id_rubrique\b,", $param)
673
					and preg_match(",^id_\w+\s*$,", $param)
674
				) {
675
					$result->modificateur['tout'] = true;
676
				} elseif (strcasecmp($type, 'hierarchie') == 0 and $param == "id_rubrique") {
677
					// rien a faire sur {id_rubrique} tout seul
678
				} else {
679
					// pas d'emplacement statique, faut un dynamique
680
					// mais il y a 2 cas qui ont les 2 !
681
					if (($param == 'unique') || (preg_match(',^!?doublons *,', $param))) {
682
						// cette variable sera inseree dans le code
683
						// et son nom sert d'indicateur des maintenant
684
						$result->doublons = '$doublons_index';
685
						if ($param == 'unique') {
686
							$param = 'doublons';
687
						}
688
					} elseif ($param == 'recherche') {
689
						// meme chose (a cause de #nom_de_boucle:URL_*)
690
						$result->hash = ' ';
691
					}
692
693
					if (preg_match(',^ *([0-9-]+) *(/) *(.+) *$,', $param, $m)) {
694
						$crit = phraser_critere_infixe($m[1], $m[3], $v, '/', '', '');
695
					} elseif (preg_match(',^([!]?)(' . CHAMP_SQL_PLUS_FONC .
696
						')[[:space:]]*(\??)(!?)(<=?|>=?|==?|\b(?:IN|LIKE)\b)(.*)$,is', $param, $m)) {
697
						$a2 = trim($m[8]);
698
						if ($a2 and ($a2[0] == "'" or $a2[0] == '"') and ($a2[0] == substr($a2, -1))) {
699
							$a2 = substr($a2, 1, -1);
700
						}
701
						$crit = phraser_critere_infixe($m[2], $a2, $v,
702
							(($m[2] == 'lang_select') ? $m[2] : $m[7]),
703
							$m[6], $m[5]);
704
						$crit->exclus = $m[1];
705
					} elseif (preg_match("/^([!]?)\s*(" .
706
						CHAMP_SQL_PLUS_FONC .
707
						")\s*(\??)(.*)$/is", $param, $m)) {
708
						// contient aussi les comparaisons implicites !
709
						// Comme ci-dessus: 
710
						// le premier arg contient l'operateur
711
						array_shift($v);
712
						if ($m[6]) {
713
							$v[0][0] = new Texte;
714
							$v[0][0]->texte = $m[6];
715
						} else {
716
							array_shift($v[0]);
717
							if (!$v[0]) {
718
								array_shift($v);
719
							}
720
						}
721
						$crit = new Critere;
722
						$crit->op = $m[2];
723
						$crit->param = $v;
724
						$crit->not = $m[1];
725
						$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...
726
					} else {
727
						$err_ci = array(
728
							'zbug_critere_inconnu',
729
							array('critere' => $param)
730
						);
731
						erreur_squelette($err_ci, $result);
732
					}
733
734
					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...
735
						$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...
736
					} else {
737
						$doublons[] = $crit;
738
					}
739
				}
740
			}
741
		}
742
	}
743
744
	// les doublons non nies doivent etre le dernier critere
745
	// pour que la variable $doublon_index ait la bonne valeur
746
	// cf critere_doublon
747
	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...
748
		$args = array_merge($args, $doublons);
749
	}
750
751
	// Si erreur, laisser la chaine dans ce champ pour le HTTP 503
752
	if (!$err_ci) {
753
		$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...
754
	}
755
}
756
757
// http://code.spip.net/@phraser_critere_infixe
758
function phraser_critere_infixe($arg1, $arg2, $args, $op, $not, $cond) {
759
	$args[0] = new Texte;
760
	$args[0]->texte = $arg1;
761
	$args[0] = array($args[0]);
762
	$args[1][0] = new Texte;
763
	$args[1][0]->texte = $arg2;
764
	$crit = new Critere;
765
	$crit->op = $op;
766
	$crit->not = $not;
767
	$crit->cond = $cond;
768
	$crit->param = $args;
769
770
	return $crit;
771
}
772
773
/**
774
 * Compter le nombre de lignes dans une partie texte
775
 * @param $texte
776
 * @param int $debut
777
 * @param null $longueur
778
 * @return int
779
 */
780
function public_compte_ligne($texte, $debut = 0, $longueur = null) {
781
	if (is_null($longueur)) {
782
		return substr_count($texte, "\n", $debut);
783
	}
784
	else {
785
		return substr_count($texte, "\n", $debut, $longueur);
786
	}
787
}
788
789
790
/**
791
 * Trouver la boucle qui commence en premier dans un texte
792
 * On repere les boucles via <BOUCLE_xxx(
793
 * et ensuite on regarde son vrai debut soit <B_xxx> soit <BB_xxx>
794
 *
795
 * @param $texte
796
 * @param $id_parent
797
 * @param $descr
798
 * @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...
799
 */
800
function public_trouver_premiere_boucle($texte, $id_parent, $descr) {
801
	$premiere_boucle = null;
802
	$current_pos = 0;
803
	while (($pos_boucle = strpos($texte, BALISE_BOUCLE, $current_pos)) !== false) {
804
		$current_pos = $pos_boucle + 1;
805
		$pos_parent = strpos($texte,'(', $pos_boucle);
806
		if ($pos_parent === false
807
		  or !$id_boucle = trim(substr($texte,$pos_boucle + strlen(BALISE_BOUCLE), $pos_parent - $pos_boucle - strlen(BALISE_BOUCLE)))
808
			or !(is_numeric($id_boucle) or strpos($id_boucle, '_') === 0)) {
809
810
			$result = new Boucle;
811
			$result->id_parent = $id_parent;
812
			$result->descr = $descr;
813
814
			// un id_boucle pour l'affichage de l'erreur
815
			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...
816
				$id_boucle = substr($texte,$pos_boucle + strlen(BALISE_BOUCLE), 15);
817
			}
818
			$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...
819
			$err_b = array('zbug_erreur_boucle_syntaxe', array('id' => $id_boucle));
820
			erreur_squelette($err_b, $result);
821
822
			continue;
823
		}
824
		else {
825
			$boucle = [
826
				'id_boucle' => $id_boucle,
827
				'debut_boucle' => $pos_boucle,
828
				'pos_boucle' => $pos_boucle,
829
				'pos_parent' => $pos_parent,
830
				'pos_precond' => false,
831
				'pos_precond_inside' => false,
832
				'pos_preaff' => false,
833
				'pos_preaff_inside' => false,
834
			];
835
836
			// trouver sa position de depart reelle : au <B_ ou au <BB_
837
			$precond_boucle = BALISE_PRECOND_BOUCLE . $id_boucle . '>';
838
			$pos_precond = strpos($texte, $precond_boucle);
839
			if ($pos_precond !== false and $pos_precond < $boucle['debut_boucle']) {
840
				$boucle['debut_boucle'] = $pos_precond;
841
				$boucle['pos_precond'] = $pos_precond;
842
				$boucle['pos_precond_inside'] = $pos_precond + strlen($precond_boucle);
843
			}
844
845
			$preaff_boucle = BALISE_PREAFF_BOUCLE . $id_boucle . '>';
846
			$pos_preaff = strpos($texte, $preaff_boucle);
847
			if ($pos_preaff !== false and $pos_preaff < $boucle['debut_boucle']) {
848
				$boucle['debut_boucle'] = $pos_preaff;
849
				$boucle['pos_preaff'] = $pos_preaff;
850
				$boucle['pos_preaff_inside'] = $pos_preaff + strlen($preaff_boucle);
851
			}
852
853
			if (is_null($premiere_boucle) or $premiere_boucle['debut_boucle'] > $boucle['debut_boucle']) {
854
				$premiere_boucle = $boucle;
855
			}
856
		}
857
	}
858
859
	return $premiere_boucle;
860
}
861
862
863
function public_phraser_html_dist($texte, $id_parent, &$boucles, $descr, $ligne = 1) {
864
865
	$all_res = array();
866
867
	while ($boucle = public_trouver_premiere_boucle($texte, $id_parent, $descr)) {
868
		$err_b = ''; // indiquera s'il y a eu une erreur
869
		$result = new Boucle;
870
		$result->id_parent = $id_parent;
871
		$result->descr = $descr;
872
873
		$pos_boucle = $boucle['pos_boucle'];
874
		$id_boucle = $boucle['id_boucle'];
875
		$pos_parent = $boucle['pos_parent'];
876
877
		$ligne_preaff = $ligne_avant = $ligne_milieu = $ligne + public_compte_ligne($texte, 0, $pos_parent);
878
		$pos_debut_boucle = $pos_boucle;
879
		$milieu = substr($texte, $pos_parent);
880
881
		// Regarder si on a une partie conditionnelle avant <B_xxx>
882 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...
883
884
			$pos_debut_boucle = $boucle['pos_precond'];
885
886
			$pos_avant = $boucle['pos_precond_inside'];
887
			$result->avant = substr($texte, $pos_avant, $pos_boucle - $pos_avant);
888
			$ligne_avant = $ligne +  public_compte_ligne($texte,0, $pos_avant);
889
		}
890
891
		// Regarder si on a une partie inconditionnelle avant <BB_xxx>
892 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...
893
894
			$end_preaff = $pos_debut_boucle;
895
896
			$pos_preaff = $boucle['pos_preaff_inside'];
897
			$result->preaff = substr($texte, $pos_preaff, $end_preaff - $pos_preaff);
898
			$ligne_preaff = $ligne +  public_compte_ligne($texte,0, $pos_preaff);
899
		}
900
901
		$debut = substr($texte, 0, $boucle['debut_boucle']);
902
903
		$result->id_boucle = $id_boucle;
904
905
		if (!preg_match(SPEC_BOUCLE, $milieu, $match)) {
906
			$err_b = array('zbug_erreur_boucle_syntaxe', array('id' => $id_boucle));
907
			erreur_squelette($err_b, $result);
908
909
			$ligne += public_compte_ligne($texte, 0, $pos_boucle + 1);
910
			$texte = substr($texte, $pos_boucle + 1);
911
			continue;
912
		}
913
914
		$result->type_requete = $match[0];
915
		$milieu = substr($milieu, strlen($match[0]));
916
		$pos_boucle = $pos_parent + strlen($match[0]); // on s'en sert pour compter les lignes plus precisemment
917
918
		$type = $match[1];
919
		$jointures = trim($match[2]);
920
		$table_optionnelle = ($match[3]);
921
		if ($jointures) {
922
			// on affecte pas ici les jointures explicites, mais dans la compilation
923
			// ou elles seront completees des jointures declarees
924
			$result->jointures_explicites = $jointures;
925
		}
926
927
		if ($table_optionnelle) {
928
			$result->table_optionnelle = $type;
0 ignored issues
show
Documentation Bug introduced by
The property $table_optionnelle was declared of type boolean, but $type 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...
929
		}
930
931
		// 1ere passe sur les criteres, vu comme des arguments sans fct
932
		// Resultat mis dans result->param
933
		phraser_args($milieu, "/>", "", $all_res, $result);
934
935
		// En 2e passe result->criteres contiendra un tableau
936
		// pour l'instant on met le source (chaine) :
937
		// si elle reste ici au final, c'est qu'elle contient une erreur
938
		$pos_fin_criteres = strpos($milieu, $result->apres);
939
		$pos_boucle += $pos_fin_criteres; // on s'en sert pour compter les lignes plus precisemment
940
		$result->criteres = substr($milieu, 0, $pos_fin_criteres);
941
		$milieu = $result->apres;
942
		$result->apres = "";
943
944
		//
945
		// Recuperer la fin :
946
		//
947
		if ($milieu[0] === '/') {
948
			// boucle autofermante : pas de partie conditionnelle apres
949
			$suite = substr($milieu, 2);
950
			$pos_boucle += 2;
951
			$milieu = '';
952
		} else {
953
			$milieu = substr($milieu, 1);
954
			$pos_boucle += 1;
955
956
			$fin_boucle = BALISE_FIN_BOUCLE . $id_boucle . ">";
957
			$pos_fin = strpos($milieu, $fin_boucle);
958
			if ($pos_fin === false) {
959
				$err_b = array(
960
					'zbug_erreur_boucle_fermant',
961
					array('id' => $id_boucle)
962
				);
963
				erreur_squelette($err_b, $result);
964
			}
965
966
			$pos_boucle += $pos_fin + strlen($fin_boucle);
967
			$suite = substr($milieu, $pos_fin + strlen($fin_boucle));
968
			$milieu = substr($milieu, 0, $pos_fin);
969
		}
970
971
		$result->milieu = $milieu;
972
		$ligne_suite = $ligne_apres = $ligne + public_compte_ligne($texte, 0, $pos_boucle);
973
974
		//
975
		// 1. Recuperer la partie conditionnelle apres
976
		//
977
		$apres_boucle = BALISE_POSTCOND_BOUCLE . $id_boucle . ">";
978
		$pos_apres = strpos($suite, $apres_boucle);
979 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...
980
			$result->apres = substr($suite, 0, $pos_apres);
981
			$pos_apres += strlen($apres_boucle);
982
			$suite = substr($suite, $pos_apres);
983
			$ligne_suite += public_compte_ligne($texte, $pos_boucle, $pos_apres);
984
			$pos_boucle += $pos_apres ;
985
		}
986
987
988
		//
989
		// 2. Recuperer la partie alternative
990
		//
991
		$ligne_altern = $ligne_suite;
992
		$altern_boucle = BALISE_ALT_BOUCLE . $id_boucle . ">";
993
		$pos_altern = strpos($suite, $altern_boucle);
994 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...
995
			$result->altern = substr($suite, 0, $pos_altern);
996
			$pos_altern += strlen($altern_boucle);
997
			$suite = substr($suite, $pos_altern);
998
			$ligne_suite += public_compte_ligne($texte, $pos_boucle, $pos_altern);
999
			$pos_boucle += $pos_altern;
1000
		}
1001
1002
		//
1003
		// 3. Recuperer la partie footer non alternative
1004
		//
1005
		$ligne_postaff = $ligne_suite;
1006
		$postaff_boucle = BALISE_POSTAFF_BOUCLE . $id_boucle . ">";
1007
		$pos_postaff = strpos($suite, $postaff_boucle);
1008 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...
1009
			$result->postaff = substr($suite, 0, $pos_postaff);
1010
			$pos_postaff += strlen($postaff_boucle);
1011
			$suite = substr($suite, $pos_postaff);
1012
			$ligne_suite += public_compte_ligne($texte, $pos_boucle, $pos_postaff);
1013
			$pos_boucle += $pos_postaff ;
1014
		}
1015
1016
		$result->ligne = $ligne_preaff;
1017
1018 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...
1019
			$result->sql_serveur = substr($type, 0, $p);
1020
			$type = substr($type, $p + 1);
1021
		}
1022
		$soustype = strtolower($type);
1023
1024
		if (!isset($GLOBALS["table_des_tables"][$soustype])) {
1025
			$soustype = $type;
1026
		}
1027
1028
		$result->type_requete = $soustype;
1029
		// Lancer la 2e passe sur les criteres si la 1ere etait bonne
1030
		if (!is_array($result->param)) {
1031
			$err_b = true;
1032
		} else {
1033
			phraser_criteres($result->param, $result);
1034
			if (strncasecmp($soustype, TYPE_RECURSIF, strlen(TYPE_RECURSIF)) == 0) {
1035
				$result->type_requete = TYPE_RECURSIF;
1036
				$args = $result->param;
1037
				array_unshift($args,
1038
					substr($type, strlen(TYPE_RECURSIF)));
1039
				$result->param = $args;
1040
			}
1041
		}
1042
1043
		$descr['id_mere_contexte'] = $id_boucle;
1044
		$result->milieu = public_phraser_html_dist($milieu, $id_boucle, $boucles, $descr, $ligne_milieu);
1045
		// reserver la place dans la pile des boucles pour compiler ensuite dans le bon ordre
1046
		// ie les boucles qui apparaissent dans les partie conditionnelles doivent etre compilees apres cette boucle
1047
		$boucles[$id_boucle] = null;
1048
		$result->preaff = public_phraser_html_dist($result->preaff, $id_parent, $boucles, $descr, $ligne_preaff);
1049
		$result->avant = public_phraser_html_dist($result->avant, $id_parent, $boucles, $descr, $ligne_avant);
1050
		$result->apres = public_phraser_html_dist($result->apres, $id_parent, $boucles, $descr, $ligne_apres);
1051
		$result->altern = public_phraser_html_dist($result->altern, $id_parent, $boucles, $descr, $ligne_altern);
1052
		$result->postaff = public_phraser_html_dist($result->postaff, $id_parent, $boucles, $descr, $ligne_postaff);
1053
1054
		// Prevenir le generateur de code que le squelette est faux
1055
		if ($err_b) {
1056
			$result->type_requete = false;
1057
		}
1058
1059
		// Verifier qu'il n'y a pas double definition
1060
		// apres analyse des sous-parties (pas avant).
1061
1062
		if (!empty($boucles[$id_boucle])) {
1063
			$err_b_d = array(
1064
				'zbug_erreur_boucle_double',
1065
				array('id' => $id_boucle)
1066
			);
1067
			erreur_squelette($err_b_d, $result);
1068
			// Prevenir le generateur de code que le squelette est faux
1069
			$boucles[$id_boucle]->type_requete = false;
1070
		} else {
1071
			$boucles[$id_boucle] = $result;
1072
		}
1073
		$all_res = phraser_champs_etendus($debut, $ligne, $all_res);
1074
		$all_res[] = &$boucles[$id_boucle];
1075
1076
		$ligne = $ligne_suite;
1077
		$texte = $suite;
1078
	}
1079
1080
	return phraser_champs_etendus($texte, $ligne, $all_res);
1081
}
1082