Completed
Push — master ( b877eb...a07606 )
by cam
02:58 queued 01:51
created
ecrire/public/iterateur.php 3 patches
Indentation   +655 added lines, -655 removed lines patch added patch discarded remove patch
@@ -11,7 +11,7 @@  discard block
 block discarded – undo
11 11
 \***************************************************************************/
12 12
 
13 13
 if (!defined('_ECRIRE_INC_VERSION')) {
14
-	return;
14
+    return;
15 15
 }
16 16
 
17 17
 /**
@@ -21,668 +21,668 @@  discard block
 block discarded – undo
21 21
  *
22 22
  */
23 23
 class IterFactory {
24
-	public static function create($iterateur, $command, $info = null) {
25
-
26
-		// cas des SI {si expression} analises tres tot
27
-		// pour eviter le chargement de tout iterateur
28
-		if (isset($command['si'])) {
29
-			foreach ($command['si'] as $si) {
30
-				if (!$si) {
31
-					// $command pour boucle SQL peut generer des erreurs de compilation
32
-					// s'il est transmis alors qu'on est dans un iterateur vide
33
-					return new IterDecorator(new EmptyIterator(), [], $info);
34
-				}
35
-			}
36
-		}
37
-
38
-		// chercher un iterateur PHP existant (par exemple dans SPL)
39
-		// (il faudrait passer l'argument ->sql_serveur
40
-		// pour etre certain qu'on est sur un "php:")
41
-		if (class_exists($iterateur)) {
42
-			$a = isset($command['args']) ? $command['args'] : [];
43
-
44
-			// permettre de passer un Iterateur directement {args #ITERATEUR} :
45
-			// si on recoit deja un iterateur en argument, on l'utilise
46
-			if (count($a) == 1 and is_object($a[0]) and is_subclass_of($a[0], 'Iterator')) {
47
-				$iter = $a[0];
48
-
49
-				// sinon, on cree un iterateur du type donne
50
-			} else {
51
-				// arguments de creation de l'iterateur...
52
-				// (pas glop)
53
-				try {
54
-					switch (count($a)) {
55
-						case 0:
56
-							$iter = new $iterateur();
57
-							break;
58
-						case 1:
59
-							$iter = new $iterateur($a[0]);
60
-							break;
61
-						case 2:
62
-							$iter = new $iterateur($a[0], $a[1]);
63
-							break;
64
-						case 3:
65
-							$iter = new $iterateur($a[0], $a[1], $a[2]);
66
-							break;
67
-						case 4:
68
-							$iter = new $iterateur($a[0], $a[1], $a[2], $a[3]);
69
-							break;
70
-					}
71
-				} catch (Exception $e) {
72
-					spip_log("Erreur de chargement de l'iterateur $iterateur");
73
-					spip_log($e->getMessage());
74
-					$iter = new EmptyIterator();
75
-				}
76
-			}
77
-		} else {
78
-			// chercher la classe d'iterateur
79
-			// IterateurXXX
80
-			// definie dans le fichier iterateurs/xxx.php
81
-			$class = 'Iterateur' . $iterateur;
82
-			if (!class_exists($class)) {
83
-				if (
84
-					!include_spip('iterateur/' . strtolower($iterateur))
85
-					or !class_exists($class)
86
-				) {
87
-					die("Iterateur $iterateur non trouvé");
88
-					// si l'iterateur n'existe pas, on se rabat sur le generique
89
-					# $iter = new EmptyIterator();
90
-				}
91
-			}
92
-			$iter = new $class($command, $info);
93
-		}
94
-
95
-		return new IterDecorator($iter, $command, $info);
96
-	}
24
+    public static function create($iterateur, $command, $info = null) {
25
+
26
+        // cas des SI {si expression} analises tres tot
27
+        // pour eviter le chargement de tout iterateur
28
+        if (isset($command['si'])) {
29
+            foreach ($command['si'] as $si) {
30
+                if (!$si) {
31
+                    // $command pour boucle SQL peut generer des erreurs de compilation
32
+                    // s'il est transmis alors qu'on est dans un iterateur vide
33
+                    return new IterDecorator(new EmptyIterator(), [], $info);
34
+                }
35
+            }
36
+        }
37
+
38
+        // chercher un iterateur PHP existant (par exemple dans SPL)
39
+        // (il faudrait passer l'argument ->sql_serveur
40
+        // pour etre certain qu'on est sur un "php:")
41
+        if (class_exists($iterateur)) {
42
+            $a = isset($command['args']) ? $command['args'] : [];
43
+
44
+            // permettre de passer un Iterateur directement {args #ITERATEUR} :
45
+            // si on recoit deja un iterateur en argument, on l'utilise
46
+            if (count($a) == 1 and is_object($a[0]) and is_subclass_of($a[0], 'Iterator')) {
47
+                $iter = $a[0];
48
+
49
+                // sinon, on cree un iterateur du type donne
50
+            } else {
51
+                // arguments de creation de l'iterateur...
52
+                // (pas glop)
53
+                try {
54
+                    switch (count($a)) {
55
+                        case 0:
56
+                            $iter = new $iterateur();
57
+                            break;
58
+                        case 1:
59
+                            $iter = new $iterateur($a[0]);
60
+                            break;
61
+                        case 2:
62
+                            $iter = new $iterateur($a[0], $a[1]);
63
+                            break;
64
+                        case 3:
65
+                            $iter = new $iterateur($a[0], $a[1], $a[2]);
66
+                            break;
67
+                        case 4:
68
+                            $iter = new $iterateur($a[0], $a[1], $a[2], $a[3]);
69
+                            break;
70
+                    }
71
+                } catch (Exception $e) {
72
+                    spip_log("Erreur de chargement de l'iterateur $iterateur");
73
+                    spip_log($e->getMessage());
74
+                    $iter = new EmptyIterator();
75
+                }
76
+            }
77
+        } else {
78
+            // chercher la classe d'iterateur
79
+            // IterateurXXX
80
+            // definie dans le fichier iterateurs/xxx.php
81
+            $class = 'Iterateur' . $iterateur;
82
+            if (!class_exists($class)) {
83
+                if (
84
+                    !include_spip('iterateur/' . strtolower($iterateur))
85
+                    or !class_exists($class)
86
+                ) {
87
+                    die("Iterateur $iterateur non trouvé");
88
+                    // si l'iterateur n'existe pas, on se rabat sur le generique
89
+                    # $iter = new EmptyIterator();
90
+                }
91
+            }
92
+            $iter = new $class($command, $info);
93
+        }
94
+
95
+        return new IterDecorator($iter, $command, $info);
96
+    }
97 97
 }
98 98
 
99 99
 
100 100
 class IterDecorator extends FilterIterator {
101
-	private $iter;
102
-
103
-	/**
104
-	 * Conditions de filtrage
105
-	 * ie criteres de selection
106
-	 *
107
-	 * @var array
108
-	 */
109
-	protected $filtre = [];
110
-
111
-	/**
112
-	 * Fonction de filtrage compilee a partir des criteres de filtre
113
-	 *
114
-	 * @var string
115
-	 */
116
-	protected $func_filtre = null;
117
-
118
-	/**
119
-	 * Critere {offset, limit}
120
-	 *
121
-	 * @var int
122
-	 * @var int
123
-	 */
124
-	protected $offset = null;
125
-	protected $limit = null;
126
-
127
-	/**
128
-	 * nombre d'elements recuperes depuis la position 0,
129
-	 * en tenant compte des filtres
130
-	 *
131
-	 * @var int
132
-	 */
133
-	protected $fetched = 0;
134
-
135
-	/**
136
-	 * Y a t'il une erreur ?
137
-	 *
138
-	 * @var bool
139
-	 **/
140
-	protected $err = false;
141
-
142
-	/**
143
-	 * Drapeau a activer en cas d'echec
144
-	 * (select SQL errone, non chargement des DATA, etc)
145
-	 */
146
-	public function err() {
147
-		if (method_exists($this->iter, 'err')) {
148
-			return $this->iter->err();
149
-		}
150
-		if (property_exists($this->iter, 'err')) {
151
-			return $this->iter->err;
152
-		}
153
-
154
-		return false;
155
-	}
156
-
157
-	public function __construct(Iterator $iter, $command, $info) {
158
-		parent::__construct($iter);
159
-		parent::rewind(); // remettre a la premiere position (bug? connu de FilterIterator)
160
-
161
-		// recuperer l'iterateur transmis
162
-		$this->iter = $this->getInnerIterator();
163
-		$this->command = $command;
164
-		$this->info = $info;
165
-		$this->pos = 0;
166
-		$this->fetched = 0;
167
-
168
-		// chercher la liste des champs a retourner par
169
-		// fetch si l'objet ne les calcule pas tout seul
170
-		if (!method_exists($this->iter, 'fetch')) {
171
-			$this->calculer_select();
172
-			$this->calculer_filtres();
173
-		}
174
-
175
-		// emptyIterator critere {si} faux n'a pas d'erreur !
176
-		if (isset($this->iter->err)) {
177
-			$this->err = $this->iter->err;
178
-		}
179
-
180
-		// pas d'init a priori, le calcul ne sera fait qu'en cas de besoin (provoque une double requete souvent inutile en sqlite)
181
-		//$this->total = $this->count();
182
-	}
183
-
184
-
185
-	// calcule les elements a retournes par fetch()
186
-	// enleve les elements inutiles du select()
187
-	//
188
-	private function calculer_select() {
189
-		if ($select = &$this->command['select']) {
190
-			foreach ($select as $s) {
191
-				// /!\ $s = '.nom'
192
-				if ($s[0] == '.') {
193
-					$s = substr($s, 1);
194
-				}
195
-				$this->select[] = $s;
196
-			}
197
-		}
198
-	}
199
-
200
-	// recuperer la valeur d'une balise #X
201
-	// en fonction des methodes
202
-	// et proprietes disponibles
203
-	public function get_select($nom) {
204
-		if (
205
-			is_object($this->iter)
206
-			and method_exists($this->iter, $nom)
207
-		) {
208
-			try {
209
-				return $this->iter->$nom();
210
-			} catch (Exception $e) {
211
-				// #GETCHILDREN sur un fichier de DirectoryIterator ...
212
-				spip_log("Methode $nom en echec sur " . get_class($this->iter));
213
-				spip_log("Cela peut être normal : retour d'une ligne de resultat ne pouvant pas calculer cette methode");
214
-
215
-				return '';
216
-			}
217
-		}
218
-		/*
101
+    private $iter;
102
+
103
+    /**
104
+     * Conditions de filtrage
105
+     * ie criteres de selection
106
+     *
107
+     * @var array
108
+     */
109
+    protected $filtre = [];
110
+
111
+    /**
112
+     * Fonction de filtrage compilee a partir des criteres de filtre
113
+     *
114
+     * @var string
115
+     */
116
+    protected $func_filtre = null;
117
+
118
+    /**
119
+     * Critere {offset, limit}
120
+     *
121
+     * @var int
122
+     * @var int
123
+     */
124
+    protected $offset = null;
125
+    protected $limit = null;
126
+
127
+    /**
128
+     * nombre d'elements recuperes depuis la position 0,
129
+     * en tenant compte des filtres
130
+     *
131
+     * @var int
132
+     */
133
+    protected $fetched = 0;
134
+
135
+    /**
136
+     * Y a t'il une erreur ?
137
+     *
138
+     * @var bool
139
+     **/
140
+    protected $err = false;
141
+
142
+    /**
143
+     * Drapeau a activer en cas d'echec
144
+     * (select SQL errone, non chargement des DATA, etc)
145
+     */
146
+    public function err() {
147
+        if (method_exists($this->iter, 'err')) {
148
+            return $this->iter->err();
149
+        }
150
+        if (property_exists($this->iter, 'err')) {
151
+            return $this->iter->err;
152
+        }
153
+
154
+        return false;
155
+    }
156
+
157
+    public function __construct(Iterator $iter, $command, $info) {
158
+        parent::__construct($iter);
159
+        parent::rewind(); // remettre a la premiere position (bug? connu de FilterIterator)
160
+
161
+        // recuperer l'iterateur transmis
162
+        $this->iter = $this->getInnerIterator();
163
+        $this->command = $command;
164
+        $this->info = $info;
165
+        $this->pos = 0;
166
+        $this->fetched = 0;
167
+
168
+        // chercher la liste des champs a retourner par
169
+        // fetch si l'objet ne les calcule pas tout seul
170
+        if (!method_exists($this->iter, 'fetch')) {
171
+            $this->calculer_select();
172
+            $this->calculer_filtres();
173
+        }
174
+
175
+        // emptyIterator critere {si} faux n'a pas d'erreur !
176
+        if (isset($this->iter->err)) {
177
+            $this->err = $this->iter->err;
178
+        }
179
+
180
+        // pas d'init a priori, le calcul ne sera fait qu'en cas de besoin (provoque une double requete souvent inutile en sqlite)
181
+        //$this->total = $this->count();
182
+    }
183
+
184
+
185
+    // calcule les elements a retournes par fetch()
186
+    // enleve les elements inutiles du select()
187
+    //
188
+    private function calculer_select() {
189
+        if ($select = &$this->command['select']) {
190
+            foreach ($select as $s) {
191
+                // /!\ $s = '.nom'
192
+                if ($s[0] == '.') {
193
+                    $s = substr($s, 1);
194
+                }
195
+                $this->select[] = $s;
196
+            }
197
+        }
198
+    }
199
+
200
+    // recuperer la valeur d'une balise #X
201
+    // en fonction des methodes
202
+    // et proprietes disponibles
203
+    public function get_select($nom) {
204
+        if (
205
+            is_object($this->iter)
206
+            and method_exists($this->iter, $nom)
207
+        ) {
208
+            try {
209
+                return $this->iter->$nom();
210
+            } catch (Exception $e) {
211
+                // #GETCHILDREN sur un fichier de DirectoryIterator ...
212
+                spip_log("Methode $nom en echec sur " . get_class($this->iter));
213
+                spip_log("Cela peut être normal : retour d'une ligne de resultat ne pouvant pas calculer cette methode");
214
+
215
+                return '';
216
+            }
217
+        }
218
+        /*
219 219
 		if (property_exists($this->iter, $nom)) {
220 220
 			return $this->iter->$nom;
221 221
 		}*/
222
-		// cle et valeur par defaut
223
-		// ICI PLANTAGE SI ON NE CONTROLE PAS $nom
224
-		if (
225
-			in_array($nom, ['cle', 'valeur'])
226
-			and method_exists($this, $nom)
227
-		) {
228
-			return $this->$nom();
229
-		}
230
-
231
-		// Par defaut chercher en xpath dans la valeur()
232
-		return table_valeur($this->valeur(), $nom, null);
233
-	}
234
-
235
-
236
-	private function calculer_filtres() {
237
-
238
-		// Issu de calculer_select() de public/composer L.519
239
-		// TODO: externaliser...
240
-		//
241
-		// retirer les criteres vides:
242
-		// {X ?} avec X absent de l'URL
243
-		// {par #ENV{X}} avec X absent de l'URL
244
-		// IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil)
245
-		if ($where = &$this->command['where']) {
246
-			foreach ($where as $k => $v) {
247
-				$this->filtre[] = $this->traduire_condition_sql_en_filtre($v);
248
-			}
249
-		}
250
-
251
-		// critere {2,7}
252
-		if (isset($this->command['limit']) and $this->command['limit']) {
253
-			$limit = explode(',', $this->command['limit']);
254
-			$this->offset = $limit[0];
255
-			$this->limit = $limit[1];
256
-		}
257
-
258
-		// Creer la fonction de filtrage sur $this
259
-		if ($this->filtre) {
260
-			if ($filtres = $this->assembler_filtres($this->filtre)) {
261
-				$filtres = 'return ' . $filtres.';';
262
-				$this->func_filtre = function () use ($filtres) {
263
-					return eval($filtres);
264
-				};
265
-			}
266
-			else {
267
-				$this->func_filtre = null;
268
-			}
269
-		}
270
-	}
271
-
272
-	/**
273
-	 * Assembler le tableau de filtres traduits depuis les conditions SQL
274
-	 * les filtres vides ou null sont ignores
275
-	 * @param $filtres
276
-	 * @param string $operateur
277
-	 * @return string|null
278
-	 */
279
-	protected function assembler_filtres($filtres, $operateur = 'AND') {
280
-
281
-		$filtres_string = [];
282
-		foreach ($filtres as $k => $v) {
283
-			// si c'est un tableau de OR/AND + 2 sous-filtres, on recurse pour transformer en chaine
284
-			if (is_array($v) and in_array(reset($v), ['OR', 'AND'])) {
285
-				$op = array_shift($v);
286
-				$v = $this->assembler_filtres($v, $op);
287
-			}
288
-			if (is_null($v) or !is_string($v) or empty($v)) {
289
-				continue;
290
-			}
291
-			$filtres_string[] = $v;
292
-		}
293
-
294
-		if (!count($filtres_string)) {
295
-			return null;
296
-		}
297
-
298
-		return "(" . implode( ") $operateur (", $filtres_string) . ")";
299
-	}
300
-
301
-	/**
302
-	 * Traduire un element du tableau where SQL en un filtre
303
-	 * @param $v
304
-	 * @return string|array|null
305
-	 */
306
-	protected function traduire_condition_sql_en_filtre($v) {
307
-		if (is_array($v)) {
308
-			if ((count($v) >= 2) && ($v[0] == 'REGEXP') && ($v[2] == "'.*'")) {
309
-				return 'true';
310
-			} elseif ((count($v) >= 2) && ($v[0] == 'LIKE') && ($v[2] == "'%'")) {
311
-				return 'true';
312
-			} else {
313
-				$op = $v[0] ?: $v;
314
-			}
315
-		} else {
316
-			$op = $v;
317
-		}
318
-		if ((!$op) or ($op == 1) or ($op == '0=0')) {
319
-			return 'true';
320
-		}
321
-		if ($op === '0=1') {
322
-			return 'false';
323
-		}
324
-		// traiter {cle IN a,b} ou {valeur !IN a,b}
325
-		if (preg_match(',^\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\)$,', $op, $regs)) {
326
-			return $this->composer_filtre($regs[1], 'IN', $regs[3], $regs[2]);
327
-		}
328
-
329
-		// 3 possibilites : count($v) =
330
-		// * 1 : {x y} ; on recoit $v[0] = y
331
-		// * 2 : {x !op y} ; on recoit $v[0] = 'NOT', $v[1] = array() // array du type {x op y}
332
-		// * 3 : {x op y} ; on recoit $v[0] = 'op', $v[1] = x, $v[2] = y
333
-
334
-		// 1 : forcement traite par un critere, on passe
335
-		if (!$v or !is_array($v) or count($v) == 1) {
336
-			return null; // sera ignore
337
-		}
338
-		if (count($v) == 2 and is_array($v[1])) {
339
-			return $this->composer_filtre($v[1][1], $v[1][0], $v[1][2], 'NOT');
340
-		}
341
-		if (count($v) == 3) {
342
-			// traiter le OR/AND suivi de 2 valeurs
343
-			if (in_array($op, ['OR', 'AND'])) {
344
-				array_shift($v);
345
-				foreach (array_keys($v) as $k) {
346
-					$v[$k] = $this->traduire_condition_sql_en_filtre($v[$k]);
347
-					if ($v[$k] === null) {
348
-						unset($v[$k]);
349
-					}
350
-					elseif ($v[$k] === 'true') {
351
-						if ($op === 'OR') {
352
-							return 'true';
353
-						}
354
-						if ($op === 'AND') {
355
-							unset($v[$k]);
356
-						}
357
-					}
358
-					elseif ($v[$k] === 'false') {
359
-						if ($op === 'OR') {
360
-							unset($v[$k]);
361
-						}
362
-						if ($op === 'AND') {
363
-							return 'false';
364
-						}
365
-					}
366
-				}
367
-				if (!count($v)) {
368
-					return null;
369
-				}
370
-				if (count($v) === 1) {
371
-					return reset($v);
372
-				}
373
-				array_unshift($v, $op);
374
-				return $v;
375
-			}
376
-			return $this->composer_filtre($v[1], $v[0], $v[2]);
377
-		}
378
-
379
-		return null;  // sera ignore
380
-	}
381
-
382
-	/**
383
-	 * Calculer un filtre sur un champ du tableau
384
-	 * @param $cle
385
-	 * @param $op
386
-	 * @param $valeur
387
-	 * @param false $not
388
-	 * @return string|null
389
-	 */
390
-	protected function composer_filtre($cle, $op, $valeur, $not = false) {
391
-		if (method_exists($this->iter, 'exception_des_criteres')) {
392
-			if (in_array($cle, $this->iter->exception_des_criteres())) {
393
-				return null;
394
-			}
395
-		}
396
-		// TODO: analyser le filtre pour refuser ce qu'on ne sait pas traiter ?
397
-		# mais c'est normalement deja opere par calculer_critere_infixe()
398
-		# qui regarde la description 'desc' (en casse reelle d'ailleurs : {isDir=1}
399
-		# ne sera pas vu si l'on a defini desc['field']['isdir'] pour que #ISDIR soit present.
400
-		# il faudrait peut etre definir les 2 champs isDir et isdir... a reflechir...
401
-
402
-		# if (!in_array($cle, array('cle', 'valeur')))
403
-		#	return;
404
-
405
-		$a = '$this->get_select(\'' . $cle . '\')';
406
-
407
-		$filtre = '';
408
-
409
-		if ($op == 'REGEXP') {
410
-			$filtre = 'filtrer("match", ' . $a . ', ' . str_replace('\"', '"', $valeur) . ')';
411
-			$op = '';
412
-		} else {
413
-			if ($op == 'LIKE') {
414
-				$valeur = str_replace(['\"', '_', '%'], ['"', '.', '.*'], preg_quote($valeur));
415
-				$filtre = 'filtrer("match", ' . $a . ', ' . $valeur . ')';
416
-				$op = '';
417
-			} else {
418
-				if ($op == '=') {
419
-					$op = '==';
420
-				} else {
421
-					if ($op == 'IN') {
422
-						$filtre = 'in_array(' . $a . ', array' . $valeur . ')';
423
-						$op = '';
424
-					} else {
425
-						if (!in_array($op, ['<', '<=', '>', '>='])) {
426
-							spip_log('operateur non reconnu ' . $op); // [todo] mettre une erreur de squelette
427
-							$op = '';
428
-						}
429
-					}
430
-				}
431
-			}
432
-		}
433
-
434
-		if ($op) {
435
-			$filtre = $a . $op . str_replace('\"', '"', $valeur);
436
-		}
437
-
438
-		if ($not) {
439
-			$filtre = "!($filtre)";
440
-		}
441
-
442
-		return $filtre;
443
-	}
444
-
445
-
446
-	public function next() {
447
-		$this->pos++;
448
-		parent::next();
449
-	}
450
-
451
-	/**
452
-	 * revient au depart
453
-	 *
454
-	 * @return void
455
-	 */
456
-	public function rewind() {
457
-		$this->pos = 0;
458
-		$this->fetched = 0;
459
-		parent::rewind();
460
-	}
461
-
462
-
463
-	# Extension SPIP des iterateurs PHP
464
-	/**
465
-	 * type de l'iterateur
466
-	 *
467
-	 * @var string
468
-	 */
469
-	protected $type;
470
-
471
-	/**
472
-	 * parametres de l'iterateur
473
-	 *
474
-	 * @var array
475
-	 */
476
-	protected $command;
477
-
478
-	/**
479
-	 * infos de compilateur
480
-	 *
481
-	 * @var array
482
-	 */
483
-	protected $info;
484
-
485
-	/**
486
-	 * position courante de l'iterateur
487
-	 *
488
-	 * @var int
489
-	 */
490
-	protected $pos = null;
491
-
492
-	/**
493
-	 * nombre total resultats dans l'iterateur
494
-	 *
495
-	 * @var int
496
-	 */
497
-	protected $total = null;
498
-
499
-	/**
500
-	 * nombre maximal de recherche pour $total
501
-	 * si l'iterateur n'implemente pas de fonction specifique
502
-	 */
503
-	protected $max = 100000;
504
-
505
-
506
-	/**
507
-	 * Liste des champs a inserer dans les $row
508
-	 * retournes par ->fetch()
509
-	 */
510
-	protected $select = [];
511
-
512
-
513
-	/**
514
-	 * aller a la position absolue n,
515
-	 * comptee depuis le debut
516
-	 *
517
-	 * @param int $n
518
-	 *   absolute pos
519
-	 * @param string $continue
520
-	 *   param for sql_ api
521
-	 * @return bool
522
-	 *   success or fail if not implemented
523
-	 */
524
-	public function seek($n = 0, $continue = null) {
525
-		if ($this->func_filtre or !method_exists($this->iter, 'seek') or !$this->iter->seek($n)) {
526
-			$this->seek_loop($n);
527
-		}
528
-		$this->pos = $n;
529
-		$this->fetched = $n;
530
-
531
-		return true;
532
-	}
533
-
534
-	/*
222
+        // cle et valeur par defaut
223
+        // ICI PLANTAGE SI ON NE CONTROLE PAS $nom
224
+        if (
225
+            in_array($nom, ['cle', 'valeur'])
226
+            and method_exists($this, $nom)
227
+        ) {
228
+            return $this->$nom();
229
+        }
230
+
231
+        // Par defaut chercher en xpath dans la valeur()
232
+        return table_valeur($this->valeur(), $nom, null);
233
+    }
234
+
235
+
236
+    private function calculer_filtres() {
237
+
238
+        // Issu de calculer_select() de public/composer L.519
239
+        // TODO: externaliser...
240
+        //
241
+        // retirer les criteres vides:
242
+        // {X ?} avec X absent de l'URL
243
+        // {par #ENV{X}} avec X absent de l'URL
244
+        // IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil)
245
+        if ($where = &$this->command['where']) {
246
+            foreach ($where as $k => $v) {
247
+                $this->filtre[] = $this->traduire_condition_sql_en_filtre($v);
248
+            }
249
+        }
250
+
251
+        // critere {2,7}
252
+        if (isset($this->command['limit']) and $this->command['limit']) {
253
+            $limit = explode(',', $this->command['limit']);
254
+            $this->offset = $limit[0];
255
+            $this->limit = $limit[1];
256
+        }
257
+
258
+        // Creer la fonction de filtrage sur $this
259
+        if ($this->filtre) {
260
+            if ($filtres = $this->assembler_filtres($this->filtre)) {
261
+                $filtres = 'return ' . $filtres.';';
262
+                $this->func_filtre = function () use ($filtres) {
263
+                    return eval($filtres);
264
+                };
265
+            }
266
+            else {
267
+                $this->func_filtre = null;
268
+            }
269
+        }
270
+    }
271
+
272
+    /**
273
+     * Assembler le tableau de filtres traduits depuis les conditions SQL
274
+     * les filtres vides ou null sont ignores
275
+     * @param $filtres
276
+     * @param string $operateur
277
+     * @return string|null
278
+     */
279
+    protected function assembler_filtres($filtres, $operateur = 'AND') {
280
+
281
+        $filtres_string = [];
282
+        foreach ($filtres as $k => $v) {
283
+            // si c'est un tableau de OR/AND + 2 sous-filtres, on recurse pour transformer en chaine
284
+            if (is_array($v) and in_array(reset($v), ['OR', 'AND'])) {
285
+                $op = array_shift($v);
286
+                $v = $this->assembler_filtres($v, $op);
287
+            }
288
+            if (is_null($v) or !is_string($v) or empty($v)) {
289
+                continue;
290
+            }
291
+            $filtres_string[] = $v;
292
+        }
293
+
294
+        if (!count($filtres_string)) {
295
+            return null;
296
+        }
297
+
298
+        return "(" . implode( ") $operateur (", $filtres_string) . ")";
299
+    }
300
+
301
+    /**
302
+     * Traduire un element du tableau where SQL en un filtre
303
+     * @param $v
304
+     * @return string|array|null
305
+     */
306
+    protected function traduire_condition_sql_en_filtre($v) {
307
+        if (is_array($v)) {
308
+            if ((count($v) >= 2) && ($v[0] == 'REGEXP') && ($v[2] == "'.*'")) {
309
+                return 'true';
310
+            } elseif ((count($v) >= 2) && ($v[0] == 'LIKE') && ($v[2] == "'%'")) {
311
+                return 'true';
312
+            } else {
313
+                $op = $v[0] ?: $v;
314
+            }
315
+        } else {
316
+            $op = $v;
317
+        }
318
+        if ((!$op) or ($op == 1) or ($op == '0=0')) {
319
+            return 'true';
320
+        }
321
+        if ($op === '0=1') {
322
+            return 'false';
323
+        }
324
+        // traiter {cle IN a,b} ou {valeur !IN a,b}
325
+        if (preg_match(',^\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\)$,', $op, $regs)) {
326
+            return $this->composer_filtre($regs[1], 'IN', $regs[3], $regs[2]);
327
+        }
328
+
329
+        // 3 possibilites : count($v) =
330
+        // * 1 : {x y} ; on recoit $v[0] = y
331
+        // * 2 : {x !op y} ; on recoit $v[0] = 'NOT', $v[1] = array() // array du type {x op y}
332
+        // * 3 : {x op y} ; on recoit $v[0] = 'op', $v[1] = x, $v[2] = y
333
+
334
+        // 1 : forcement traite par un critere, on passe
335
+        if (!$v or !is_array($v) or count($v) == 1) {
336
+            return null; // sera ignore
337
+        }
338
+        if (count($v) == 2 and is_array($v[1])) {
339
+            return $this->composer_filtre($v[1][1], $v[1][0], $v[1][2], 'NOT');
340
+        }
341
+        if (count($v) == 3) {
342
+            // traiter le OR/AND suivi de 2 valeurs
343
+            if (in_array($op, ['OR', 'AND'])) {
344
+                array_shift($v);
345
+                foreach (array_keys($v) as $k) {
346
+                    $v[$k] = $this->traduire_condition_sql_en_filtre($v[$k]);
347
+                    if ($v[$k] === null) {
348
+                        unset($v[$k]);
349
+                    }
350
+                    elseif ($v[$k] === 'true') {
351
+                        if ($op === 'OR') {
352
+                            return 'true';
353
+                        }
354
+                        if ($op === 'AND') {
355
+                            unset($v[$k]);
356
+                        }
357
+                    }
358
+                    elseif ($v[$k] === 'false') {
359
+                        if ($op === 'OR') {
360
+                            unset($v[$k]);
361
+                        }
362
+                        if ($op === 'AND') {
363
+                            return 'false';
364
+                        }
365
+                    }
366
+                }
367
+                if (!count($v)) {
368
+                    return null;
369
+                }
370
+                if (count($v) === 1) {
371
+                    return reset($v);
372
+                }
373
+                array_unshift($v, $op);
374
+                return $v;
375
+            }
376
+            return $this->composer_filtre($v[1], $v[0], $v[2]);
377
+        }
378
+
379
+        return null;  // sera ignore
380
+    }
381
+
382
+    /**
383
+     * Calculer un filtre sur un champ du tableau
384
+     * @param $cle
385
+     * @param $op
386
+     * @param $valeur
387
+     * @param false $not
388
+     * @return string|null
389
+     */
390
+    protected function composer_filtre($cle, $op, $valeur, $not = false) {
391
+        if (method_exists($this->iter, 'exception_des_criteres')) {
392
+            if (in_array($cle, $this->iter->exception_des_criteres())) {
393
+                return null;
394
+            }
395
+        }
396
+        // TODO: analyser le filtre pour refuser ce qu'on ne sait pas traiter ?
397
+        # mais c'est normalement deja opere par calculer_critere_infixe()
398
+        # qui regarde la description 'desc' (en casse reelle d'ailleurs : {isDir=1}
399
+        # ne sera pas vu si l'on a defini desc['field']['isdir'] pour que #ISDIR soit present.
400
+        # il faudrait peut etre definir les 2 champs isDir et isdir... a reflechir...
401
+
402
+        # if (!in_array($cle, array('cle', 'valeur')))
403
+        #	return;
404
+
405
+        $a = '$this->get_select(\'' . $cle . '\')';
406
+
407
+        $filtre = '';
408
+
409
+        if ($op == 'REGEXP') {
410
+            $filtre = 'filtrer("match", ' . $a . ', ' . str_replace('\"', '"', $valeur) . ')';
411
+            $op = '';
412
+        } else {
413
+            if ($op == 'LIKE') {
414
+                $valeur = str_replace(['\"', '_', '%'], ['"', '.', '.*'], preg_quote($valeur));
415
+                $filtre = 'filtrer("match", ' . $a . ', ' . $valeur . ')';
416
+                $op = '';
417
+            } else {
418
+                if ($op == '=') {
419
+                    $op = '==';
420
+                } else {
421
+                    if ($op == 'IN') {
422
+                        $filtre = 'in_array(' . $a . ', array' . $valeur . ')';
423
+                        $op = '';
424
+                    } else {
425
+                        if (!in_array($op, ['<', '<=', '>', '>='])) {
426
+                            spip_log('operateur non reconnu ' . $op); // [todo] mettre une erreur de squelette
427
+                            $op = '';
428
+                        }
429
+                    }
430
+                }
431
+            }
432
+        }
433
+
434
+        if ($op) {
435
+            $filtre = $a . $op . str_replace('\"', '"', $valeur);
436
+        }
437
+
438
+        if ($not) {
439
+            $filtre = "!($filtre)";
440
+        }
441
+
442
+        return $filtre;
443
+    }
444
+
445
+
446
+    public function next() {
447
+        $this->pos++;
448
+        parent::next();
449
+    }
450
+
451
+    /**
452
+     * revient au depart
453
+     *
454
+     * @return void
455
+     */
456
+    public function rewind() {
457
+        $this->pos = 0;
458
+        $this->fetched = 0;
459
+        parent::rewind();
460
+    }
461
+
462
+
463
+    # Extension SPIP des iterateurs PHP
464
+    /**
465
+     * type de l'iterateur
466
+     *
467
+     * @var string
468
+     */
469
+    protected $type;
470
+
471
+    /**
472
+     * parametres de l'iterateur
473
+     *
474
+     * @var array
475
+     */
476
+    protected $command;
477
+
478
+    /**
479
+     * infos de compilateur
480
+     *
481
+     * @var array
482
+     */
483
+    protected $info;
484
+
485
+    /**
486
+     * position courante de l'iterateur
487
+     *
488
+     * @var int
489
+     */
490
+    protected $pos = null;
491
+
492
+    /**
493
+     * nombre total resultats dans l'iterateur
494
+     *
495
+     * @var int
496
+     */
497
+    protected $total = null;
498
+
499
+    /**
500
+     * nombre maximal de recherche pour $total
501
+     * si l'iterateur n'implemente pas de fonction specifique
502
+     */
503
+    protected $max = 100000;
504
+
505
+
506
+    /**
507
+     * Liste des champs a inserer dans les $row
508
+     * retournes par ->fetch()
509
+     */
510
+    protected $select = [];
511
+
512
+
513
+    /**
514
+     * aller a la position absolue n,
515
+     * comptee depuis le debut
516
+     *
517
+     * @param int $n
518
+     *   absolute pos
519
+     * @param string $continue
520
+     *   param for sql_ api
521
+     * @return bool
522
+     *   success or fail if not implemented
523
+     */
524
+    public function seek($n = 0, $continue = null) {
525
+        if ($this->func_filtre or !method_exists($this->iter, 'seek') or !$this->iter->seek($n)) {
526
+            $this->seek_loop($n);
527
+        }
528
+        $this->pos = $n;
529
+        $this->fetched = $n;
530
+
531
+        return true;
532
+    }
533
+
534
+    /*
535 535
 	 * aller a la position $n en parcourant
536 536
 	 * un par un tous les elements
537 537
 	 */
538
-	private function seek_loop($n) {
539
-		if ($this->pos > $n) {
540
-			$this->rewind();
541
-		}
542
-
543
-		while ($this->pos < $n and $this->valid()) {
544
-			$this->next();
545
-		}
546
-
547
-		return true;
548
-	}
549
-
550
-	/**
551
-	 * Avancer de $saut pas
552
-	 *
553
-	 * @param  $saut
554
-	 * @param  $max
555
-	 * @return int
556
-	 */
557
-	public function skip($saut, $max = null) {
558
-		// pas de saut en arriere autorise pour cette fonction
559
-		if (($saut = intval($saut)) <= 0) {
560
-			return $this->pos;
561
-		}
562
-		$seek = $this->pos + $saut;
563
-		// si le saut fait depasser le maxi, on libere la resource
564
-		// et on sort
565
-		if (is_null($max)) {
566
-			$max = $this->count();
567
-		}
568
-
569
-		if ($seek >= $max or $seek >= $this->count()) {
570
-			// sortie plus rapide que de faire next() jusqu'a la fin !
571
-			$this->free();
572
-
573
-			return $max;
574
-		}
575
-
576
-		$this->seek($seek);
577
-
578
-		return $this->pos;
579
-	}
580
-
581
-	/**
582
-	 * Renvoyer un tableau des donnees correspondantes
583
-	 * a la position courante de l'iterateur
584
-	 * en controlant si on respecte le filtre
585
-	 * Appliquer aussi le critere {offset,limit}
586
-	 *
587
-	 * @return array|bool
588
-	 */
589
-	public function fetch() {
590
-		if (method_exists($this->iter, 'fetch')) {
591
-			return $this->iter->fetch();
592
-		} else {
593
-			while (
594
-				$this->valid()
595
-				and (
596
-					!$this->accept()
597
-					or (isset($this->offset) and $this->fetched++ < $this->offset)
598
-				)
599
-			) {
600
-				$this->next();
601
-			}
602
-
603
-			if (!$this->valid()) {
604
-				return false;
605
-			}
606
-
607
-			if (
608
-				isset($this->limit)
609
-				and $this->fetched > $this->offset + $this->limit
610
-			) {
611
-				return false;
612
-			}
613
-
614
-			$r = [];
615
-			foreach ($this->select as $nom) {
616
-				$r[$nom] = $this->get_select($nom);
617
-			}
618
-			$this->next();
619
-
620
-			return $r;
621
-		}
622
-	}
623
-
624
-	// retourner la cle pour #CLE
625
-	public function cle() {
626
-		return $this->key();
627
-	}
628
-
629
-	// retourner la valeur pour #VALEUR
630
-	public function valeur() {
631
-		return $this->current();
632
-	}
633
-
634
-	/**
635
-	 * Accepte-t-on l'entree courante lue ?
636
-	 * On execute les filtres pour le savoir.
637
-	 **/
638
-	public function accept() {
639
-		if ($f = $this->func_filtre) {
640
-			return $f();
641
-		}
642
-
643
-		return true;
644
-	}
645
-
646
-	/**
647
-	 * liberer la ressource
648
-	 *
649
-	 * @return bool
650
-	 */
651
-	public function free() {
652
-		if (method_exists($this->iter, 'free')) {
653
-			$this->iter->free();
654
-		}
655
-		$this->pos = $this->total = 0;
656
-
657
-		return true;
658
-	}
659
-
660
-	/**
661
-	 * Compter le nombre total de resultats
662
-	 * pour #TOTAL_BOUCLE
663
-	 *
664
-	 * @return int
665
-	 */
666
-	public function count() {
667
-		if (is_null($this->total)) {
668
-			if (
669
-				method_exists($this->iter, 'count')
670
-				and !$this->func_filtre
671
-			) {
672
-				return $this->total = $this->iter->count();
673
-			} else {
674
-				// compter les lignes et rembobiner
675
-				$total = 0;
676
-				$pos = $this->pos; // sauver la position
677
-				$this->rewind();
678
-				while ($this->fetch() and $total < $this->max) {
679
-					$total++;
680
-				}
681
-				$this->seek($pos);
682
-				$this->total = $total;
683
-			}
684
-		}
685
-
686
-		return $this->total;
687
-	}
538
+    private function seek_loop($n) {
539
+        if ($this->pos > $n) {
540
+            $this->rewind();
541
+        }
542
+
543
+        while ($this->pos < $n and $this->valid()) {
544
+            $this->next();
545
+        }
546
+
547
+        return true;
548
+    }
549
+
550
+    /**
551
+     * Avancer de $saut pas
552
+     *
553
+     * @param  $saut
554
+     * @param  $max
555
+     * @return int
556
+     */
557
+    public function skip($saut, $max = null) {
558
+        // pas de saut en arriere autorise pour cette fonction
559
+        if (($saut = intval($saut)) <= 0) {
560
+            return $this->pos;
561
+        }
562
+        $seek = $this->pos + $saut;
563
+        // si le saut fait depasser le maxi, on libere la resource
564
+        // et on sort
565
+        if (is_null($max)) {
566
+            $max = $this->count();
567
+        }
568
+
569
+        if ($seek >= $max or $seek >= $this->count()) {
570
+            // sortie plus rapide que de faire next() jusqu'a la fin !
571
+            $this->free();
572
+
573
+            return $max;
574
+        }
575
+
576
+        $this->seek($seek);
577
+
578
+        return $this->pos;
579
+    }
580
+
581
+    /**
582
+     * Renvoyer un tableau des donnees correspondantes
583
+     * a la position courante de l'iterateur
584
+     * en controlant si on respecte le filtre
585
+     * Appliquer aussi le critere {offset,limit}
586
+     *
587
+     * @return array|bool
588
+     */
589
+    public function fetch() {
590
+        if (method_exists($this->iter, 'fetch')) {
591
+            return $this->iter->fetch();
592
+        } else {
593
+            while (
594
+                $this->valid()
595
+                and (
596
+                    !$this->accept()
597
+                    or (isset($this->offset) and $this->fetched++ < $this->offset)
598
+                )
599
+            ) {
600
+                $this->next();
601
+            }
602
+
603
+            if (!$this->valid()) {
604
+                return false;
605
+            }
606
+
607
+            if (
608
+                isset($this->limit)
609
+                and $this->fetched > $this->offset + $this->limit
610
+            ) {
611
+                return false;
612
+            }
613
+
614
+            $r = [];
615
+            foreach ($this->select as $nom) {
616
+                $r[$nom] = $this->get_select($nom);
617
+            }
618
+            $this->next();
619
+
620
+            return $r;
621
+        }
622
+    }
623
+
624
+    // retourner la cle pour #CLE
625
+    public function cle() {
626
+        return $this->key();
627
+    }
628
+
629
+    // retourner la valeur pour #VALEUR
630
+    public function valeur() {
631
+        return $this->current();
632
+    }
633
+
634
+    /**
635
+     * Accepte-t-on l'entree courante lue ?
636
+     * On execute les filtres pour le savoir.
637
+     **/
638
+    public function accept() {
639
+        if ($f = $this->func_filtre) {
640
+            return $f();
641
+        }
642
+
643
+        return true;
644
+    }
645
+
646
+    /**
647
+     * liberer la ressource
648
+     *
649
+     * @return bool
650
+     */
651
+    public function free() {
652
+        if (method_exists($this->iter, 'free')) {
653
+            $this->iter->free();
654
+        }
655
+        $this->pos = $this->total = 0;
656
+
657
+        return true;
658
+    }
659
+
660
+    /**
661
+     * Compter le nombre total de resultats
662
+     * pour #TOTAL_BOUCLE
663
+     *
664
+     * @return int
665
+     */
666
+    public function count() {
667
+        if (is_null($this->total)) {
668
+            if (
669
+                method_exists($this->iter, 'count')
670
+                and !$this->func_filtre
671
+            ) {
672
+                return $this->total = $this->iter->count();
673
+            } else {
674
+                // compter les lignes et rembobiner
675
+                $total = 0;
676
+                $pos = $this->pos; // sauver la position
677
+                $this->rewind();
678
+                while ($this->fetch() and $total < $this->max) {
679
+                    $total++;
680
+                }
681
+                $this->seek($pos);
682
+                $this->total = $total;
683
+            }
684
+        }
685
+
686
+        return $this->total;
687
+    }
688 688
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -78,10 +78,10 @@  discard block
 block discarded – undo
78 78
 			// chercher la classe d'iterateur
79 79
 			// IterateurXXX
80 80
 			// definie dans le fichier iterateurs/xxx.php
81
-			$class = 'Iterateur' . $iterateur;
81
+			$class = 'Iterateur'.$iterateur;
82 82
 			if (!class_exists($class)) {
83 83
 				if (
84
-					!include_spip('iterateur/' . strtolower($iterateur))
84
+					!include_spip('iterateur/'.strtolower($iterateur))
85 85
 					or !class_exists($class)
86 86
 				) {
87 87
 					die("Iterateur $iterateur non trouv&#233;");
@@ -209,7 +209,7 @@  discard block
 block discarded – undo
209 209
 				return $this->iter->$nom();
210 210
 			} catch (Exception $e) {
211 211
 				// #GETCHILDREN sur un fichier de DirectoryIterator ...
212
-				spip_log("Methode $nom en echec sur " . get_class($this->iter));
212
+				spip_log("Methode $nom en echec sur ".get_class($this->iter));
213 213
 				spip_log("Cela peut être normal : retour d'une ligne de resultat ne pouvant pas calculer cette methode");
214 214
 
215 215
 				return '';
@@ -258,8 +258,8 @@  discard block
 block discarded – undo
258 258
 		// Creer la fonction de filtrage sur $this
259 259
 		if ($this->filtre) {
260 260
 			if ($filtres = $this->assembler_filtres($this->filtre)) {
261
-				$filtres = 'return ' . $filtres.';';
262
-				$this->func_filtre = function () use ($filtres) {
261
+				$filtres = 'return '.$filtres.';';
262
+				$this->func_filtre = function() use ($filtres) {
263 263
 					return eval($filtres);
264 264
 				};
265 265
 			}
@@ -295,7 +295,7 @@  discard block
 block discarded – undo
295 295
 			return null;
296 296
 		}
297 297
 
298
-		return "(" . implode( ") $operateur (", $filtres_string) . ")";
298
+		return "(".implode(") $operateur (", $filtres_string).")";
299 299
 	}
300 300
 
301 301
 	/**
@@ -376,7 +376,7 @@  discard block
 block discarded – undo
376 376
 			return $this->composer_filtre($v[1], $v[0], $v[2]);
377 377
 		}
378 378
 
379
-		return null;  // sera ignore
379
+		return null; // sera ignore
380 380
 	}
381 381
 
382 382
 	/**
@@ -402,28 +402,28 @@  discard block
 block discarded – undo
402 402
 		# if (!in_array($cle, array('cle', 'valeur')))
403 403
 		#	return;
404 404
 
405
-		$a = '$this->get_select(\'' . $cle . '\')';
405
+		$a = '$this->get_select(\''.$cle.'\')';
406 406
 
407 407
 		$filtre = '';
408 408
 
409 409
 		if ($op == 'REGEXP') {
410
-			$filtre = 'filtrer("match", ' . $a . ', ' . str_replace('\"', '"', $valeur) . ')';
410
+			$filtre = 'filtrer("match", '.$a.', '.str_replace('\"', '"', $valeur).')';
411 411
 			$op = '';
412 412
 		} else {
413 413
 			if ($op == 'LIKE') {
414 414
 				$valeur = str_replace(['\"', '_', '%'], ['"', '.', '.*'], preg_quote($valeur));
415
-				$filtre = 'filtrer("match", ' . $a . ', ' . $valeur . ')';
415
+				$filtre = 'filtrer("match", '.$a.', '.$valeur.')';
416 416
 				$op = '';
417 417
 			} else {
418 418
 				if ($op == '=') {
419 419
 					$op = '==';
420 420
 				} else {
421 421
 					if ($op == 'IN') {
422
-						$filtre = 'in_array(' . $a . ', array' . $valeur . ')';
422
+						$filtre = 'in_array('.$a.', array'.$valeur.')';
423 423
 						$op = '';
424 424
 					} else {
425 425
 						if (!in_array($op, ['<', '<=', '>', '>='])) {
426
-							spip_log('operateur non reconnu ' . $op); // [todo] mettre une erreur de squelette
426
+							spip_log('operateur non reconnu '.$op); // [todo] mettre une erreur de squelette
427 427
 							$op = '';
428 428
 						}
429 429
 					}
@@ -432,7 +432,7 @@  discard block
 block discarded – undo
432 432
 		}
433 433
 
434 434
 		if ($op) {
435
-			$filtre = $a . $op . str_replace('\"', '"', $valeur);
435
+			$filtre = $a.$op.str_replace('\"', '"', $valeur);
436 436
 		}
437 437
 
438 438
 		if ($not) {
Please login to merge, or discard this patch.
Braces   +3 added lines, -6 removed lines patch added patch discarded remove patch
@@ -262,8 +262,7 @@  discard block
 block discarded – undo
262 262
 				$this->func_filtre = function () use ($filtres) {
263 263
 					return eval($filtres);
264 264
 				};
265
-			}
266
-			else {
265
+			} else {
267 266
 				$this->func_filtre = null;
268 267
 			}
269 268
 		}
@@ -346,16 +345,14 @@  discard block
 block discarded – undo
346 345
 					$v[$k] = $this->traduire_condition_sql_en_filtre($v[$k]);
347 346
 					if ($v[$k] === null) {
348 347
 						unset($v[$k]);
349
-					}
350
-					elseif ($v[$k] === 'true') {
348
+					} elseif ($v[$k] === 'true') {
351 349
 						if ($op === 'OR') {
352 350
 							return 'true';
353 351
 						}
354 352
 						if ($op === 'AND') {
355 353
 							unset($v[$k]);
356 354
 						}
357
-					}
358
-					elseif ($v[$k] === 'false') {
355
+					} elseif ($v[$k] === 'false') {
359 356
 						if ($op === 'OR') {
360 357
 							unset($v[$k]);
361 358
 						}
Please login to merge, or discard this patch.