Completed
Push — master ( 36709f...e26ed7 )
by Hiraku
01:57
created

Collection::reject()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 12
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 12
loc 12
ccs 0
cts 9
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 1
crap 6
1
<?php
2
namespace Spindle;
3
4
class Collection implements \IteratorAggregate
5
{
6
    const TYPE_FOR = 'for';
7
    const TYPE_FOREACH = 'foreach';
8
9
    private $type = self::TYPE_FOREACH;
10
    private $debug = false;
11
12
    private $ops = [];
13
    private $seed;
14
    private $is_array;
15
    private $vars = [];
16
    private $fn_cnt = 0;
17
18
    /**
19
     * @return \Spindle\Collection
20
     */
21 7
    public static function from($iterable, $debug = null)
22
    {
23 7
        return new static($iterable, $debug);
24
    }
25
26
    /**
27
     * Generator-based range()
28
     * @param int|string $start
29
     * @param int|string $end
30
     * @param int $step
31
     * @param bool $debug
32
     * @return \Spindle\Collection
33
     */
34 13
    public static function range($start, $end, $step = 1, $debug = null)
35
    {
36 13
        if (is_numeric($start) && is_numeric($end)) {
37
            $seed = [
38 13
                '$_current = ' . $start,
39 13
                '$_current <= ' . $end,
40 13
                $step === 1 ? '++$_current' : '$_current += ' . $step,
41 13
            ];
42 13
        } else {
43
            $seed = [
44
                '$_current = ' . var_export($start, 1),
45
                '$_current <= ' . var_export($end, 1),
46
                implode(',', array_fill(0, $step, '++$_current')),
47
            ];
48
        }
49 13
        return new static($seed, $debug, self::TYPE_FOR);
0 ignored issues
show
Documentation introduced by
$seed is of type array<integer,string,{"0..."string","2":"string"}>, but the function expects a object<Spindle\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
50
    }
51
52
    /**
53
     * Generator-based repeat()
54
     * @param mixed $elem
55
     * @param int $count
56
     * @param bool $debug
57
     */
58 2
    public static function repeat($elem, $count, $debug = null)
59
    {
60 2
        if (!is_int($count) || $count < 0) {
61 1
            throw new \InvalidArgumentException('$count must be int >= 0. given: ' . gettype($count));
62
        }
63
        $seed = [
64 1
            '$_current = $_elem, $_count = ' . var_export($count, 1),
65 1
            '$_count > 0',
66
            '--$_count'
67 1
        ];
68 1
        $collection = new static($seed, $debug, self::TYPE_FOR);
0 ignored issues
show
Documentation introduced by
$seed is of type array<integer,string,{"0..."string","2":"string"}>, but the function expects a object<Spindle\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
69 1
        $collection->vars['_elem'] = $elem;
70 1
        return $collection;
71
    }
72
73
    /**
74
     * @param iterable $seed
75
     */
76 21
    public function __construct($seed, $debug = null, $type = null)
77
    {
78 21
        if (!is_array($seed) && !is_object($seed)) {
79 1
            throw new \InvalidArgumentException('$seed should be iterable, given ' . gettype($seed));
80
        }
81 20
        $this->seed = $seed;
82 20
        if ($debug) {
83
            $this->debug = true;
84
        }
85 20
        if ($type === self::TYPE_FOR) {
86 14
            $this->type = $type;
87 14
            $this->is_array = false;
88 14
            return;
89
        }
90 10
        $this->is_array = is_array($seed);
91 10
    }
92
93 8
    private function step()
94
    {
95 8
        if ($this->debug) {
96
            $this->toArray();
97
            return $this;
98
        }
99 8
        return $this;
100
    }
101
102
    /**
103
     * @param string|callable $fn '$_ > 100'
104
     * @return $this
105
     */
106 2 View Code Duplication
    public function filter($fn)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
107
    {
108 2
        $this->is_array = false;
109 2
        if (is_callable($fn)) {
110 1
            $fn_name = '_fn' . $this->fn_cnt++;
111 1
            $this->vars[$fn_name] = $fn;
112 1
            $this->ops[] = '    if (!$' . $fn_name . '($_)) continue;';
113 1
        } else {
114 2
            $this->ops[] = '    if (!(' . $fn . ')) continue;';
115
        }
116 2
        return $this->step();
117
    }
118
119
    /**
120
     * @param string|callable $fn '$_ > 100'
121
     * @return $this
122
     */
123 View Code Duplication
    public function reject($fn)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
124
    {
125
        $this->is_array = false;
126
        if (is_callable($fn)) {
127
            $fn_name = '_fn' . $this->fn_cnt++;
128
            $this->vars[$fn_name] = $fn;
129
            $this->ops[] = '    if ($' . $fn_name . '($_)) continue;';
130
        } else {
131
            $this->ops[] = '    if (' . $fn . ') continue;';
132
        }
133
        return $this->step();
134
    }
135
136
    /**
137
     * @param string|callable $fn '$_ * 2'
138
     * @return $this
139
     */
140 4 View Code Duplication
    public function map($fn)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
141
    {
142 4
        $this->is_array = false;
143 4
        if (is_callable($fn)) {
144 1
            $fn_name = '_fn' . $this->fn_cnt++;
145 1
            $this->vars[$fn_name] = $fn;
146 1
            $this->ops[] = '    $_ = $' . $fn_name . '($_);';
147 1
        } else {
148 4
            $this->ops[] = '    $_ = ' . $fn . ';';
149
        }
150 4
        return $this->step();
151
    }
152
153
    /**
154
     * @param string[] column
155
     * @return $this
156
     */
157 1
    public function column(array $columns)
158
    {
159 1
        $this->is_array = false;
160 1
        $defs = [];
161 1
        foreach ($columns as $key) {
162 1
            $exported = var_export($key, 1);
163 1
            $defs[] = "$exported => \$_[$exported]";
164 1
        }
165 1
        $this->ops[] = '    $_ = [' . implode(',', $defs) . '];';
166 1
        return $this->step();
167
    }
168
169
    /**
170
     * @param int $offset
171
     * @param ?int $length
0 ignored issues
show
Documentation introduced by
The doc-type ?int could not be parsed: Unknown type name "?int" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
172
     * @return $this
173
     */
174 1
    public function slice($offset, $length = null)
175
    {
176 1
        if ($offset < 0) {
177 1
            return new $this(array_slice($this->toArray(), $offset, $length), $this->debug);
178
        }
179 1
        $this->ops[] = '    if ($_i < ' . $offset . ') continue;';
180 1
        if ($length !== null) {
181 1
            $this->ops[] = '    if ($_i >= ' . ($offset + $length) . ') break;';
182 1
        }
183 1
        return $this->step();
184
    }
185
186
    /**
187
     * @param int $size
188
     * @return $this
189
     */
190 1
    public function chunk($size)
191
    {
192 1
        return new $this(array_chunk($this->toArray(), $size), $this->debug);
193
    }
194
195
    /**
196
     * @return \Spindle\Collection (new instance)
197
     */
198 1
    public function unique()
199
    {
200 1
        return new $this(array_unique($this->toArray()), $this->debug);
201
    }
202
203
    /**
204
     * @return \Spindle\Collection (new instance)
205
     */
206 1
    public function flip()
207
    {
208 1
        $this->is_array = false;
209 1
        $this->ops[] = 'list($_key, $_) = array($_, $_key);';
210 1
        return $this->step();
211
    }
212
213
    /**
214
     * @param string|callable $fn '$_carry + $_'
215
     * @param mixed $initial
216
     * @return mixed
217
     */
218 1
    public function reduce($fn, $initial = null)
219
    {
220 1
        $ops = $this->ops;
221 1
        $this->vars['_carry'] = $initial;
222 1
        if (is_callable($fn)) {
223 1
            $fn_name = '_fn' . $this->fn_cnt++;
224 1
            $this->vars[$fn_name] = $fn;
225 1
            $ops[] = '    $_carry = $' . $fn_name . '($_, $_carry);';
226 1
        } else {
227 1
            $ops[] = '    $_carry = ' . $fn . ';';
228
        }
229 1
        $after = '$_result = $_carry;';
230 1
        return self::evaluate($this->seed, $this->vars, $this->compile($ops), '', $after);
231
    }
232
233
    /**
234
     * @return int|float
235
     */
236 3 View Code Duplication
    public function sum()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
237
    {
238 3
        $ops = $this->ops;
239 3
        $before = '$_result = 0;';
240 3
        $ops[] = '    $_result += $_;';
241
242 3
        return self::evaluate($this->seed, $this->vars, $this->compile($ops), $before, '');
243
    }
244
245
    /**
246
     * @return int|float
247
     */
248 1 View Code Duplication
    public function product()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
249
    {
250 1
        $ops = $this->ops;
251 1
        $before = '$_result = 1;';
252 1
        $ops[] = '    $_result *= $_;';
253
254 1
        return self::evaluate($this->seed, $this->vars, $this->compile($ops), $before, '');
255
    }
256
257
    /**
258
     * @return \Spindle\Collection (new instance)
259
     */
260 1
    public function usort(callable $cmp)
261
    {
262 1
        $array = $this->toArray();
263 1
        usort($array, $cmp);
264 1
        return new $this($array, $this->debug);
265
    }
266
267
    /**
268
     * @return \Spindle\Collection (new instance)
269
     */
270 1
    public function rsort($sort_flags = \SORT_REGULAR)
271
    {
272 1
        $array = $this->toArray();
273 1
        rsort($array, $sort_flags);
274 1
        return new $this($array, $this->debug);
275
    }
276
277
    /**
278
     * @return \Spindle\Collection (new instance)
279
     */
280 1
    public function sort($sort_flags = \SORT_REGULAR)
281
    {
282 1
        $array = $this->toArray();
283 1
        sort($array, $sort_flags);
284 1
        return new $this($array, $this->debug);
285
    }
286
287
    /**
288
     * @return \Generator
289
     */
290 1 View Code Duplication
    public function getIterator()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
291
    {
292 1
        $ops = $this->ops;
293 1
        $ops[] = 'yield $_key => $_;';
294 1
        $gen = self::evaluate(
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $gen is correct as self::evaluate($this->se...() use($_seed){', '};') (which targets Spindle\Collection::evaluate()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
295 1
            $this->seed,
296 1
            $this->vars,
297 1
            $this->compile($ops),
298 1
            '$_result = static function() use($_seed){',
299
            '};'
300 1
        );
301 1
        return $gen();
302
    }
303
304
    /**
305
     * @return array
306
     */
307 12
    public function toArray()
308
    {
309 12
        if ($this->is_array) {
310 6
            return $this->seed;
311
        }
312 8
        $ops = $this->ops;
313 8
        $ops[] = '    $_result[$_key] = $_;';
314 8
        $array = self::evaluate(
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $array is correct as self::evaluate($this->se..., '$_result = [];', '') (which targets Spindle\Collection::evaluate()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
315 8
            $this->seed,
316 8
            $this->vars,
317 8
            $this->compile($ops),
318 8
            '$_result = [];',
319
            ''
320 8
        );
321 8
        $this->type = self::TYPE_FOREACH;
322 8
        $this->is_array = true;
323 8
        $this->ops = [];
324 8
        $this->seed = $array;
325 8
        $this->vars = [];
326 8
        $this->fn_cnt = 0;
327 8
        return $array;
328
    }
329
330
    /**
331
     * @return $this
332
     */
333 1
    public function dump()
334
    {
335 1
        var_dump($this);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($this); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
336 1
        return $this;
337
    }
338
339
    /**
340
     * @return $this
341
     */
342 1
    public function assignTo(&$var = null)
343
    {
344 1
        $var = new $this($this->toArray());
345 1
        return $var;
346
    }
347
348
    /**
349
     * @return $this
350
     */
351 1
    public function assignArrayTo(&$var = null)
352
    {
353 1
        $var = $this->toArray();
354 1
        return $this;
355
    }
356
357
    /**
358
     * @return string
359
     */
360 1
    public function __toString()
361
    {
362 1
        return implode("\n", [
363 1
            static::class,
364 1
            ' array-mode:' . (int)$this->is_array,
365 1
            " codes:\n  " . implode("\n  ", $this->ops)
366 1
        ]);
367
    }
368
369
    /**
370
     * @return array
371
     */
372 1
    public function __debugInfo()
373
    {
374 1
        if (is_array($this->seed)) {
375 1
            $cnt = count($this->seed);
376 1
            if ($cnt === 0) {
377 1
                $seed = "empty array()";
378 1
            } else {
379 1
                $first = gettype(current($this->seed));
380 1
                $seed = "array($first, ...($cnt items))";
381
            }
382 1
        } else {
383 1
            $seed = get_class($this->seed);
384
        }
385
        return [
386 1
            'seed' => $seed,
387 1
            'code' => $this->compile($this->ops),
388 1
        ];
389
    }
390
391
    private static function evaluate($_seed, $_vars, $_code, $_before, $_after)
0 ignored issues
show
Unused Code introduced by
The parameter $_seed 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...
392
    {
393 14
        $_old_handler = set_error_handler(function($severity, $message, $file, $line){
394
            throw new \ErrorException($message, 0, $severity, $file, $line);
395 14
        }, E_ALL ^ E_DEPRECATED ^ E_USER_DEPRECATED);
396
        try {
397 14
            $_result = null;
398 14
            extract($_vars);
399 14
            eval("$_before \n $_code \n $_after");
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

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.

Loading history...
400 14
        } catch (\ParseError $e) {
0 ignored issues
show
Bug introduced by
The class ParseError does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
401
            throw new \RuntimeException($e->getMessage(), $e->getCode(), $e);
402 14
        } finally {
403 14
            set_error_handler($_old_handler);
404
        }
405 14
        return $_result;
406
    }
407
408 15
    private function compile($ops)
409
    {
410 15
        if ($this->type === self::TYPE_FOR) {
411 12
            return $this->compileFor($ops, $this->seed);
412
        }
413
414 3
        return $this->compileForeach($ops);
415
    }
416
417 12
    private static function compileFor($ops, $seed)
418
    {
419 12
        array_unshift(
420 12
            $ops,
421 12
            '$_i = 0;',
422 12
            'for (' . implode('; ', $seed). ') {',
423 12
            '    $_key = $_i;',
424 12
            '    $_ = $_current;',
425
            '    ++$_i;'
426 12
        );
427 12
        $ops[] = '}';
428
429 12
        return implode("\n", $ops);
430
    }
431
432 3
    private static function compileForeach($ops)
433
    {
434 3
        array_unshift(
435 3
            $ops,
436 3
            '$_i = 0;',
437 3
            'foreach ($_seed as $_key => $_) {',
438
            '    ++$_i;'
439 3
        );
440 3
        $ops[] = '}';
441
442 3
        return implode("\n", $ops);
443
    }
444
}
445