Issues (55)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Collection.php (9 issues)

Upgrade to new PHP Analysis Engine

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);
0 ignored issues
show
$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
$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
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
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
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
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
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
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