|
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) { |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
427
|
|
|
$res[] = $collecte; |
|
428
|
|
|
$collecte = array(); |
|
429
|
|
|
} |
|
430
|
|
|
} |
|
431
|
|
|
} |
|
432
|
|
|
if ($collecte) { |
|
|
|
|
|
|
433
|
|
|
$res[] = $collecte; |
|
434
|
|
|
$collecte = array(); |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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; |
|
|
|
|
|
|
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]; |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
746
|
|
|
$args[] = $crit; |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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; |
|
|
|
|
|
|
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 |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
829
|
|
|
$id_boucle = substr($texte,$pos_boucle + strlen(BALISE_BOUCLE), 15); |
|
830
|
|
|
} |
|
831
|
|
|
$result->id_boucle = $id_boucle; |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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 |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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, ':')) { |
|
|
|
|
|
|
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
|
|
|
} |
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.