|
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
|
|
|
* Fonctions de recherche et de reservation dans l'arborescence des boucles |
|
15
|
|
|
* |
|
16
|
|
|
* @package SPIP\Core\Compilateur\References |
|
17
|
|
|
**/ |
|
18
|
|
|
if (!defined('_ECRIRE_INC_VERSION')) { |
|
19
|
|
|
return; |
|
20
|
|
|
} |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* Retrouver l'index de la boucle d'une balise |
|
24
|
|
|
* |
|
25
|
|
|
* Retrouve à quelle boucle appartient une balise, utile dans le cas |
|
26
|
|
|
* où une référence explicite est demandée |
|
27
|
|
|
* |
|
28
|
|
|
* - `#MABALISE` : l'index est celui de la première boucle englobante |
|
29
|
|
|
* - `#_autreboucle:MABALISE` : l'index est celui de la boucle _autreboucle |
|
30
|
|
|
* |
|
31
|
|
|
* @example |
|
32
|
|
|
* Dans une balise dynamique ou calculée : |
|
33
|
|
|
* ``` |
|
34
|
|
|
* $idb = index_boucle($p); |
|
35
|
|
|
* ``` |
|
36
|
|
|
* |
|
37
|
|
|
* @param Champ $p AST au niveau de la balise |
|
38
|
|
|
* @return string |
|
|
|
|
|
|
39
|
|
|
* |
|
40
|
|
|
* - Identifiant de la boucle possédant ce champ. |
|
41
|
|
|
* - '' si une référence explicite incorrecte est envoyée |
|
42
|
|
|
*/ |
|
43
|
|
|
function index_boucle($p) { |
|
44
|
|
View Code Duplication |
if (strlen($p->nom_boucle)) { |
|
|
|
|
|
|
45
|
|
|
// retourne l’index explicite demandé s’il existe |
|
46
|
|
|
if (!empty($p->boucles[$p->nom_boucle])) { |
|
47
|
|
|
return $p->nom_boucle; |
|
48
|
|
|
} |
|
49
|
|
|
return ''; |
|
50
|
|
|
} |
|
51
|
|
|
return $p->id_boucle; |
|
52
|
|
|
} |
|
53
|
|
|
|
|
54
|
|
|
|
|
55
|
|
|
/** |
|
56
|
|
|
* Retrouve la boucle mère d’une balise, sauf si son nom est explicité |
|
57
|
|
|
* |
|
58
|
|
|
* - `#MABALISE` : l'index sera celui de la boucle parente |
|
59
|
|
|
* - `#_autreboucle:MABALISE` : l'index est celui de la boucle _autreboucle, si elle existe |
|
60
|
|
|
* |
|
61
|
|
|
* @example |
|
62
|
|
|
* Dans une balise dynamique ou calculée : |
|
63
|
|
|
* ``` |
|
64
|
|
|
* $idb = index_boucle_mere($p); |
|
65
|
|
|
* ``` |
|
66
|
|
|
* |
|
67
|
|
|
* @param Champ $p AST au niveau de la balise |
|
68
|
|
|
* @return string |
|
69
|
|
|
* |
|
70
|
|
|
* - Identifiant de la boucle parente possédant ce champ, ou '' si pas de parent. |
|
71
|
|
|
* - '' si une référence explicite incorrecte est envoyée |
|
72
|
|
|
*/ |
|
73
|
|
|
function index_boucle_mere($p) { |
|
74
|
|
View Code Duplication |
if (strlen($p->nom_boucle)) { |
|
|
|
|
|
|
75
|
|
|
// retourne l’index explicite demandé s’il existe |
|
76
|
|
|
if (!empty($p->boucles[$p->nom_boucle])) { |
|
77
|
|
|
return $p->nom_boucle; |
|
78
|
|
|
} |
|
79
|
|
|
return ''; |
|
80
|
|
|
} |
|
81
|
|
|
if (!empty($p->descr['id_mere'])) { |
|
82
|
|
|
return $p->descr['id_mere']; |
|
83
|
|
|
} |
|
84
|
|
|
return ''; |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* Retourne la position dans la pile d'un champ SQL |
|
89
|
|
|
* |
|
90
|
|
|
* Retourne le code PHP permettant de récupérer un champ SQL dans |
|
91
|
|
|
* une boucle parente, en prenant la boucle la plus proche du sommet de pile |
|
92
|
|
|
* (indiqué par $idb). |
|
93
|
|
|
* |
|
94
|
|
|
* Si on ne trouve rien, on considère que ça doit provenir du contexte |
|
95
|
|
|
* (par l'URL ou l'include) qui a été recopié dans Pile[0] |
|
96
|
|
|
* (un essai d'affinage a débouché sur un bug vicieux) |
|
97
|
|
|
* |
|
98
|
|
|
* Si ca référence un champ SQL, on le mémorise dans la structure $boucles |
|
99
|
|
|
* afin de construire un requête SQL minimale (plutôt qu'un brutal 'SELECT *') |
|
100
|
|
|
* |
|
101
|
|
|
* @param string $idb Identifiant de la boucle |
|
102
|
|
|
* @param string $nom_champ Nom du champ SQL cherché |
|
103
|
|
|
* @param array $boucles AST du squelette |
|
104
|
|
|
* @param string $explicite |
|
105
|
|
|
* Indique que le nom de la boucle est explicite dans la balise #_nomboucletruc:CHAMP |
|
106
|
|
|
* @param null|string $defaut |
|
107
|
|
|
* Code par defaut si le champ n'est pas trouvé dans l'index. |
|
108
|
|
|
* Utilise @$Pile[0][$nom_champ] si non fourni |
|
109
|
|
|
* @param bool $remonte_pile |
|
110
|
|
|
* Permettre de remonter la pile des boucles ou non (dans ce cas on |
|
111
|
|
|
* ne cherche que danss la 1ère boucle englobante) |
|
112
|
|
|
* @param bool $select |
|
113
|
|
|
* Pour ajouter au select de la boucle, par defaut true |
|
114
|
|
|
* @return string |
|
115
|
|
|
* Code PHP pour obtenir le champ SQL |
|
116
|
|
|
*/ |
|
117
|
|
|
function index_pile( |
|
118
|
|
|
$idb, |
|
119
|
|
|
$nom_champ, |
|
120
|
|
|
&$boucles, |
|
121
|
|
|
$explicite = '', |
|
122
|
|
|
$defaut = null, |
|
123
|
|
|
$remonte_pile = true, |
|
124
|
|
|
$select = true |
|
125
|
|
|
) { |
|
126
|
|
|
if (!is_string($defaut)) { |
|
127
|
|
|
$defaut = '@$Pile[0][\'' . strtolower($nom_champ) . '\']'; |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
$i = 0; |
|
131
|
|
|
if (strlen($explicite)) { |
|
132
|
|
|
// Recherche d'un champ dans un etage superieur |
|
133
|
|
|
while (($idb !== $explicite) && ($idb !== '')) { |
|
134
|
|
|
# spip_log("Cherchexpl: $nom_champ '$explicite' '$idb' '$i'"); |
|
135
|
|
|
$i++; |
|
136
|
|
|
$idb = $boucles[$idb]->id_parent; |
|
137
|
|
|
} |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
# spip_log("Cherche: $nom_champ a partir de '$idb'"); |
|
141
|
|
|
$nom_champ = strtolower($nom_champ); |
|
142
|
|
|
$conditionnel = array(); |
|
143
|
|
|
// attention: entre la boucle nommee 0, "" et le tableau vide, |
|
144
|
|
|
// il y a incoherences qu'il vaut mieux eviter |
|
145
|
|
|
while (isset($boucles[$idb])) { |
|
146
|
|
|
$joker = true; |
|
147
|
|
|
// modifie $joker si tous les champs sont autorisés. |
|
148
|
|
|
// $t = le select pour le champ, si on l'a trouvé (ou si joker) |
|
149
|
|
|
// $c = le nom du champ demandé |
|
150
|
|
|
list($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles, $joker); |
|
|
|
|
|
|
151
|
|
|
if ($t) { |
|
152
|
|
View Code Duplication |
if ($select and !in_array($t, $boucles[$idb]->select)) { |
|
|
|
|
|
|
153
|
|
|
$boucles[$idb]->select[] = $t; |
|
154
|
|
|
} |
|
155
|
|
|
$champ = '$Pile[$SP' . ($i ? "-$i" : "") . '][\'' . $c . '\']'; |
|
156
|
|
|
if (!$joker) { |
|
157
|
|
|
return index_compose($conditionnel, $champ); |
|
158
|
|
|
} |
|
159
|
|
|
|
|
160
|
|
|
// tant que l'on trouve des tables avec joker, on continue |
|
161
|
|
|
// avec la boucle parente et on conditionne à l'exécution |
|
162
|
|
|
// la présence du champ. Si le champ existe à l'exécution |
|
163
|
|
|
// dans une boucle, il est pris, sinon on le cherche dans le parent... |
|
164
|
|
|
$conditionnel[] = "isset($champ)?$champ"; |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
if ($remonte_pile) { |
|
168
|
|
|
# spip_log("On remonte vers $i"); |
|
169
|
|
|
// Sinon on remonte d'un cran |
|
170
|
|
|
$idb = $boucles[$idb]->id_parent; |
|
171
|
|
|
$i++; |
|
172
|
|
|
} else { |
|
173
|
|
|
$idb = null; |
|
174
|
|
|
} |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
# spip_log("Pas vu $nom_champ"); |
|
178
|
|
|
// esperons qu'il y sera |
|
179
|
|
|
// ou qu'on a fourni une valeur par "defaut" plus pertinent |
|
180
|
|
|
return index_compose($conditionnel, $defaut); |
|
181
|
|
|
} |
|
182
|
|
|
|
|
183
|
|
|
/** |
|
184
|
|
|
* Reconstuire la cascade de condition de recherche d'un champ |
|
185
|
|
|
* |
|
186
|
|
|
* On ajoute la valeur finale par défaut pour les balises dont on ne saura |
|
187
|
|
|
* qu'à l'exécution si elles sont definies ou non (boucle DATA) |
|
188
|
|
|
* |
|
189
|
|
|
* @param array $conditionnel Liste de codes PHP pour retrouver un champ |
|
190
|
|
|
* @param string $defaut Valeur par défaut si aucun des moyens ne l'a trouvé |
|
191
|
|
|
* @return string Code PHP complet de recherche d'un champ |
|
192
|
|
|
*/ |
|
193
|
|
|
function index_compose($conditionnel, $defaut) { |
|
194
|
|
|
while ($c = array_pop($conditionnel)) { |
|
195
|
|
|
// si on passe defaut = '', ne pas générer d'erreur de compilation. |
|
196
|
|
|
$defaut = "($c:(" . ($defaut ? $defaut : "''") . "))"; |
|
197
|
|
|
} |
|
198
|
|
|
|
|
199
|
|
|
return $defaut; |
|
200
|
|
|
} |
|
201
|
|
|
|
|
202
|
|
|
/** |
|
203
|
|
|
* Cherche un champ dans une boucle |
|
204
|
|
|
* |
|
205
|
|
|
* Le champ peut être : |
|
206
|
|
|
* |
|
207
|
|
|
* - un alias d'un autre : il faut alors le calculer, éventuellement en |
|
208
|
|
|
* construisant une jointure. |
|
209
|
|
|
* - présent dans la table : on l'utilise |
|
210
|
|
|
* - absent, mais le type de boucle l'autorise (joker des itérateurs DATA) : |
|
211
|
|
|
* on l'utilise et lève le drapeau joker |
|
212
|
|
|
* - absent, on cherche une jointure et on l'utilise si on en trouve. |
|
213
|
|
|
* |
|
214
|
|
|
* @todo |
|
215
|
|
|
* Ici la recherche de jointure sur l'absence d'un champ ne cherche |
|
216
|
|
|
* une jointure que si des jointures explicites sont demandées, |
|
217
|
|
|
* et non comme à d'autres endroits sur toutes les jointures possibles. |
|
218
|
|
|
* Il faut homogénéiser cela. |
|
219
|
|
|
* |
|
220
|
|
|
* |
|
221
|
|
|
* @param string $idb Identifiant de la boucle |
|
222
|
|
|
* @param string $nom_champ Nom du champ SQL cherché |
|
223
|
|
|
* @param Boucle $boucles AST du squelette |
|
224
|
|
|
* @param bool $joker |
|
225
|
|
|
* Le champ peut-il être inconnu à la compilation ? |
|
226
|
|
|
* Ce drapeau sera levé si c'est le cas. |
|
227
|
|
|
* @return array |
|
228
|
|
|
* Liste (Nom du champ véritable, nom du champ demandé). |
|
229
|
|
|
* Le nom du champ véritable est une expression pour le SELECT de |
|
230
|
|
|
* la boucle tel que "rubriques.titre" ou "mots.titre AS titre_mot". |
|
231
|
|
|
* Les éléments de la liste sont vides si on ne trouve rien. |
|
232
|
|
|
**/ |
|
233
|
|
|
function index_tables_en_pile($idb, $nom_champ, &$boucles, &$joker) { |
|
234
|
|
|
|
|
235
|
|
|
$r = $boucles[$idb]->type_requete; |
|
236
|
|
|
// boucle recursive, c'est foutu... |
|
237
|
|
|
if ($r == TYPE_RECURSIF) { |
|
238
|
|
|
return array(); |
|
239
|
|
|
} |
|
240
|
|
|
if (!$r) { |
|
241
|
|
|
$joker = false; // indiquer a l'appelant |
|
242
|
|
|
# continuer pour chercher l'erreur suivante |
|
243
|
|
|
return array("'#" . $r . ':' . $nom_champ . "'", ''); |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
$desc = $boucles[$idb]->show; |
|
247
|
|
|
// le nom du champ est il une exception de la table ? un alias ? |
|
248
|
|
|
$excep = isset($GLOBALS['exceptions_des_tables'][$r]) ? $GLOBALS['exceptions_des_tables'][$r] : ''; |
|
249
|
|
|
if ($excep) { |
|
250
|
|
|
$excep = isset($excep[$nom_champ]) ? $excep[$nom_champ] : ''; |
|
251
|
|
|
} |
|
252
|
|
|
if ($excep) { |
|
253
|
|
|
$joker = false; // indiquer a l'appelant |
|
254
|
|
|
return index_exception($boucles[$idb], $desc, $nom_champ, $excep); |
|
255
|
|
|
} // pas d'alias. Le champ existe t'il ? |
|
256
|
|
|
else { |
|
257
|
|
|
// le champ est réellement présent, on le prend. |
|
258
|
|
|
if (isset($desc['field'][$nom_champ])) { |
|
259
|
|
|
$t = $boucles[$idb]->id_table; |
|
260
|
|
|
$joker = false; // indiquer a l'appelant |
|
261
|
|
|
return array("$t.$nom_champ", $nom_champ); |
|
262
|
|
|
} |
|
263
|
|
|
// Tous les champs sont-ils acceptés ? |
|
264
|
|
|
// Si oui, on retourne le champ, et on lève le flag joker |
|
265
|
|
|
// C'est le cas des itérateurs DATA qui acceptent tout |
|
266
|
|
|
// et testent la présence du champ à l'exécution et non à la compilation |
|
267
|
|
|
// car ils ne connaissent pas ici leurs contenus. |
|
268
|
|
|
elseif (/*$joker AND */ |
|
269
|
|
|
isset($desc['field']['*']) |
|
270
|
|
|
) { |
|
271
|
|
|
$joker = true; // indiquer a l'appelant |
|
272
|
|
|
return array($nom_champ, $nom_champ); |
|
273
|
|
|
} |
|
274
|
|
|
// pas d'alias, pas de champ, pas de joker... |
|
275
|
|
|
// tenter via une jointure... |
|
276
|
|
|
else { |
|
277
|
|
|
$joker = false; // indiquer a l'appelant |
|
278
|
|
|
// regarder si le champ est deja dans une jointure existante |
|
279
|
|
|
// sinon, si il y a des joitures explicites, la construire |
|
280
|
|
|
if (!$t = trouver_champ_exterieur($nom_champ, $boucles[$idb]->from, $boucles[$idb])) { |
|
281
|
|
|
if ($boucles[$idb]->jointures_explicites) { |
|
282
|
|
|
// [todo] Ne pas lancer que lorsque il y a des jointures explicites !!!! |
|
283
|
|
|
// fonctionnel, il suffit d'utiliser $boucles[$idb]->jointures au lieu de jointures_explicites |
|
284
|
|
|
// mais est-ce ce qu'on veut ? |
|
285
|
|
|
$jointures = preg_split("/\s+/", $boucles[$idb]->jointures_explicites); |
|
286
|
|
|
if ($cle = trouver_jointure_champ($nom_champ, $boucles[$idb], $jointures)) { |
|
|
|
|
|
|
287
|
|
|
$t = trouver_champ_exterieur($nom_champ, $boucles[$idb]->from, $boucles[$idb]); |
|
288
|
|
|
} |
|
289
|
|
|
} |
|
290
|
|
|
} |
|
291
|
|
|
if ($t) { |
|
292
|
|
|
// si on a trouvé une jointure possible, on fait comme |
|
293
|
|
|
// si c'était une exception pour le champ demandé |
|
294
|
|
|
return index_exception($boucles[$idb], |
|
295
|
|
|
$desc, |
|
296
|
|
|
$nom_champ, |
|
297
|
|
|
array($t[1]['id_table'], reset($t[2]))); |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
return array('', ''); |
|
301
|
|
|
} |
|
302
|
|
|
} |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
|
|
306
|
|
|
/** |
|
307
|
|
|
* Retrouve un alias d'un champ dans une boucle |
|
308
|
|
|
* |
|
309
|
|
|
* Référence à une entite SPIP alias d'un champ SQL. |
|
310
|
|
|
* Ça peut même être d'un champ dans une jointure qu'il faut provoquer |
|
311
|
|
|
* si ce n'est fait |
|
312
|
|
|
* |
|
313
|
|
|
* @param Boucle $boucle Boucle dont on prend un alias de champ |
|
314
|
|
|
* @param array $desc Description de la table SQL de la boucle |
|
315
|
|
|
* @param string $nom_champ Nom du champ original demandé |
|
316
|
|
|
* @param array $excep |
|
317
|
|
|
* Description de l'exception pour ce champ. Peut être : |
|
318
|
|
|
* |
|
319
|
|
|
* - string : nom du champ véritable dans la table |
|
320
|
|
|
* - array : |
|
321
|
|
|
* - liste (table, champ) indique que le véritable champ |
|
322
|
|
|
* est dans une autre table et construit la jointure dessus |
|
323
|
|
|
* - liste (table, champ, fonction) idem, mais en passant un |
|
324
|
|
|
* nom de fonction qui s'occupera de créer la jointure. |
|
325
|
|
|
* @return array |
|
|
|
|
|
|
326
|
|
|
* Liste (nom du champ alias, nom du champ). Le nom du champ alias |
|
327
|
|
|
* est une expression pour le SELECT de la boucle du style "mots.titre AS titre_mot" |
|
328
|
|
|
**/ |
|
329
|
|
|
function index_exception(&$boucle, $desc, $nom_champ, $excep) { |
|
330
|
|
|
static $trouver_table; |
|
331
|
|
|
if (!$trouver_table) { |
|
332
|
|
|
$trouver_table = charger_fonction('trouver_table', 'base'); |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
|
if (is_array($excep)) { |
|
336
|
|
|
// permettre aux plugins de gerer eux meme des jointures derogatoire ingerables |
|
337
|
|
|
$t = null; |
|
338
|
|
|
if (count($excep) == 3) { |
|
339
|
|
|
$index_exception_derogatoire = array_pop($excep); |
|
340
|
|
|
$t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep); |
|
341
|
|
|
} |
|
342
|
|
|
if ($t == null) { |
|
343
|
|
|
list($e, $x) = $excep; #PHP4 affecte de gauche a droite |
|
344
|
|
|
$excep = $x; #PHP5 de droite a gauche ! |
|
345
|
|
|
$j = $trouver_table($e, $boucle->sql_serveur); |
|
346
|
|
|
if (!$j) { |
|
347
|
|
|
return array('', ''); |
|
348
|
|
|
} |
|
349
|
|
|
$e = $j['table']; |
|
350
|
|
|
if (!$t = array_search($e, $boucle->from)) { |
|
351
|
|
|
$k = $j['key']['PRIMARY KEY']; |
|
352
|
|
|
if (strpos($k, ',')) { |
|
353
|
|
|
$l = (preg_split('/\s*,\s*/', $k)); |
|
354
|
|
|
$k = $desc['key']['PRIMARY KEY']; |
|
355
|
|
|
if (!in_array($k, $l)) { |
|
356
|
|
|
spip_log("jointure impossible $e " . join(',', $l)); |
|
357
|
|
|
|
|
358
|
|
|
return array('', ''); |
|
359
|
|
|
} |
|
360
|
|
|
} |
|
361
|
|
|
$k = array($boucle->id_table, array($e), $k); |
|
362
|
|
|
fabrique_jointures($boucle, array($k)); |
|
363
|
|
|
$t = array_search($e, $boucle->from); |
|
364
|
|
|
} |
|
365
|
|
|
} |
|
366
|
|
|
} else { |
|
367
|
|
|
$t = $boucle->id_table; |
|
368
|
|
|
} |
|
369
|
|
|
// demander a SQL de gerer le synonyme |
|
370
|
|
|
// ca permet que excep soit dynamique (Cedric, 2/3/06) |
|
371
|
|
|
if ($excep != $nom_champ) { |
|
372
|
|
|
$excep .= ' AS ' . $nom_champ; |
|
373
|
|
|
} |
|
374
|
|
|
|
|
375
|
|
|
return array("$t.$excep", $nom_champ); |
|
376
|
|
|
} |
|
377
|
|
|
|
|
378
|
|
|
/** |
|
379
|
|
|
* Demande le champ '$champ' dans la pile |
|
380
|
|
|
* |
|
381
|
|
|
* Le champ est cherché dans l'empilement de boucles, sinon dans la valeur |
|
382
|
|
|
* par défaut (qui est l'environnement du squelette si on ne la précise pas). |
|
383
|
|
|
* |
|
384
|
|
|
* @api |
|
385
|
|
|
* @param string $champ |
|
386
|
|
|
* Champ recherché |
|
387
|
|
|
* @param Champ $p |
|
388
|
|
|
* AST au niveau de la balise |
|
389
|
|
|
* @param null|string $defaut |
|
390
|
|
|
* Code de la valeur par défaut si on ne trouve pas le champ dans une |
|
391
|
|
|
* des boucles parentes. Sans précision, il sera pris dans l'environnement |
|
392
|
|
|
* du squelette. |
|
393
|
|
|
* Passer $defaut = '' pour ne pas prendre l'environnement. |
|
394
|
|
|
* @param bool $remonte_pile |
|
395
|
|
|
* Permettre de remonter dans la pile des boucles pour trouver le champ |
|
396
|
|
|
* @return string |
|
397
|
|
|
* Code PHP pour retrouver le champ |
|
398
|
|
|
*/ |
|
399
|
|
|
function champ_sql($champ, $p, $defaut = null, $remonte_pile = true) { |
|
400
|
|
|
return index_pile($p->id_boucle, $champ, $p->boucles, $p->nom_boucle, $defaut, $remonte_pile); |
|
401
|
|
|
} |
|
402
|
|
|
|
|
403
|
|
|
|
|
404
|
|
|
/** |
|
405
|
|
|
* Calcule et retourne le code PHP d'exécution d'une balise SPIP et des ses filtres |
|
406
|
|
|
* |
|
407
|
|
|
* Cette fonction qui sert d'API au compilateur demande à calculer |
|
408
|
|
|
* le code PHP d'une balise, puis lui applique les filtres (automatiques |
|
409
|
|
|
* et décrits dans le squelette) |
|
410
|
|
|
* |
|
411
|
|
|
* @uses calculer_balise() |
|
412
|
|
|
* @uses applique_filtres() |
|
413
|
|
|
* |
|
414
|
|
|
* @param Champ $p |
|
415
|
|
|
* AST au niveau de la balise |
|
416
|
|
|
* @return string |
|
417
|
|
|
* Code PHP pour d'exécution de la balise et de ses filtres |
|
418
|
|
|
**/ |
|
419
|
|
|
function calculer_champ($p) { |
|
420
|
|
|
$p = calculer_balise($p->nom_champ, $p); |
|
421
|
|
|
|
|
422
|
|
|
return applique_filtres($p); |
|
423
|
|
|
} |
|
424
|
|
|
|
|
425
|
|
|
|
|
426
|
|
|
/** |
|
427
|
|
|
* Calcule et retourne le code PHP d'exécution d'une balise SPIP |
|
428
|
|
|
* |
|
429
|
|
|
* Cette fonction qui sert d'API au compilateur demande à calculer |
|
430
|
|
|
* le code PHP d'une balise (cette fonction ne calcule pas les éventuels |
|
431
|
|
|
* filtres de la balise). |
|
432
|
|
|
* |
|
433
|
|
|
* Pour une balise nommmée `NOM`, elle demande à `charger_fonction()` de chercher |
|
434
|
|
|
* s'il existe une fonction `balise_NOM` ou `balise_NOM_dist` |
|
435
|
|
|
* éventuellement en chargeant le fichier `balise/NOM.php.` |
|
436
|
|
|
* |
|
437
|
|
|
* Si la balise est de la forme `PREFIXE_SUFFIXE` (cf `LOGO_*` et `URL_*`) |
|
438
|
|
|
* elle fait de même avec juste le `PREFIXE`. |
|
439
|
|
|
* |
|
440
|
|
|
* S'il n'y a pas de fonction trouvée, on considère la balise comme une référence |
|
441
|
|
|
* à une colonne de table SQL connue, sinon à l'environnement (cf. `calculer_balise_DEFAUT_dist()`). |
|
442
|
|
|
* |
|
443
|
|
|
* Les surcharges des colonnes SQL via charger_fonction sont donc possibles. |
|
444
|
|
|
* |
|
445
|
|
|
* @uses calculer_balise_DEFAUT_dist() |
|
446
|
|
|
* Lorsqu'aucune fonction spécifique n'est trouvée. |
|
447
|
|
|
* @see charger_fonction() |
|
448
|
|
|
* Pour la recherche des fonctions de balises |
|
449
|
|
|
* |
|
450
|
|
|
* @param string $nom |
|
451
|
|
|
* Nom de la balise |
|
452
|
|
|
* @param Champ $p |
|
453
|
|
|
* AST au niveau de la balise |
|
454
|
|
|
* @return Champ |
|
455
|
|
|
* Pile complétée par le code PHP pour l'exécution de la balise et de ses filtres |
|
456
|
|
|
**/ |
|
457
|
|
|
function calculer_balise($nom, $p) { |
|
458
|
|
|
|
|
459
|
|
|
// S'agit-t-il d'une balise_XXXX[_dist]() ? |
|
460
|
|
|
if ($f = charger_fonction($nom, 'balise', true)) { |
|
461
|
|
|
$p->balise_calculee = true; |
|
462
|
|
|
$res = $f($p); |
|
463
|
|
|
if ($res !== null and is_object($res)) { |
|
464
|
|
|
return $res; |
|
465
|
|
|
} |
|
466
|
|
|
} |
|
467
|
|
|
|
|
468
|
|
|
// Certaines des balises comportant un _ sont generiques |
|
469
|
|
|
if ($balise_generique = chercher_balise_generique($nom)) { |
|
470
|
|
|
$res = $balise_generique['fonction_generique']($p); |
|
471
|
|
|
if ($res !== null and is_object($res)) { |
|
472
|
|
|
return $res; |
|
473
|
|
|
} |
|
474
|
|
|
} |
|
475
|
|
|
|
|
476
|
|
|
$f = charger_fonction('DEFAUT', 'calculer_balise'); |
|
477
|
|
|
|
|
478
|
|
|
return $f($nom, $p); |
|
479
|
|
|
} |
|
480
|
|
|
|
|
481
|
|
|
|
|
482
|
|
|
/** |
|
483
|
|
|
* Calcule et retourne le code PHP d'exécution d'une balise SPIP non déclarée |
|
484
|
|
|
* |
|
485
|
|
|
* Cette fonction demande à calculer le code PHP d'une balise qui |
|
486
|
|
|
* n'a pas de fonction spécifique. |
|
487
|
|
|
* |
|
488
|
|
|
* On considère la balise comme une référence à une colonne de table SQL |
|
489
|
|
|
* connue, sinon à l'environnement. |
|
490
|
|
|
* |
|
491
|
|
|
* @uses index_pile() |
|
492
|
|
|
* Pour la recherche de la balise comme colonne SQL ou comme environnement |
|
493
|
|
|
* @note |
|
494
|
|
|
* Le texte de la balise est retourné si il ressemble à une couleur |
|
495
|
|
|
* et qu'aucun champ correspondant n'a été trouvé, comme `#CCAABB` |
|
496
|
|
|
* |
|
497
|
|
|
* @param string $nom |
|
498
|
|
|
* Nom de la balise |
|
499
|
|
|
* @param Champ $p |
|
500
|
|
|
* AST au niveau de la balise |
|
501
|
|
|
* @return string |
|
|
|
|
|
|
502
|
|
|
* Code PHP pour d'exécution de la balise et de ses filtres |
|
503
|
|
|
**/ |
|
504
|
|
|
function calculer_balise_DEFAUT_dist($nom, $p) { |
|
505
|
|
|
|
|
506
|
|
|
// ca pourrait etre un champ SQL homonyme, |
|
507
|
|
|
$p->code = index_pile($p->id_boucle, $nom, $p->boucles, $p->nom_boucle); |
|
508
|
|
|
|
|
509
|
|
|
// compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour |
|
510
|
|
|
// il faut recracher {...} quand ce n'est finalement pas des args |
|
511
|
|
|
if ($p->fonctions and (!$p->fonctions[0][0]) and $p->fonctions[0][1]) { |
|
512
|
|
|
$code = addslashes($p->fonctions[0][1]); |
|
513
|
|
|
$p->code .= " . '$code'"; |
|
514
|
|
|
} |
|
515
|
|
|
|
|
516
|
|
|
// ne pas passer le filtre securite sur les id_xxx |
|
517
|
|
|
if (strpos($nom, 'ID_') === 0) { |
|
518
|
|
|
$p->interdire_scripts = false; |
|
519
|
|
|
} |
|
520
|
|
|
|
|
521
|
|
|
// Compatibilite ascendante avec les couleurs html (#FEFEFE) : |
|
522
|
|
|
// SI le champ SQL n'est pas trouve |
|
523
|
|
|
// ET si la balise a une forme de couleur |
|
524
|
|
|
// ET s'il n'y a ni filtre ni etoile |
|
525
|
|
|
// ALORS retourner la couleur. |
|
526
|
|
|
// Ca permet si l'on veut vraiment de recuperer [(#ACCEDE*)] |
|
527
|
|
|
if (preg_match("/^[A-F]{1,6}$/i", $nom) |
|
528
|
|
|
and !$p->etoile |
|
529
|
|
|
and !$p->fonctions |
|
530
|
|
|
) { |
|
531
|
|
|
$p->code = "'#$nom'"; |
|
532
|
|
|
$p->interdire_scripts = false; |
|
533
|
|
|
} |
|
534
|
|
|
|
|
535
|
|
|
return $p; |
|
536
|
|
|
} |
|
537
|
|
|
|
|
538
|
|
|
|
|
539
|
|
|
/** Code PHP d'exécution d'une balise dynamique */ |
|
540
|
|
|
define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s', |
|
541
|
|
|
array(%s%s), |
|
542
|
|
|
array(%s%s))"); |
|
543
|
|
|
|
|
544
|
|
|
|
|
545
|
|
|
/** |
|
546
|
|
|
* Calcule le code PHP d'exécution d'une balise SPIP dynamique |
|
547
|
|
|
* |
|
548
|
|
|
* Calcule les balises dynamiques, notamment les `formulaire_*`. |
|
549
|
|
|
* |
|
550
|
|
|
* Inclut le fichier associé à son nom, qui contient la fonction homonyme |
|
551
|
|
|
* donnant les arguments à chercher dans la pile, et qui sont donc compilés. |
|
552
|
|
|
* |
|
553
|
|
|
* On leur adjoint les arguments explicites de la balise (cf `#LOGIN{url}`) |
|
554
|
|
|
* et d'éventuelles valeurs transmises d'autorité par la balise. |
|
555
|
|
|
* (cf https://core.spip.net/issues/1728) |
|
556
|
|
|
* |
|
557
|
|
|
* La fonction `executer_balise_dynamique()` définie par la |
|
558
|
|
|
* constante `CODE_EXECUTER_BALISE` recevra à l'exécution la valeur de tout ca. |
|
559
|
|
|
* |
|
560
|
|
|
* @uses collecter_balise_dynamique() |
|
561
|
|
|
* Qui calcule le code d'exécution de chaque argument de la balise |
|
562
|
|
|
* @see executer_balise_dynamique() |
|
563
|
|
|
* Code PHP produit qui chargera les fonctions de la balise dynamique à l'exécution, |
|
564
|
|
|
* appelée avec les arguments calculés. |
|
565
|
|
|
* @param Champ $p |
|
566
|
|
|
* AST au niveau de la balise |
|
567
|
|
|
* @param string $nom |
|
568
|
|
|
* Nom de la balise dynamique |
|
569
|
|
|
* @param array $l |
|
570
|
|
|
* Liste des noms d'arguments (balises) à collecter |
|
571
|
|
|
* @param array $supp |
|
572
|
|
|
* Liste de données supplémentaires à transmettre au code d'exécution. |
|
573
|
|
|
* @return Champ |
|
574
|
|
|
* Balise complétée de son code d'exécution |
|
575
|
|
|
**/ |
|
576
|
|
|
function calculer_balise_dynamique($p, $nom, $l, $supp = array()) { |
|
577
|
|
|
|
|
578
|
|
|
if (!balise_distante_interdite($p)) { |
|
579
|
|
|
$p->code = "''"; |
|
580
|
|
|
|
|
581
|
|
|
return $p; |
|
582
|
|
|
} |
|
583
|
|
|
// compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour |
|
584
|
|
|
// il faut recracher {...} quand ce n'est finalement pas des args |
|
585
|
|
|
if ($p->fonctions and (!$p->fonctions[0][0]) and $p->fonctions[0][1]) { |
|
586
|
|
|
$p->fonctions = null; |
|
587
|
|
|
} |
|
588
|
|
|
|
|
589
|
|
|
if ($p->param and ($c = $p->param[0])) { |
|
590
|
|
|
// liste d'arguments commence toujours par la chaine vide |
|
591
|
|
|
array_shift($c); |
|
592
|
|
|
// construire la liste d'arguments comme pour un filtre |
|
593
|
|
|
$param = compose_filtres_args($p, $c, ','); |
|
594
|
|
|
} else { |
|
595
|
|
|
$param = ""; |
|
596
|
|
|
} |
|
597
|
|
|
$collecte = collecter_balise_dynamique($l, $p, $nom); |
|
598
|
|
|
|
|
599
|
|
|
$p->code = sprintf(CODE_EXECUTER_BALISE, $nom, |
|
600
|
|
|
join(',', $collecte), |
|
601
|
|
|
($collecte ? $param : substr($param, 1)), # virer la virgule |
|
602
|
|
|
memoriser_contexte_compil($p), |
|
603
|
|
|
(!$supp ? '' : (', ' . join(',', $supp)))); |
|
604
|
|
|
|
|
605
|
|
|
$p->interdire_scripts = false; |
|
606
|
|
|
|
|
607
|
|
|
return $p; |
|
608
|
|
|
} |
|
609
|
|
|
|
|
610
|
|
|
|
|
611
|
|
|
/** |
|
612
|
|
|
* Construction du tableau des arguments d'une balise dynamique. |
|
613
|
|
|
* |
|
614
|
|
|
* Pour chaque argument (un nom de balise), crée le code PHP qui le calculera. |
|
615
|
|
|
* |
|
616
|
|
|
* @note |
|
617
|
|
|
* Ces arguments peuvent être eux-même des balises (cf FORMULAIRE_SIGNATURE) |
|
618
|
|
|
* mais gare au bouclage (on peut s'aider de `$nom` pour le réperer au besoin) |
|
619
|
|
|
* |
|
620
|
|
|
* En revanche ils n'ont pas de filtres, donc on appelle `calculer_balise()` qui |
|
621
|
|
|
* ne s'occupe pas de ce qu'il y a dans `$p` (mais qui va y ecrire le code) |
|
622
|
|
|
* |
|
623
|
|
|
* @uses calculer_balise() |
|
624
|
|
|
* Pour obtenir le code d'éxécution de chaque argument. |
|
625
|
|
|
* |
|
626
|
|
|
* @param array $l |
|
627
|
|
|
* Liste des noms d'arguments (balises) à collecter (chaque argument |
|
628
|
|
|
* de la balise dynamique est considéré comme étant un nom de balise) |
|
629
|
|
|
* @param Champ $p |
|
630
|
|
|
* AST au niveau de la balise |
|
631
|
|
|
* @param string $nom |
|
632
|
|
|
* Nom de la balise |
|
633
|
|
|
* @return array |
|
634
|
|
|
* Liste des codes PHP d'éxecution des balises collectées |
|
635
|
|
|
**/ |
|
636
|
|
|
function collecter_balise_dynamique($l, &$p, $nom) { |
|
|
|
|
|
|
637
|
|
|
$args = array(); |
|
638
|
|
|
foreach ($l as $c) { |
|
639
|
|
|
$x = calculer_balise($c, $p); |
|
640
|
|
|
$args[] = $x->code; |
|
641
|
|
|
} |
|
642
|
|
|
|
|
643
|
|
|
return $args; |
|
644
|
|
|
} |
|
645
|
|
|
|
|
646
|
|
|
|
|
647
|
|
|
/** |
|
648
|
|
|
* Récuperer le nom du serveur |
|
649
|
|
|
* |
|
650
|
|
|
* Mais pas si c'est un serveur spécifique dérogatoire |
|
651
|
|
|
* |
|
652
|
|
|
* @param Champ $p |
|
653
|
|
|
* AST positionné sur la balise |
|
654
|
|
|
* @return string |
|
655
|
|
|
* Nom de la connexion |
|
656
|
|
|
**/ |
|
657
|
|
|
function trouver_nom_serveur_distant($p) { |
|
658
|
|
|
$nom = $p->id_boucle; |
|
659
|
|
|
if ($nom |
|
660
|
|
|
and isset($p->boucles[$nom]) |
|
661
|
|
|
) { |
|
662
|
|
|
$s = $p->boucles[$nom]->sql_serveur; |
|
663
|
|
|
if (strlen($s) |
|
664
|
|
|
and strlen($serveur = strtolower($s)) |
|
665
|
|
|
and !in_array($serveur, $GLOBALS['exception_des_connect']) |
|
666
|
|
|
) { |
|
667
|
|
|
return $serveur; |
|
668
|
|
|
} |
|
669
|
|
|
} |
|
670
|
|
|
|
|
671
|
|
|
return ""; |
|
672
|
|
|
} |
|
673
|
|
|
|
|
674
|
|
|
|
|
675
|
|
|
/** |
|
676
|
|
|
* Teste si une balise est appliquée sur une base distante |
|
677
|
|
|
* |
|
678
|
|
|
* La fonction loge une erreur si la balise est utilisée sur une |
|
679
|
|
|
* base distante et retourne false dans ce cas. |
|
680
|
|
|
* |
|
681
|
|
|
* @note |
|
682
|
|
|
* Il faudrait savoir traiter les formulaires en local |
|
683
|
|
|
* tout en appelant le serveur SQL distant. |
|
684
|
|
|
* En attendant, cette fonction permet de refuser une authentification |
|
685
|
|
|
* sur quelque-chose qui n'a rien a voir. |
|
686
|
|
|
* |
|
687
|
|
|
* @param Champ $p |
|
688
|
|
|
* AST positionné sur la balise |
|
689
|
|
|
* @return bool |
|
690
|
|
|
* |
|
691
|
|
|
* - true : La balise est autorisée |
|
692
|
|
|
* - false : La balise est interdite car le serveur est distant |
|
693
|
|
|
**/ |
|
694
|
|
|
function balise_distante_interdite($p) { |
|
695
|
|
|
$nom = $p->id_boucle; |
|
696
|
|
|
|
|
697
|
|
|
if ($nom and trouver_nom_serveur_distant($p)) { |
|
698
|
|
|
spip_log($nom . ':' . $p->nom_champ . ' ' . _T('zbug_distant_interdit')); |
|
699
|
|
|
|
|
700
|
|
|
return false; |
|
701
|
|
|
} |
|
702
|
|
|
|
|
703
|
|
|
return true; |
|
704
|
|
|
} |
|
705
|
|
|
|
|
706
|
|
|
|
|
707
|
|
|
// |
|
708
|
|
|
// Traitements standard de divers champs |
|
709
|
|
|
// definis par $table_des_traitements, cf. ecrire/public/interfaces |
|
710
|
|
|
// |
|
711
|
|
|
// https://code.spip.net/@champs_traitements |
|
712
|
|
|
function champs_traitements($p) { |
|
713
|
|
|
|
|
714
|
|
|
if (isset($GLOBALS['table_des_traitements'][$p->nom_champ])) { |
|
715
|
|
|
$ps = $GLOBALS['table_des_traitements'][$p->nom_champ]; |
|
716
|
|
|
} else { |
|
717
|
|
|
// quand on utilise un traitement catch-all * |
|
718
|
|
|
// celui-ci ne s'applique pas sur les balises calculees qui peuvent gerer |
|
719
|
|
|
// leur propre securite |
|
720
|
|
|
if (!$p->balise_calculee) { |
|
721
|
|
|
$ps = $GLOBALS['table_des_traitements']['*']; |
|
722
|
|
|
} else { |
|
723
|
|
|
$ps = false; |
|
724
|
|
|
} |
|
725
|
|
|
} |
|
726
|
|
|
|
|
727
|
|
|
if (is_array($ps)) { |
|
728
|
|
|
// Recuperer le type de boucle (articles, DATA) et la table SQL sur laquelle elle porte |
|
729
|
|
|
$idb = index_boucle($p); |
|
730
|
|
|
// mais on peut aussi etre hors boucle. Se mefier. |
|
731
|
|
|
$type_requete = isset($p->boucles[$idb]->type_requete) ? $p->boucles[$idb]->type_requete : false; |
|
732
|
|
|
$table_sql = isset($p->boucles[$idb]->show['table_sql']) ? $p->boucles[$idb]->show['table_sql'] : false; |
|
733
|
|
|
|
|
734
|
|
|
// bien prendre en compte les alias de boucles (hierarchie => rubrique, syndication => syncdic, etc.) |
|
735
|
|
|
if ($type_requete and isset($GLOBALS['table_des_tables'][$type_requete])) { |
|
736
|
|
|
$type_alias = $type_requete; |
|
737
|
|
|
$type_requete = $GLOBALS['table_des_tables'][$type_requete]; |
|
738
|
|
|
} else { |
|
739
|
|
|
$type_alias = false; |
|
740
|
|
|
} |
|
741
|
|
|
|
|
742
|
|
|
// le traitement peut n'etre defini que pour une table en particulier "spip_articles" |
|
743
|
|
|
if ($table_sql and isset($ps[$table_sql])) { |
|
744
|
|
|
$ps = $ps[$table_sql]; |
|
745
|
|
|
} // ou pour une boucle en particulier "DATA","articles" |
|
746
|
|
|
elseif ($type_requete and isset($ps[$type_requete])) { |
|
747
|
|
|
$ps = $ps[$type_requete]; |
|
748
|
|
|
} // ou pour une boucle utilisant un alias ("hierarchie") |
|
749
|
|
|
elseif ($type_alias and isset($ps[$type_alias])) { |
|
750
|
|
|
$ps = $ps[$type_alias]; |
|
751
|
|
|
} // ou pour indifféremment quelle que soit la boucle |
|
752
|
|
|
elseif (isset($ps[0])) { |
|
753
|
|
|
$ps = $ps[0]; |
|
754
|
|
|
} else { |
|
755
|
|
|
$ps = false; |
|
756
|
|
|
} |
|
757
|
|
|
} |
|
758
|
|
|
|
|
759
|
|
|
if (!$ps) { |
|
760
|
|
|
return $p->code; |
|
761
|
|
|
} |
|
762
|
|
|
|
|
763
|
|
|
// Si une boucle DOCUMENTS{doublons} est presente dans le squelette, |
|
764
|
|
|
// ou si in INCLURE contient {doublons} |
|
765
|
|
|
// on insere une fonction de remplissage du tableau des doublons |
|
766
|
|
|
// dans les filtres propre() ou typo() |
|
767
|
|
|
// (qui traitent les raccourcis <docXX> referencant les docs) |
|
768
|
|
|
|
|
769
|
|
|
if (isset($p->descr['documents']) |
|
770
|
|
|
and |
|
771
|
|
|
$p->descr['documents'] |
|
772
|
|
|
and ( |
|
773
|
|
|
(strpos($ps, 'propre') !== false) |
|
774
|
|
|
or |
|
775
|
|
|
(strpos($ps, 'typo') !== false) |
|
776
|
|
|
) |
|
777
|
|
|
) { |
|
778
|
|
|
$ps = 'traiter_doublons_documents($doublons, ' . $ps . ')'; |
|
779
|
|
|
} |
|
780
|
|
|
|
|
781
|
|
|
// La protection des champs par |safehtml est assuree par les extensions |
|
782
|
|
|
// dans la declaration des traitements des champs sensibles |
|
783
|
|
|
|
|
784
|
|
|
// Remplacer enfin le placeholder %s par le vrai code de la balise |
|
785
|
|
|
return str_replace('%s', $p->code, $ps); |
|
786
|
|
|
} |
|
787
|
|
|
|
|
788
|
|
|
|
|
789
|
|
|
// |
|
790
|
|
|
// Appliquer les filtres a un champ [(#CHAMP|filtre1|filtre2)] |
|
791
|
|
|
// retourne un code php compile exprimant ce champ filtre et securise |
|
792
|
|
|
// - une etoile => pas de processeurs standards |
|
793
|
|
|
// - deux etoiles => pas de securite non plus ! |
|
794
|
|
|
// |
|
795
|
|
|
// https://code.spip.net/@applique_filtres |
|
796
|
|
|
function applique_filtres($p) { |
|
797
|
|
|
|
|
798
|
|
|
// Traitements standards (cf. supra) |
|
799
|
|
|
if ($p->etoile == '') { |
|
800
|
|
|
$code = champs_traitements($p); |
|
801
|
|
|
} else { |
|
802
|
|
|
$code = $p->code; |
|
803
|
|
|
} |
|
804
|
|
|
|
|
805
|
|
|
// Appliquer les filtres perso |
|
806
|
|
|
if ($p->param) { |
|
807
|
|
|
$code = compose_filtres($p, $code); |
|
808
|
|
|
} |
|
809
|
|
|
|
|
810
|
|
|
// S'il y a un lien avec la session, ajouter un code qui levera |
|
811
|
|
|
// un drapeau dans la structure d'invalidation $Cache |
|
812
|
|
|
if (isset($p->descr['session'])) { |
|
813
|
|
|
$code = "invalideur_session(\$Cache, $code)"; |
|
814
|
|
|
} |
|
815
|
|
|
|
|
816
|
|
|
$code = sandbox_composer_interdire_scripts($code, $p); |
|
817
|
|
|
|
|
818
|
|
|
return $code; |
|
819
|
|
|
} |
|
820
|
|
|
|
|
821
|
|
|
// Cf. function pipeline dans ecrire/inc_utils.php |
|
822
|
|
|
// https://code.spip.net/@compose_filtres |
|
823
|
|
|
function compose_filtres(&$p, $code) { |
|
824
|
|
|
|
|
825
|
|
|
$image_miette = false; |
|
826
|
|
|
foreach ($p->param as $filtre) { |
|
827
|
|
|
$fonc = array_shift($filtre); |
|
828
|
|
|
if (!$fonc) { |
|
829
|
|
|
continue; |
|
830
|
|
|
} // normalement qu'au premier tour. |
|
831
|
|
|
$is_filtre_image = ((substr($fonc, 0, 6) == 'image_') and $fonc != 'image_graver'); |
|
832
|
|
|
if ($image_miette and !$is_filtre_image) { |
|
833
|
|
|
// il faut graver maintenant car apres le filtre en cours |
|
834
|
|
|
// on est pas sur d'avoir encore le nom du fichier dans le pipe |
|
835
|
|
|
$code = "filtrer('image_graver', $code)"; |
|
836
|
|
|
$image_miette = false; |
|
837
|
|
|
} |
|
838
|
|
|
|
|
839
|
|
|
// recuperer les arguments du filtre, |
|
840
|
|
|
// a separer par "," ou ":" dans le cas du filtre "?{a,b}" |
|
841
|
|
|
$countfiltre = count($filtre); |
|
842
|
|
|
if ($fonc !== '?') { |
|
843
|
|
|
$sep = ','; |
|
844
|
|
|
} else { |
|
845
|
|
|
$sep = ':'; |
|
846
|
|
|
// |?{a,b} *doit* avoir exactement 2 arguments ; on les force |
|
847
|
|
|
if ($countfiltre != 2) { |
|
848
|
|
|
$filtre = array($filtre[0] ?? '', $filtre[1] ?? ''); |
|
849
|
|
|
$countfiltre = 2; |
|
850
|
|
|
} |
|
851
|
|
|
} |
|
852
|
|
|
$arglist = compose_filtres_args($p, $filtre, $sep); |
|
853
|
|
|
$logique = filtre_logique($fonc, $code, substr($arglist, 1)); |
|
854
|
|
|
if ($logique) { |
|
855
|
|
|
$code = $logique; |
|
856
|
|
|
} else { |
|
857
|
|
|
$code = sandbox_composer_filtre($fonc, $code, $arglist, $p, $countfiltre); |
|
858
|
|
|
if ($is_filtre_image) { |
|
859
|
|
|
$image_miette = true; |
|
860
|
|
|
} |
|
861
|
|
|
} |
|
862
|
|
|
} |
|
863
|
|
|
// ramasser les images intermediaires inutiles et graver l'image finale |
|
864
|
|
|
if ($image_miette) { |
|
865
|
|
|
$code = "filtrer('image_graver',$code)"; |
|
866
|
|
|
} |
|
867
|
|
|
|
|
868
|
|
|
return $code; |
|
869
|
|
|
} |
|
870
|
|
|
|
|
871
|
|
|
// Filtres et,ou,oui,non,sinon,xou,xor,and,or,not,yes |
|
872
|
|
|
// et comparateurs |
|
873
|
|
|
function filtre_logique($fonc, $code, $arg) { |
|
874
|
|
|
|
|
875
|
|
|
switch (true) { |
|
876
|
|
|
case in_array($fonc, $GLOBALS['table_criteres_infixes']): |
|
877
|
|
|
return "($code $fonc $arg)"; |
|
878
|
|
|
case ($fonc == 'and') or ($fonc == 'et'): |
|
879
|
|
|
return "((($code) AND ($arg)) ?' ' :'')"; |
|
880
|
|
|
case ($fonc == 'or') or ($fonc == 'ou'): |
|
881
|
|
|
return "((($code) OR ($arg)) ?' ' :'')"; |
|
882
|
|
|
case ($fonc == 'xor') or ($fonc == 'xou'): |
|
883
|
|
|
return "((($code) XOR ($arg)) ?' ' :'')"; |
|
884
|
|
|
case ($fonc == 'sinon'): |
|
885
|
|
|
return "(((\$a = $code) OR (is_string(\$a) AND strlen(\$a))) ? \$a : $arg)"; |
|
886
|
|
|
case ($fonc == 'not') or ($fonc == 'non'): |
|
887
|
|
|
return "(($code) ?'' :' ')"; |
|
888
|
|
|
case ($fonc == 'yes') or ($fonc == 'oui'): |
|
889
|
|
|
return "(($code) ?' ' :'')"; |
|
890
|
|
|
} |
|
891
|
|
|
|
|
892
|
|
|
return ''; |
|
893
|
|
|
} |
|
894
|
|
|
|
|
895
|
|
|
// https://code.spip.net/@compose_filtres_args |
|
896
|
|
|
function compose_filtres_args($p, $args, $sep) { |
|
897
|
|
|
$arglist = ""; |
|
898
|
|
|
foreach ($args as $arg) { |
|
899
|
|
|
$arglist .= $sep . |
|
900
|
|
|
calculer_liste($arg, $p->descr, $p->boucles, $p->id_boucle); |
|
901
|
|
|
} |
|
902
|
|
|
|
|
903
|
|
|
return $arglist; |
|
904
|
|
|
} |
|
905
|
|
|
|
|
906
|
|
|
|
|
907
|
|
|
/** |
|
908
|
|
|
* Réserve les champs necessaires à la comparaison avec le contexte donné par |
|
909
|
|
|
* la boucle parente. |
|
910
|
|
|
* |
|
911
|
|
|
* Attention en recursif il faut les réserver chez soi-même ET chez sa maman |
|
912
|
|
|
* |
|
913
|
|
|
* @param string $idb Identifiant de la boucle |
|
914
|
|
|
* @param string $nom_champ |
|
915
|
|
|
* @param array $boucles AST du squelette |
|
916
|
|
|
* @param null|string $defaut |
|
917
|
|
|
* @return |
|
918
|
|
|
**/ |
|
919
|
|
|
function calculer_argument_precedent($idb, $nom_champ, &$boucles, $defaut = null) { |
|
920
|
|
|
|
|
921
|
|
|
// si recursif, forcer l'extraction du champ SQL mais ignorer le code |
|
922
|
|
|
if ($boucles[$idb]->externe) { |
|
923
|
|
|
index_pile($idb, $nom_champ, $boucles, '', $defaut); |
|
924
|
|
|
// retourner $Pile[$SP] et pas $Pile[0] si recursion en 1ere boucle |
|
925
|
|
|
// on ignore le defaut fourni dans ce cas |
|
926
|
|
|
$defaut = "@\$Pile[\$SP]['$nom_champ']"; |
|
927
|
|
|
} |
|
928
|
|
|
|
|
929
|
|
|
return index_pile($boucles[$idb]->id_parent, $nom_champ, $boucles, '', $defaut); |
|
930
|
|
|
} |
|
931
|
|
|
|
|
932
|
|
|
// |
|
933
|
|
|
// Rechercher dans la pile des boucles actives celle ayant un critere |
|
934
|
|
|
// comportant un certain $motif, et construire alors une reference |
|
935
|
|
|
// a l'environnement de cette boucle, qu'on indexe avec $champ. |
|
936
|
|
|
// Sert a referencer une cellule non declaree dans la table et pourtant la. |
|
937
|
|
|
// Par exemple pour la balise #POINTS on produit $Pile[$SP-n]['points'] |
|
938
|
|
|
// si la n-ieme boucle a un critere "recherche", car on sait qu'il a produit |
|
939
|
|
|
// "SELECT XXXX AS points" |
|
940
|
|
|
// |
|
941
|
|
|
|
|
942
|
|
|
// https://code.spip.net/@rindex_pile |
|
943
|
|
|
function rindex_pile($p, $champ, $motif) { |
|
944
|
|
|
$n = 0; |
|
945
|
|
|
$b = $p->id_boucle; |
|
946
|
|
|
$p->code = ''; |
|
947
|
|
|
while ($b != '') { |
|
948
|
|
|
foreach ($p->boucles[$b]->criteres as $critere) { |
|
949
|
|
|
if ($critere->op == $motif) { |
|
950
|
|
|
$p->code = '$Pile[$SP' . (($n == 0) ? "" : "-$n") . |
|
951
|
|
|
"]['$champ']"; |
|
952
|
|
|
$b = ''; |
|
|
|
|
|
|
953
|
|
|
break 2; |
|
954
|
|
|
} |
|
955
|
|
|
} |
|
956
|
|
|
$n++; |
|
957
|
|
|
$b = $p->boucles[$b]->id_parent; |
|
958
|
|
|
} |
|
959
|
|
|
|
|
960
|
|
|
// si on est hors d'une boucle de {recherche}, cette balise est vide |
|
961
|
|
|
if (!$p->code) { |
|
962
|
|
|
$p->code = "''"; |
|
963
|
|
|
} |
|
964
|
|
|
|
|
965
|
|
|
$p->interdire_scripts = false; |
|
966
|
|
|
|
|
967
|
|
|
return $p; |
|
968
|
|
|
} |
|
969
|
|
|
|
|
970
|
|
|
/** |
|
971
|
|
|
* Retourne le nom de la balise indiquée pour les messages d’erreurs |
|
972
|
|
|
* @param Pile $p Description de la balise |
|
973
|
|
|
* @param string $champ Nom du champ |
|
974
|
|
|
* @return string Nom de la balise, avec indication de boucle explicite si présent. |
|
975
|
|
|
*/ |
|
976
|
|
|
function zbug_presenter_champ($p, $champ = "") { |
|
977
|
|
|
$balise = $champ ? $champ : $p->nom_champ; |
|
978
|
|
|
$explicite = $explicite = $p->nom_boucle ? $p->nom_boucle . ':' : ''; |
|
979
|
|
|
return "#{$explicite}{$balise}"; |
|
980
|
|
|
} |
This check compares the return type specified in the
@returnannotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.