Completed
Push — master ( 1a3b2f...b50fb4 )
by cam
09:59
created

IterDecorator::fetch()   B

Complexity

Conditions 10
Paths 9

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
nc 9
nop 0
dl 0
loc 32
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
4
/***************************************************************************\
5
 *  SPIP, Systeme de publication pour l'internet                           *
6
 *                                                                         *
7
 *  Copyright (c) 2001-2018                                                *
8
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
9
 *                                                                         *
10
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
11
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
12
\***************************************************************************/
13
14
if (!defined('_ECRIRE_INC_VERSION')) {
15
	return;
16
}
17
18
/**
19
 * Fabrique d'iterateur
20
 * permet de charger n'importe quel iterateur IterateurXXX
21
 * fourni dans le fichier iterateurs/xxx.php
22
 *
23
 */
24
class IterFactory {
25
	public static function create($iterateur, $command, $info = null) {
26
27
		// cas des SI {si expression} analises tres tot
28
		// pour eviter le chargement de tout iterateur
29
		if (isset($command['si'])) {
30
			foreach ($command['si'] as $si) {
31
				if (!$si) {
32
					// $command pour boucle SQL peut generer des erreurs de compilation
33
					// s'il est transmis alors qu'on est dans un iterateur vide
34
					return new IterDecorator(new EmptyIterator(), array(), $info);
35
				}
36
			}
37
		}
38
39
		// chercher un iterateur PHP existant (par exemple dans SPL)
40
		// (il faudrait passer l'argument ->sql_serveur
41
		// pour etre certain qu'on est sur un "php:")
42
		if (class_exists($iterateur)) {
43
			$a = isset($command['args']) ? $command['args'] : array();
44
45
			// permettre de passer un Iterateur directement {args #ITERATEUR} :
46
			// si on recoit deja un iterateur en argument, on l'utilise
47
			if (count($a) == 1 and is_object($a[0]) and is_subclass_of($a[0], 'Iterator')) {
48
				$iter = $a[0];
49
50
				// sinon, on cree un iterateur du type donne
51
			} else {
52
				// arguments de creation de l'iterateur...
53
				// (pas glop)
54
				try {
55
					switch (count($a)) {
56
						case 0:
57
							$iter = new $iterateur();
58
							break;
59
						case 1:
60
							$iter = new $iterateur($a[0]);
61
							break;
62
						case 2:
63
							$iter = new $iterateur($a[0], $a[1]);
64
							break;
65 View Code Duplication
						case 3:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
66
							$iter = new $iterateur($a[0], $a[1], $a[2]);
67
							break;
68 View Code Duplication
						case 4:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
69
							$iter = new $iterateur($a[0], $a[1], $a[2], $a[3]);
70
							break;
71
					}
72
				} catch (Exception $e) {
73
					spip_log("Erreur de chargement de l'iterateur $iterateur");
74
					spip_log($e->getMessage());
75
					$iter = new EmptyIterator();
76
				}
77
			}
78
		} else {
79
			// chercher la classe d'iterateur
80
			// IterateurXXX
81
			// definie dans le fichier iterateurs/xxx.php
82
			$class = "Iterateur" . $iterateur;
83
			if (!class_exists($class)) {
84
				if (!include_spip("iterateur/" . strtolower($iterateur))
85
					or !class_exists($class)
86
				) {
87
					die("Iterateur $iterateur non trouv&#233;");
0 ignored issues
show
Coding Style Compatibility introduced by
The method create() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The variable $iter does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
96
	}
97
}
98
99
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 = array();
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();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Iterator as the method err() does only exist in the following implementations of said interface: IterDecorator.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
149
		}
150
		if (property_exists($this->iter, 'err')) {
151
			return $this->iter->err;
0 ignored issues
show
Bug introduced by
Accessing err on the interface Iterator suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
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)
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (rewind() instead of __construct()). Are you sure this is correct? If so, you might want to change this to $this->rewind().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
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)) {
0 ignored issues
show
Bug introduced by
Accessing err on the interface Iterator suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
177
			$this->err = $this->iter->err;
0 ignored issues
show
Bug introduced by
Accessing err on the interface Iterator suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
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 (is_object($this->iter)
205
			and method_exists($this->iter, $nom)
206
		) {
207
			try {
208
				return $this->iter->$nom();
209
			} catch (Exception $e) {
210
				// #GETCHILDREN sur un fichier de DirectoryIterator ...
211
				spip_log("Methode $nom en echec sur " . get_class($this->iter));
212
				spip_log("Cela peut être normal : retour d'une ligne de resultat ne pouvant pas calculer cette methode");
213
214
				return '';
215
			}
216
		}
217
		/*
218
		if (property_exists($this->iter, $nom)) {
219
			return $this->iter->$nom;
220
		}*/
221
		// cle et valeur par defaut
222
		// ICI PLANTAGE SI ON NE CONTROLE PAS $nom
223
		if (in_array($nom, array('cle', 'valeur'))
224
			and method_exists($this, $nom)
225
		) {
226
			return $this->$nom();
227
		}
228
229
		// Par defaut chercher en xpath dans la valeur()
230
		return table_valeur($this->valeur(), $nom, null);
231
	}
232
233
234
	private function calculer_filtres() {
235
236
		// Issu de calculer_select() de public/composer L.519
237
		// TODO: externaliser...
238
		//
239
		// retirer les criteres vides:
240
		// {X ?} avec X absent de l'URL
241
		// {par #ENV{X}} avec X absent de l'URL
242
		// IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil)
243
		if ($where = &$this->command['where']) {
244
			foreach ($where as $k => $v) {
245 View Code Duplication
				if (is_array($v)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246
					if ((count($v) >= 2) && ($v[0] == 'REGEXP') && ($v[2] == "'.*'")) {
247
						$op = false;
248
					} elseif ((count($v) >= 2) && ($v[0] == 'LIKE') && ($v[2] == "'%'")) {
249
						$op = false;
250
					} else {
251
						$op = $v[0] ? $v[0] : $v;
252
					}
253
				} else {
254
					$op = $v;
255
				}
256
				if ((!$op) or ($op == 1) or ($op == '0=0')) {
257
					unset($where[$k]);
258
				}
259
				// traiter {cle IN a,b} ou {valeur !IN a,b}
260
				// prendre en compte le cas particulier de sous-requetes
261
				// produites par sql_in quand plus de 255 valeurs passees a IN
262
				if (preg_match_all(',\s+IN\s+(\(.*\)),', $op, $s_req)) {
263
					$req = '';
264
					foreach ($s_req[1] as $key => $val) {
265
						$req .= trim($val, '(,)') . ',';
266
					}
267
					$req = '(' . rtrim($req, ',') . ')';
268
				}
269
				if (preg_match(',^\(\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\)(?:\s+(AND|OR)\s+\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\))*\)$,',
270
					$op, $regs)) {
271
					$this->ajouter_filtre($regs[1], 'IN', strlen($req) ? $req : $regs[3], $regs[2]);
0 ignored issues
show
Bug introduced by
The variable $req does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
272
					unset($op, $where[$k]);
273
				}
274
			}
275
			foreach ($where as $k => $v) {
276
				// 3 possibilites : count($v) =
277
				// * 1 : {x y} ; on recoit $v[0] = y
278
				// * 2 : {x !op y} ; on recoit $v[0] = 'NOT', $v[1] = array() // array du type {x op y}
279
				// * 3 : {x op y} ; on recoit $v[0] = 'op', $v[1] = x, $v[2] = y
280
281
				// 1 : forcement traite par un critere, on passe
282
				if (count($v) == 1) {
283
					continue;
284
				}
285
				if (count($v) == 2 and is_array($v[1])) {
286
					$this->ajouter_filtre($v[1][1], $v[1][0], $v[1][2], 'NOT');
287
				}
288
				if (count($v) == 3) {
289
					$this->ajouter_filtre($v[1], $v[0], $v[2]);
290
				}
291
			}
292
		}
293
294
		// critere {2,7}
295
		if (isset($this->command['limit']) and $this->command['limit']) {
296
			$limit = explode(',', $this->command['limit']);
297
			$this->offset = $limit[0];
298
			$this->limit = $limit[1];
299
		}
300
301
		// Creer la fonction de filtrage sur $this
302
		if ($this->filtre) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->filtre of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
303
			$filtres = 'return (' . join(') AND (', $this->filtre) . ');';
304
			$this->func_filtre = function () use ($filtres) {
0 ignored issues
show
Documentation Bug introduced by
It seems like function () use($filtres...eturn eval($filtres); } of type object<Closure> is incompatible with the declared type string of property $func_filtre.

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

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

Loading history...
305
				return eval($filtres);
306
			};
307
		}
308
	}
309
310
311
	protected function ajouter_filtre($cle, $op, $valeur, $not = false) {
312
		if (method_exists($this->iter, 'exception_des_criteres')) {
313
			if (in_array($cle, $this->iter->exception_des_criteres())) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Iterator as the method exception_des_criteres() does only exist in the following implementations of said interface: IterateurCONDITION, IterateurDATA.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
314
				return;
315
			}
316
		}
317
		// TODO: analyser le filtre pour refuser ce qu'on ne sait pas traiter ?
318
		# mais c'est normalement deja opere par calculer_critere_infixe()
319
		# qui regarde la description 'desc' (en casse reelle d'ailleurs : {isDir=1}
320
		# ne sera pas vu si l'on a defini desc['field']['isdir'] pour que #ISDIR soit present.
321
		# il faudrait peut etre definir les 2 champs isDir et isdir... a reflechir...
322
323
		# if (!in_array($cle, array('cle', 'valeur')))
324
		#	return;
325
326
		$a = '$this->get_select(\'' . $cle . '\')';
327
328
		$filtre = '';
329
330
		if ($op == 'REGEXP') {
331
			$filtre = 'match(' . $a . ', ' . str_replace('\"', '"', $valeur) . ')';
332
			$op = '';
333
		} else {
334
			if ($op == 'LIKE') {
335
				$valeur = str_replace(array('\"', '_', '%'), array('"', '.', '.*'), preg_quote($valeur));
336
				$filtre = 'match(' . $a . ', ' . $valeur . ')';
337
				$op = '';
338
			} else {
339
				if ($op == '=') {
340
					$op = '==';
341
				} else {
342
					if ($op == 'IN') {
343
						$filtre = 'in_array(' . $a . ', array' . $valeur . ')';
344
						$op = '';
345
					} else {
346
						if (!in_array($op, array('<', '<=', '>', '>='))) {
347
							spip_log('operateur non reconnu ' . $op); // [todo] mettre une erreur de squelette
348
							$op = '';
349
						}
350
					}
351
				}
352
			}
353
		}
354
355
		if ($op) {
356
			$filtre = $a . $op . str_replace('\"', '"', $valeur);
357
		}
358
359
		if ($not) {
360
			$filtre = "!($filtre)";
361
		}
362
363
		if ($filtre) {
364
			$this->filtre[] = $filtre;
365
		}
366
	}
367
368
369
	public function next() {
370
		$this->pos++;
371
		parent::next();
372
	}
373
374
	/**
375
	 * revient au depart
376
	 *
377
	 * @return void
378
	 */
379
	public function rewind() {
380
		$this->pos = 0;
381
		$this->fetched = 0;
382
		parent::rewind();
383
	}
384
385
386
	# Extension SPIP des iterateurs PHP
387
	/**
388
	 * type de l'iterateur
389
	 *
390
	 * @var string
391
	 */
392
	protected $type;
393
394
	/**
395
	 * parametres de l'iterateur
396
	 *
397
	 * @var array
398
	 */
399
	protected $command;
400
401
	/**
402
	 * infos de compilateur
403
	 *
404
	 * @var array
405
	 */
406
	protected $info;
407
408
	/**
409
	 * position courante de l'iterateur
410
	 *
411
	 * @var int
412
	 */
413
	protected $pos = null;
414
415
	/**
416
	 * nombre total resultats dans l'iterateur
417
	 *
418
	 * @var int
419
	 */
420
	protected $total = null;
421
422
	/**
423
	 * nombre maximal de recherche pour $total
424
	 * si l'iterateur n'implemente pas de fonction specifique
425
	 */
426
	protected $max = 100000;
427
428
429
	/**
430
	 * Liste des champs a inserer dans les $row
431
	 * retournes par ->fetch()
432
	 */
433
	protected $select = array();
434
435
436
	/**
437
	 * aller a la position absolue n,
438
	 * comptee depuis le debut
439
	 *
440
	 * @param int $n
441
	 *   absolute pos
442
	 * @param string $continue
0 ignored issues
show
Documentation introduced by
Should the type for parameter $continue not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
443
	 *   param for sql_ api
444
	 * @return bool
445
	 *   success or fail if not implemented
446
	 */
447
	public function seek($n = 0, $continue = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $continue is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
448
		if ($this->func_filtre or !method_exists($this->iter, 'seek') or !$this->iter->seek($n)) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Iterator as the method seek() does only exist in the following implementations of said interface: ArrayIterator, DirectoryIterator, FilesystemIterator, GlobIterator, IterDecorator, IterateurSQL, LimitIterator, Phar, PharData, RecursiveArrayIterator, RecursiveDirectoryIterator, SQLiteResult, SplFileObject, SplTempFileObject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
449
			$this->seek_loop($n);
450
		}
451
		$this->pos = $n;
452
		$this->fetched = $n;
453
454
		return true;
455
	}
456
457
	/*
458
	 * aller a la position $n en parcourant
459
	 * un par un tous les elements
460
	 */
461
	private function seek_loop($n) {
462
		if ($this->pos > $n) {
463
			$this->rewind();
464
		}
465
466
		while ($this->pos < $n and $this->valid()) {
467
			$this->next();
468
		}
469
470
		return true;
471
	}
472
473
	/**
474
	 * Avancer de $saut pas
475
	 *
476
	 * @param  $saut
477
	 * @param  $max
478
	 * @return int
479
	 */
480
	public function skip($saut, $max = null) {
481
		// pas de saut en arriere autorise pour cette fonction
482
		if (($saut = intval($saut)) <= 0) {
483
			return $this->pos;
484
		}
485
		$seek = $this->pos + $saut;
486
		// si le saut fait depasser le maxi, on libere la resource
487
		// et on sort
488
		if (is_null($max)) {
489
			$max = $this->count();
490
		}
491
492
		if ($seek >= $max or $seek >= $this->count()) {
493
			// sortie plus rapide que de faire next() jusqu'a la fin !
494
			$this->free();
495
496
			return $max;
497
		}
498
499
		$this->seek($seek);
500
501
		return $this->pos;
502
	}
503
504
	/**
505
	 * Renvoyer un tableau des donnees correspondantes
506
	 * a la position courante de l'iterateur
507
	 * en controlant si on respecte le filtre
508
	 * Appliquer aussi le critere {offset,limit}
509
	 *
510
	 * @return array|bool
511
	 */
512
	public function fetch() {
513
		if (method_exists($this->iter, 'fetch')) {
514
			return $this->iter->fetch();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Iterator as the method fetch() does only exist in the following implementations of said interface: IterDecorator, IterateurSQL, SQLiteResult.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
515
		} else {
516
517
			while ($this->valid()
518
				and (
519
					!$this->accept()
520
					or (isset($this->offset) and $this->fetched++ < $this->offset)
521
				)) {
522
				$this->next();
523
			}
524
525
			if (!$this->valid()) {
526
				return false;
527
			}
528
529
			if (isset($this->limit)
530
				and $this->fetched > $this->offset + $this->limit
531
			) {
532
				return false;
533
			}
534
535
			$r = array();
536
			foreach ($this->select as $nom) {
537
				$r[$nom] = $this->get_select($nom);
538
			}
539
			$this->next();
540
541
			return $r;
542
		}
543
	}
544
545
	// retourner la cle pour #CLE
546
	public function cle() {
547
		return $this->key();
548
	}
549
550
	// retourner la valeur pour #VALEUR
551
	public function valeur() {
552
		# attention PHP est mechant avec les objets, parfois il ne les
553
		# clone pas proprement (directoryiterator sous php 5.2.2)
554
		# on se rabat sur la version __toString()
555
		if (is_object($v = $this->current())) {
556
			if (method_exists($v, '__toString')) {
557
				$v = $v->__toString();
558
			} else {
559
				$v = (array)$v;
560
			}
561
		}
562
563
		return $v;
564
	}
565
566
	/**
567
	 * Accepte-t-on l'entree courante lue ?
568
	 * On execute les filtres pour le savoir.
569
	 **/
570
	public function accept() {
571
		if ($f = $this->func_filtre) {
572
			return $f();
573
		}
574
575
		return true;
576
	}
577
578
	/**
579
	 * liberer la ressource
580
	 *
581
	 * @return bool
582
	 */
583
	public function free() {
584
		if (method_exists($this->iter, 'free')) {
585
			$this->iter->free();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Iterator as the method free() does only exist in the following implementations of said interface: IterDecorator, IterateurSQL.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
586
		}
587
		$this->pos = $this->total = 0;
588
589
		return true;
590
	}
591
592
	/**
593
	 * Compter le nombre total de resultats
594
	 * pour #TOTAL_BOUCLE
595
	 *
596
	 * @return int
597
	 */
598
	public function count() {
599
		if (is_null($this->total)) {
600
			if (method_exists($this->iter, 'count')
601
				and !$this->func_filtre
602
			) {
603
				return $this->total = $this->iter->count();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Iterator as the method count() does only exist in the following implementations of said interface: ArrayIterator, CachingIterator, GlobIterator, HttpMessage, HttpRequestPool, IterDecorator, IterateurCONDITION, IterateurDATA, IterateurSQL, MongoCursor, MongoGridFSCursor, Phar, PharData, RecursiveArrayIterator, RecursiveCachingIterator, SQLiteResult, SimpleXMLIterator, SplDoublyLinkedList, SplFixedArray, SplHeap, SplMaxHeap, SplMinHeap, SplObjectStorage, SplPriorityQueue, SplQueue, SplStack.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
604
			} else {
605
				// compter les lignes et rembobiner
606
				$total = 0;
607
				$pos = $this->pos; // sauver la position
608
				$this->rewind();
609
				while ($this->fetch() and $total < $this->max) {
610
					$total++;
611
				}
612
				$this->seek($pos);
613
				$this->total = $total;
614
			}
615
		}
616
617
		return $this->total;
618
	}
619
620
}
621