This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Spindle\Collection; |
||
3 | |||
4 | class Collection implements \IteratorAggregate |
||
5 | { |
||
6 | use Traits\SortTrait; |
||
7 | use Traits\SetsTrait; |
||
8 | use Traits\TerminateTrait; |
||
9 | use Traits\LimitTrait; |
||
10 | use Traits\TransformTrait; |
||
11 | |||
12 | const TYPE_FOR = 'for'; |
||
13 | const TYPE_FOREACH = 'foreach'; |
||
14 | |||
15 | private $type = self::TYPE_FOREACH; |
||
16 | private $debug = false; |
||
17 | |||
18 | private $ops = []; |
||
19 | private $seed; |
||
20 | private $is_array; |
||
21 | private $vars = []; |
||
22 | private $fn_cnt = 0; |
||
23 | |||
24 | /** |
||
25 | * @return \Spindle\Collection |
||
26 | */ |
||
27 | 10 | public static function from($iterable, $debug = null) |
|
28 | { |
||
29 | 10 | return new static($iterable, $debug); |
|
30 | } |
||
31 | |||
32 | /** |
||
33 | * Generator-based range() |
||
34 | * @param int|string $start |
||
35 | * @param int|string $end |
||
36 | * @param int $step |
||
37 | * @param bool $debug |
||
38 | * @return \Spindle\Collection |
||
39 | */ |
||
40 | 23 | public static function range($start, $end, $step = 1, $debug = null) |
|
41 | { |
||
42 | 23 | if (!is_int($step) || $step < 1) { |
|
43 | 1 | throw new \InvalidArgumentException('$step must be natural number: ' . $step); |
|
44 | } |
||
45 | 22 | if (is_numeric($start) && is_numeric($end)) { |
|
46 | $seed = [ |
||
47 | 22 | '$_current = ' . $start, |
|
48 | 22 | '$_current <= ' . $end, |
|
49 | 22 | $step === 1 ? '++$_current' : '$_current += ' . $step, |
|
50 | 22 | ]; |
|
51 | 22 | } else { |
|
52 | $seed = [ |
||
53 | 1 | '$_current = ' . var_export($start, 1), |
|
54 | 1 | '$_current <= ' . var_export($end, 1), |
|
55 | 1 | implode(',', array_fill(0, $step, '++$_current')), |
|
56 | 1 | ]; |
|
57 | } |
||
58 | 22 | return new static($seed, $debug, self::TYPE_FOR); |
|
59 | } |
||
60 | |||
61 | /** |
||
62 | * Generator-based repeat() |
||
63 | * @param mixed $elem |
||
64 | * @param int $count |
||
65 | * @param bool $debug |
||
66 | */ |
||
67 | 2 | public static function repeat($elem, $count, $debug = null) |
|
68 | { |
||
69 | 2 | if (!is_int($count) || $count < 0) { |
|
70 | 1 | throw new \InvalidArgumentException('$count must be int >= 0. given: ' . gettype($count)); |
|
71 | } |
||
72 | $seed = [ |
||
73 | 1 | '$_current = $_elem, $_count = ' . var_export($count, 1), |
|
74 | 1 | '$_count > 0', |
|
75 | '--$_count' |
||
76 | 1 | ]; |
|
77 | 1 | $collection = new static($seed, $debug, self::TYPE_FOR); |
|
78 | 1 | $collection->vars['_elem'] = $elem; |
|
79 | 1 | return $collection; |
|
80 | } |
||
81 | |||
82 | /** |
||
83 | * @param iterable $seed |
||
84 | */ |
||
85 | 32 | public function __construct($seed, $debug = null, $type = null) |
|
86 | { |
||
87 | 32 | if (!is_array($seed) && !is_object($seed)) { |
|
88 | 1 | throw new \InvalidArgumentException('$seed should be iterable, given ' . gettype($seed)); |
|
89 | } |
||
90 | 31 | $this->seed = $seed; |
|
91 | 31 | if ($debug) { |
|
92 | 1 | $this->debug = true; |
|
93 | 1 | } |
|
94 | 31 | if ($type === self::TYPE_FOR) { |
|
95 | 23 | $this->type = $type; |
|
96 | 23 | $this->is_array = false; |
|
97 | 23 | return; |
|
98 | } |
||
99 | 16 | $this->is_array = is_array($seed); |
|
100 | 16 | } |
|
101 | |||
102 | 17 | private function step() |
|
103 | { |
||
104 | 17 | if ($this->debug) { |
|
105 | 1 | $this->toArray(); |
|
106 | 1 | return $this; |
|
107 | } |
||
108 | 16 | return $this; |
|
109 | } |
||
110 | |||
111 | /** |
||
112 | * @return \Generator|\ArrayIterator |
||
113 | */ |
||
114 | 1 | public function getIterator() |
|
115 | { |
||
116 | 1 | if ($this->is_array) { |
|
117 | 1 | return new \ArrayIterator($this->seed); |
|
118 | } |
||
119 | 1 | $ops = $this->ops; |
|
120 | 1 | $ops[] = 'yield $_key => $_;'; |
|
121 | 1 | $gen = self::evaluate( |
|
122 | 1 | $this->seed, |
|
123 | 1 | $this->vars, |
|
124 | 1 | $this->compile($ops), |
|
125 | 1 | '$_result = static function() use($_seed){', |
|
126 | '};' |
||
127 | 1 | ); |
|
128 | 1 | return $gen(); |
|
129 | } |
||
130 | |||
131 | /** |
||
132 | * @return array |
||
133 | */ |
||
134 | 19 | public function toArray() |
|
135 | { |
||
136 | 19 | if ($this->is_array) { |
|
137 | 10 | return $this->seed; |
|
138 | } |
||
139 | 15 | $ops = $this->ops; |
|
140 | 15 | $ops[] = ' $_result[$_key] = $_;'; |
|
141 | 15 | $array = self::evaluate( |
|
142 | 15 | $this->seed, |
|
143 | 15 | $this->vars, |
|
144 | 15 | $this->compile($ops), |
|
145 | 15 | '$_result = [];', |
|
146 | '' |
||
147 | 15 | ); |
|
148 | 15 | $this->type = self::TYPE_FOREACH; |
|
149 | 15 | $this->is_array = true; |
|
150 | 15 | $this->ops = []; |
|
151 | 15 | $this->seed = $array; |
|
152 | 15 | $this->vars = []; |
|
153 | 15 | $this->fn_cnt = 0; |
|
154 | 15 | return $array; |
|
155 | } |
||
156 | |||
157 | /** |
||
158 | * @return $this |
||
159 | */ |
||
160 | 1 | public function dump() |
|
161 | { |
||
162 | 1 | var_dump($this); |
|
163 | 1 | return $this; |
|
164 | } |
||
165 | |||
166 | /** |
||
167 | * @return $this |
||
168 | */ |
||
169 | 1 | public function assignTo(&$var = null) |
|
170 | { |
||
171 | 1 | $var = $this; |
|
172 | 1 | return $this; |
|
173 | } |
||
174 | |||
175 | /** |
||
176 | * @return $this |
||
177 | */ |
||
178 | 4 | public function assignArrayTo(&$var = null) |
|
179 | { |
||
180 | 4 | $var = $this->toArray(); |
|
181 | 4 | return $this; |
|
182 | } |
||
183 | |||
184 | /** |
||
185 | * @return string |
||
186 | */ |
||
187 | 1 | public function __toString() |
|
188 | { |
||
189 | 1 | return implode("\n", [ |
|
190 | 1 | static::class, |
|
191 | 1 | ' array-mode:' . (int)$this->is_array, |
|
192 | 1 | " codes:\n " . implode("\n ", $this->ops) |
|
193 | 1 | ]); |
|
194 | } |
||
195 | |||
196 | /** |
||
197 | * @return array |
||
198 | */ |
||
199 | 1 | public function __debugInfo() |
|
200 | { |
||
201 | 1 | if (is_array($this->seed)) { |
|
202 | 1 | $cnt = count($this->seed); |
|
203 | 1 | if ($cnt === 0) { |
|
204 | 1 | $seed = "empty array()"; |
|
205 | 1 | } else { |
|
206 | 1 | $first = gettype(current($this->seed)); |
|
207 | 1 | $seed = "array($first, ...($cnt items))"; |
|
208 | } |
||
209 | 1 | } else { |
|
210 | 1 | $seed = get_class($this->seed); |
|
211 | } |
||
212 | return [ |
||
213 | 1 | 'seed' => $seed, |
|
214 | 1 | 'code' => $this->compile($this->ops), |
|
215 | 1 | ]; |
|
216 | } |
||
217 | |||
218 | private static function evaluate($_seed, $_vars, $_code, $_before, $_after) |
||
219 | { |
||
220 | 24 | set_error_handler(function($severity, $message, $file, $line){ |
|
221 | 1 | throw new \ErrorException($message, 0, $severity, $file, $line); |
|
222 | 24 | }, E_ALL ^ E_DEPRECATED ^ E_USER_DEPRECATED); |
|
223 | try { |
||
224 | 24 | $_result = null; |
|
225 | 24 | extract($_vars); |
|
226 | 24 | eval("$_before \n $_code \n $_after"); |
|
0 ignored issues
–
show
|
|||
227 | 24 | } catch (\ParseError $e) { |
|
228 | throw new \RuntimeException($e->getMessage(), $e->getCode(), $e); |
||
229 | 24 | } finally { |
|
230 | 24 | restore_error_handler(); |
|
231 | } |
||
232 | 24 | return $_result; |
|
233 | } |
||
234 | |||
235 | 25 | private function compile($ops) |
|
236 | { |
||
237 | 25 | if ($this->type === self::TYPE_FOR) { |
|
238 | 20 | return $this->compileFor($ops, $this->seed); |
|
239 | } |
||
240 | |||
241 | 10 | return $this->compileForeach($ops); |
|
242 | } |
||
243 | |||
244 | 20 | private static function compileFor($ops, $seed) |
|
245 | { |
||
246 | 20 | array_unshift( |
|
247 | 20 | $ops, |
|
248 | 20 | '$_i = 0;', |
|
249 | 20 | 'for (' . implode('; ', $seed). ') {', |
|
250 | 20 | ' $_key = $_i;', |
|
251 | 20 | ' $_ = $_current;', |
|
252 | ' ++$_i;' |
||
253 | 20 | ); |
|
254 | 20 | $ops[] = '}'; |
|
255 | |||
256 | 20 | return implode("\n", $ops); |
|
257 | } |
||
258 | |||
259 | 10 | private static function compileForeach($ops) |
|
260 | { |
||
261 | 10 | array_unshift( |
|
262 | 10 | $ops, |
|
263 | 10 | '$_i = 0;', |
|
264 | 10 | 'foreach ($_seed as $_key => $_) {', |
|
265 | ' ++$_i;' |
||
266 | 10 | ); |
|
267 | 10 | $ops[] = '}'; |
|
268 | |||
269 | 10 | return implode("\n", $ops); |
|
270 | } |
||
271 | } |
||
272 |
On one hand,
eval
might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM,eval
prevents some optimization that they perform.