1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Enzyme\Loopy; |
4
|
|
|
|
5
|
|
|
use ArrayAccess; |
6
|
|
|
use Closure; |
7
|
|
|
use Enzyme\Loopy\Filters\FilterInterface; |
8
|
|
|
|
9
|
|
|
class Each implements LooperInterface |
10
|
|
|
{ |
11
|
|
|
/** |
12
|
|
|
* If this loop will go deep into the enumerated object. |
13
|
|
|
* |
14
|
|
|
* @var boolean |
15
|
|
|
*/ |
16
|
|
|
protected $deep; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Stores the current cycle value. |
20
|
|
|
* |
21
|
|
|
* @var integer |
22
|
|
|
*/ |
23
|
|
|
protected $cycle; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Stored the current index value. |
27
|
|
|
* |
28
|
|
|
* @var integer |
29
|
|
|
*/ |
30
|
|
|
protected $index; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Stored the current depth value. |
34
|
|
|
* |
35
|
|
|
* @var integer |
36
|
|
|
*/ |
37
|
|
|
protected $depth; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* The optional filter to pass the key and value through |
41
|
|
|
* upon each iteration. |
42
|
|
|
* |
43
|
|
|
* @var FilterInterface |
44
|
|
|
*/ |
45
|
|
|
protected $filter; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Creates a new Each loop. |
49
|
|
|
* |
50
|
|
|
* @param bool $deep Whether to traverse deeply. |
51
|
|
|
* |
52
|
|
|
* @param FilterInterface|null $filter The optional filter to apply to each iteration. |
53
|
|
|
*/ |
54
|
|
|
private function __construct($deep, FilterInterface $filter = null) |
55
|
|
|
{ |
56
|
|
|
$this->deep = $deep; |
57
|
|
|
$this->filter = $filter; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Creates a new shallow Each loopy. |
62
|
|
|
* |
63
|
|
|
* @param FilterInterface|null $filter The optional filter to apply to each iteration. |
64
|
|
|
* |
65
|
|
|
* @return Each |
66
|
|
|
*/ |
67
|
|
|
public static function shallow(FilterInterface $filter = null) |
68
|
|
|
{ |
69
|
|
|
$deep = false; |
70
|
|
|
|
71
|
|
|
return new static($deep, $filter); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Creates a new deep Each loopy. |
76
|
|
|
* |
77
|
|
|
* @param FilterInterface|null $filter The optional filter to apply to each iteration. |
78
|
|
|
* |
79
|
|
|
* @return Each |
80
|
|
|
*/ |
81
|
|
|
public static function deep(FilterInterface $filter = null) |
82
|
|
|
{ |
83
|
|
|
$deep = true; |
84
|
|
|
|
85
|
|
|
return new static($deep, $filter); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* {@inheritdoc} |
90
|
|
|
*/ |
91
|
|
|
public function begin($enumerable, Closure $function, $cycles = 1) |
92
|
|
|
{ |
93
|
|
|
if($this->isEnumerable($enumerable) === false) { |
94
|
|
|
throw new InvalidLoopException('The supplied $enumerable object cannot be enumerated.'); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
$this->index = 0; |
98
|
|
|
$this->depth = 0; |
99
|
|
|
$this->cycle = 0; |
100
|
|
|
|
101
|
|
|
for ($i = 0; $i < $cycles; $i++) { |
102
|
|
|
$this->doSingleForeachCycle($enumerable, $function); |
103
|
|
|
$this->cycle++; |
104
|
|
|
} |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Performs a single foreach cycle. |
109
|
|
|
* |
110
|
|
|
* @param mixed $enumerable The object to iterate over. |
111
|
|
|
* @param Closure $function The callback function. |
112
|
|
|
* |
113
|
|
|
* @return void |
114
|
|
|
*/ |
115
|
|
|
protected function doSingleForeachCycle($enumerable, Closure $function) |
116
|
|
|
{ |
117
|
|
|
foreach ($enumerable as $key => $value) { |
118
|
|
|
if ($this->canGoDeeperInto($value)) { |
119
|
|
|
$this->doSingleForeachCycle($value, $function); |
120
|
|
|
} elseif ($this->passesThroughFilter($key, $value)) { |
121
|
|
|
$this->processIteration($key, $value, $function); |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Checks if the given key and value pass through the filter, |
128
|
|
|
* if one exists. |
129
|
|
|
* |
130
|
|
|
* @param mixed $key The key. |
131
|
|
|
* @param mixed $value The value. |
132
|
|
|
* |
133
|
|
|
* @return boolean |
134
|
|
|
*/ |
135
|
|
|
protected function passesThroughFilter($key, $value) |
136
|
|
|
{ |
137
|
|
|
if ($this->filter === null) { |
138
|
|
|
return true; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
return $this->filter->passes($key, $value); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Checks whether we can go deeper into this value, |
146
|
|
|
* if the deep setting is set to true. |
147
|
|
|
* |
148
|
|
|
* @param mixed $value The value to check. |
149
|
|
|
* |
150
|
|
|
* @return boolean |
151
|
|
|
*/ |
152
|
|
|
protected function canGoDeeperInto($value) |
153
|
|
|
{ |
154
|
|
|
return $this->deep === true && $this->isEnumerable($value); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Checks whether the given item is enumerable. |
159
|
|
|
* |
160
|
|
|
* @param mixed $item The item. |
161
|
|
|
* |
162
|
|
|
* @return boolean |
163
|
|
|
*/ |
164
|
|
|
protected function isEnumerable($item) |
165
|
|
|
{ |
166
|
|
|
return is_array($item) === true || ($item instanceof ArrayAccess) === true; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Processes this iterations key and value. |
171
|
|
|
* |
172
|
|
|
* @param mixed $key The key. |
173
|
|
|
* @param mixed $value The value. |
174
|
|
|
* @param Closure $function The callback function |
175
|
|
|
* |
176
|
|
|
* @return void |
177
|
|
|
*/ |
178
|
|
|
protected function processIteration($key, $value, $function) |
179
|
|
|
{ |
180
|
|
|
$function($this->packBag($key, $value, $function)); |
|
|
|
|
181
|
|
|
$this->index++; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Packs and returns a new bag with the given values. |
186
|
|
|
* |
187
|
|
|
* @param mixed $key The key. |
188
|
|
|
* @param mixed $value The value. |
189
|
|
|
* |
190
|
|
|
* @return Bag |
191
|
|
|
*/ |
192
|
|
|
protected function packBag($key, $value) |
193
|
|
|
{ |
194
|
|
|
$index = $this->index; |
195
|
|
|
$cycle = $this->cycle; |
196
|
|
|
$depth = $this->depth; |
197
|
|
|
|
198
|
|
|
return new Bag(compact('key', 'value', 'index', 'cycle', 'depth')); |
199
|
|
|
} |
200
|
|
|
} |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.