Collection   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 268
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 99.25%

Importance

Changes 0
Metric Value
wmc 34
lcom 1
cbo 5
dl 0
loc 268
ccs 133
cts 134
cp 0.9925
rs 9.2
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A from() 0 4 1
B range() 0 20 6
A repeat() 0 14 3
B __construct() 0 16 5
A step() 0 8 2
A getIterator() 0 16 2
A toArray() 0 22 2
A dump() 0 5 1
A assignTo() 0 5 1
A assignArrayTo() 0 5 1
A __toString() 0 8 1
A __debugInfo() 0 18 3
A evaluate() 0 16 2
A compile() 0 8 2
A compileFor() 0 14 1
A compileForeach() 0 12 1
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);
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\Collection\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...
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);
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\Collection\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...
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()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
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(
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\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...
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(
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\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...
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);
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...
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)
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...
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
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...
227 24
        } 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...
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