Issues (6)

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 (6 issues)

Labels
Severity

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
3
namespace vakata\collection;
4
5
class Collection implements \Iterator, \ArrayAccess, \Serializable, \Countable
6
{
7
    protected $array = null;
8
    protected $stack = [];
9
    protected $iterator = null;
10
    protected $key = null;
11
    protected $val = null;
12
13 4
    protected static function rangeGenerator($low, $high, $step = 1)
14
    {
15 4
        $k = -1;
16 4
        for ($i = $low; $i <= $high; $i += $step) {
17 4
            yield ++$k => $i;
18
        }
19 4
    }
20
    /**
21
     * Create a collection based on a range generator
22
     * @param  int|float  $low  start value
23
     * @param  int|float  $high end value
24
     * @param  int|float  $step increment
25
     * @return Collection
26
     */
27 4
    public static function range($low, $high, $step = 1) : Collection
28
    {
29 4
        return new static(static::rangeGenerator($low, $high, $step));
30
    }
31
    /**
32
     * A static alias of the __constructor
33
     * @param  mixed  $input  Anything iterable
34
     * @return Collection
35
     */
36 53
    public static function from($input) : Collection
37
    {
38 53
        return new static($input);
39
    }
40
    /**
41
     * Create an instance
42
     * @param  mixed  $input  Anything iterable
43
     */
44 57
    public function __construct($input = [])
45
    {
46 57
        if (is_object($input)) {
47 28
            if ($input instanceof \Iterator) {
48 5
                $this->array = $input;
49 5
                $this->iterator = $input;
50 23
            } else if ($input instanceof self) {
51
                $this->array = $input->toArray();
52
            } else {
53 23
                $input = get_object_vars($input);
54
            }
55
        }
56 57
        if (is_array($input)) {
57 53
            $this->array = new \ArrayObject($input);
58 53
            $this->iterator = $this->array->getIterator();
59
        }
60 57
    }
61
    public function __clone()
62
    {
63
        $this->array = new \ArrayObject(iterator_to_array($this));
64
        $this->stack = [];
65
        $this->iterator = $this->array->getIterator();
66
    }
67 1
    public function __toString()
68
    {
69 1
        return implode(', ', $this->toArray());
70
    }
71 1
    public function serialize() {
72 1
        return serialize($this->toArray());
73
    }
74 1
    public function unserialize($array) {
75 1
        $this->array = new \ArrayObject(unserialize($array));
76 1
        $this->stack = [];
77 1
        $this->iterator = $this->array->getIterator();
78 1
    }
79
80
    /**
81
     * Applies all pending operations
82
     * @return $this
83
     */
84 46
    public function squash() : Collection
85
    {
86 46
        if (count($this->stack) || !($this->array instanceof \ArrayObject)) {
87 18
            $this->array = new \ArrayObject(iterator_to_array($this));
88 18
            $this->stack = [];
89 18
            $this->iterator = $this->array->getIterator();
90
        }
91 46
        return $this;
92
    }
93
    /**
94
     * Get an actual array from the collection
95
     * @return array
96
     */
97 45
    public function toArray() : array
98
    {
99 45
        return $this->squash()->array->getArrayCopy();
0 ignored issues
show
The method getArrayCopy does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
100
    }
101
    /**
102
     * Gets the first value in the collection or null if empty
103
     * @return mixed
104
     */
105 3
    public function value()
106
    {
107 3
        foreach ($this as $v) {
108 3
            return $v;
109
        }
110 1
        return null;
111
    }
112
113
    // iterator
114 29
    public function key()
115
    {
116 29
        return $this->key;
117
    }
118 34
    public function current()
119
    {
120 34
        return $this->val;
121
    }
122 34
    public function rewind()
123
    {
124 34
        return $this->iterator->rewind();
125
    }
126 31
    public function next()
127
    {
128 31
        return $this->iterator->next();
129
    }
130 34
    public function valid()
131
    {
132 34
        while ($this->iterator->valid()) {
133 34
            $this->val = $this->iterator->current();
134 34
            $this->key = $this->iterator->key();
135 34
            $con = false;
136 34
            foreach ($this->stack as $action) {
137 14
                if ($action[0] === 'filter') {
138 10
                    if (!call_user_func($action[1], $this->val, $this->key, $this)) {
139 10
                        $con = true;
140 10
                        break;
141
                    }
142
                }
143 14
                if ($action[0] === 'map') {
144 7
                    $this->val = call_user_func($action[1], $this->val, $this->key, $this);
145
                }
146 14
                if ($action[0] === 'mapKey') {
147 14
                    $this->key = call_user_func($action[1], $this->val, $this->key, $this);
148
                }
149
            }
150 34
            if ($con) {
151 10
                $this->iterator->next();
152 10
                continue;
153
            }
154 34
            return true;
155
        }
156 31
        return false;
157
    }
158
159
    // array access
160 1
    public function offsetGet($offset)
161
    {
162 1
        return $this->squash()->array->offsetGet($offset);
0 ignored issues
show
The method offsetGet does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
163
    }
164 1
    public function offsetExists($offset)
165
    {
166 1
        return $this->squash()->array->offsetExists($offset);
0 ignored issues
show
The method offsetExists does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
167
    }
168 1
    public function offsetUnset($offset)
169
    {
170 1
        return $this->squash()->array->offsetUnset($offset);
0 ignored issues
show
The method offsetUnset does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
171
    }
172 1
    public function offsetSet($offset, $value)
173
    {
174 1
        return $this->squash()->array->offsetSet($offset, $value);
0 ignored issues
show
The method offsetSet does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
175
    }
176
    public function add($value)
177
    {
178
        $this->squash()->array->append($value);
0 ignored issues
show
The method append does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
179
        return $this;
180
    }
181
    public function append($value)
182
    {
183
        return $this->add($value);
184
    }
185
    public function remove($value)
186
    {
187
        return $this->filter(function ($v) use ($value) { return $v !== $value; })->squash();
188
    }
189
    /**
190
     * Get the collection length
191
     * @return int
192
     */
193 1
    public function count()
194
    {
195 1
        if (count($this->stack) || !($this->array instanceof \Countable)) {
196
            $this->squash();
197
        }
198 1
        return $this->array->count();
199
    }
200
201
    // mutators
202
    /**
203
     * Filter values from the collection based on a predicate. The callback will receive the value, key and collection
204
     * @param  callable $iterator the predicate
205
     * @return $this
206
     */
207 10
    public function filter(callable $iterator) : Collection
208
    {
209 10
        $this->stack[] = [ 'filter', $iterator ];
210 10
        return $this;
211
    }
212
    /**
213
     * Pass all values of the collection through a mutator callable, which will receive the value, key and collection
214
     * @param  callable $iterator the mutator
215
     * @return $this
216
     */
217 7
    public function map(callable $iterator) : Collection
218
    {
219 7
        $this->stack[] = [ 'map', $iterator ];
220 7
        return $this;
221
    }
222
    /**
223
     * Pass all values of the collection through a key mutator callable, which will receive the value, key and collection
224
     * @param  callable $iterator the mutator
225
     * @return $this
226
     */
227 1
    public function mapKey(callable $iterator) : Collection
228
    {
229 1
        $this->stack[] = [ 'mapKey', $iterator ];
230 1
        return $this;
231
    }
232
    /**
233
     * Clone the current collection and return it.
234
     * @return Collection
235
     */
236
    public function clone() : Collection
237
    {
238 1
        return new static($this->toArray());
239
    }
240
    /**
241
     * Remove all falsy values from the collection (uses filter internally).
242
     * @return $this
243
     */
244 1
    public function compact() : Collection
245
    {
246
        return $this->filter(function ($v) {
247 1
            return !!$v;
248 1
        });
249
    }
250
    /**
251
     * Exclude all listed values from the collection (uses filter internally).
252
     * @param  iterable $values the values to exclude
253
     * @return $this
254
     */
255 3
    public function difference($values) : Collection
256
    {
257 3
        if (!is_array($values)) {
258 1
            $values = iterator_to_array($values);
259
        }
260 3
        $keys = array_keys($values);
261 3
        $isAssoc = $keys !== array_keys($keys);
262
        return $this->filter(function ($v, $k) use ($values, $isAssoc) {
263 3
            return $isAssoc ? 
264 1
                ($index = array_search($v, $values)) === false || $index !== $k :
265 3
                !in_array($v, $values, true);
266 3
        });
267
    }
268
    /**
269
     * Append more values to the collection
270
     * @param  iterable $source the values to add
271
     * @return Collection
272
     */
273 2
    public function extend($source) : Collection
274
    {
275 2
        if (!is_array($source)) {
276 1
            $source = iterator_to_array($source);
277
        }
278 2
        return new static(array_merge($this->toArray(), $source));
279
    }
280
    /**
281
     * Append more values to the collection
282
     * @param  iterable $source the values to add
283
     * @return Collection
284
     */
285 1
    public function merge($source) : Collection
286
    {
287 1
        return $this->extend($source);
288
    }
289
    /**
290
     * Perform a shallow flatten of the collection
291
     * @return Collection
292
     */
293 1
    public function flatten() : Collection
294
    {
295 1
        $rslt = [];
296 1
        $temp = $this->toArray();
297 1
        foreach ($temp as $v) {
298 1
            $rslt = array_merge($rslt, is_array($v) ? $v : [$v]);
299
        }
300 1
        return new static($rslt);
301
    }
302
    /**
303
     * Group by a key (if a callable is used - return the value to group by)
304
     * @param  string|callable $iterator the key to group by
305
     * @return Collection
306
     */
307 1
    public function groupBy($iterator) : Collection
308
    {
309 1
        $rslt = [];
310 1
        $temp = $this->toArray();
311 1
        foreach ($temp as $k => $v) {
312 1
            $rslt[is_string($iterator) ? (is_object($v) ? $v->{$iterator} : $v[$iterator]) : call_user_func($iterator, $v, $k)][] = $v;
313
        }
314 1
        return new static($rslt);
315
    }
316
    /**
317
     * Get the first X items from the collection
318
     * @param  int $count the number of items to include (defaults to 1)
319
     * @return Collection
320
     */
321 1
    public function first(int $count = 1) : Collection
322
    {
323 1
        $i = 0;
324 1
        $new = [];
325 1
        foreach ($this as $k => $v) {
326 1
            if (++$i > $count) {
327 1
                break;
328
            }
329 1
            $new[$k] = $v;
330
        }
331 1
        return new static($new);
332
    }
333
    /**
334
     * Get the first X items from the collection
335
     * @param  int $count the number of items to include (defaults to 1)
336
     * @return Collection
337
     */
338 1
    public function head(int $count = 1) : Collection
339
    {
340 1
        return $this->first($count);
341
    }
342
    /**
343
     * Get the last X items from the collection
344
     * @param  int $count the number of items to include (defaults to 1)
345
     * @return Collection
346
     */
347 2
    public function last(int $count = 1) : Collection
348
    {
349 2
        $new = $this->toArray();
350 2
        return new static(array_slice($new, $count * -1));
351
    }
352
    /**
353
     * Get the first X items from the collection
354
     * @param  int $count the number of items to include (defaults to 1)
355
     * @return Collection
356
     */
357 1
    public function tail(int $count = 1) : Collection
358
    {
359 1
        return $this->last($count);
360
    }
361
    /**
362
     * Get all but the last X items from the collection
363
     * @param  int $count the number of items to exclude (defaults to 1)
364
     * @return Collection
365
     */
366 1
    public function initial(int $count = 1) : Collection
367
    {
368 1
        $new = $this->toArray();
369 1
        return new static(array_slice($new, 0, $count * -1));
370
    }
371
    /**
372
     * Get all but the first X items from the collection
373
     * @param  int $count the number of items to exclude (defaults to 1)
374
     * @return Collection
375
     */
376 1
    public function rest(int $count = 1) : Collection
377
    {
378 1
        $new = $this->toArray();
379 1
        return new static(array_slice($new, $count));
380
    }
381
    /**
382
     * Execute a callable for each item in the collection (does not modify the collection)
383
     * @param  callable $iterator the callable to execute
384
     * @return $this
385
     */
386 1
    public function each(callable $iterator) : Collection
387
    {
388 1
        foreach ($this as $k => $v) {
389 1
            call_user_func($iterator, $v, $k, $this);
390
        }
391 1
        return $this;
392
    }
393
    /**
394
     * Execute a callable for each item in the collection (does not modify the collection)
395
     * @param  callable $iterator the callable to execute
396
     * @return $this
397
     */
398 1
    public function invoke(callable $iterator) : Collection
399
    {
400 1
        return $this->each($iterator);
401
    }
402
    /**
403
     * Get all the collection keys
404
     * @return $this
405
     */
406
    public function keys() : Collection
407
    {
408
        return $this->map(function ($v, $k) { return $k; })->values();
409
    }
410
    /**
411
     * Pluck a value from each object (uses map internally)
412
     * @param  string|int $key the key to extract
413
     * @return $this
414
     */
415 2
    public function pluck($key) : Collection
416
    {
417
        return $this->map(function ($v) use ($key) {
418 2
            return is_object($v) ?
419 1
                (isset($v->{$key}) ? $v->{$key} : null) :
420 2
                (isset($v[$key]) ? $v[$key] : null);
421 2
        });
422
    }
423
    /**
424
     * Intersect the collection with another iterable (uses filter internally)
425
     * @param  interable $values the data to intersect with
426
     * @return $this
427
     */
428 1
    public function intersection($values) : Collection
429
    {
430 1
        if (!is_array($values)) {
431 1
            $values = iterator_to_array($values);
432
        }
433 1
        $keys = array_keys($values);
434 1
        $isAssoc = $keys !== array_keys($keys);
435
        return $this->filter(function ($v, $k) use ($values, $isAssoc) {
436 1
            return $isAssoc ? 
437 1
                array_search($v, $values) === $k :
438 1
                in_array($v, $values, true);
439 1
        });
440
    }
441
    /**
442
     * Reject values on a given predicate (opposite of filter)
443
     * @param  callable $iterator the predicate
444
     * @return $this
445
     */
446 1
    public function reject(callable $iterator) : Collection
447
    {
448
        return $this->filter(function ($v, $k, $array) use ($iterator) {
449 1
            return !call_user_func($iterator, $v, $k, $array);
450 1
        });
451
    }
452
    /**
453
     * Shuffle the values in the collection
454
     * @return Collection
455
     */
456 1
    public function shuffle() : Collection
457
    {
458 1
        $temp = $this->toArray();
459 1
        $keys = array_keys($temp);
460 1
        shuffle($keys);
461 1
        $rslt = [];
462 1
        foreach ($keys as $key) {
463 1
            $rslt[$key] = $temp[$key];
464
        }
465 1
        return new static($rslt);
466
    }
467
    /**
468
     * Sort the collection using a standard sorting function
469
     * @param  callable $iterator the sort function (must return -1, 0 or 1)
470
     * @return Collection
471
     */
472 1
    public function sortBy(callable $iterator) : Collection
473
    {
474 1
        $this->squash();
475 1
        $this->array->uasort($iterator);
476 1
        return $this;
477
    }
478
    /**
479
     * Inspect the whole collection (as an array) mid-chain
480
     * @param  callable $iterator the callable to execute
481
     * @return $this
482
     */
483 1
    public function tap(callable $iterator) : Collection
484
    {
485 1
        call_user_func($iterator, $this->toArray());
486 1
        return $this;
487
    }
488
    /**
489
     * Modify the whole collection (as an array) mid-chain
490
     * @param  callable $iterator the callable to execute
491
     * @return Collection
492
     */
493 1
    public function thru(callable $iterator) : Collection
494
    {
495 1
        $temp = $this->toArray();
496 1
        $rslt = call_user_func($iterator, $temp);
497 1
        return new static($rslt);
498
    }
499
    /**
500
     * Leave only unique items in the collection
501
     * @return Collection
502
     */
503 3
    public function unique() : Collection
504
    {
505 3
        $temp = $this->toArray();
506 3
        $rslt = [];
507 3
        foreach ($temp as $k => $v) {
508 3
            if (!in_array($v, $rslt, true)) {
509 3
                $rslt[$k] = $v;
510
            }
511
        }
512 3
        return new static($rslt);
513
    }
514
    /**
515
     * Get only the values of the collection
516
     * @return Collection
517
     */
518 5
    public function values() : Collection
519
    {
520 5
        return new static(array_values($this->toArray()));
521
    }
522
523 3
    protected function whereCallback($v, $properties, $strict = true)
524
    {
525 3
        foreach ($properties as $key => $value) {
526 3
            $vv = is_object($v) ? (isset($v->{$key}) ? $v->{$key} : null) : (isset($v[$key]) ? $v[$key] : null);
527 3
            $negate = false;
528 3
            if (is_array($value) && count($value) === 1 && isset($value['not'])) {
529 2
                $value = $value['not'];
530 2
                $negate = true;
531
            }
532 3
            if (is_array($value) && isset($value['beg']) && strlen($value['beg']) && (!isset($value['end']) || !strlen($value['end']))) {
533
                $value = [ 'gte' => $value['beg'] ];
534
            }
535 3
            if (is_array($value) && isset($value['end']) && strlen($value['end']) && (!isset($value['beg']) || !strlen($value['beg']))) {
536
                $value = [ 'lte' => $value['end'] ];
537
            }
538 3
            if (is_array($value)) {
539 1
                if (isset($value['beg']) && isset($value['end'])) {
540 1
                    if ($vv < $value['beg'] || $vv > $value['end']) {
541 1
                        if (!$negate) {
542 1
                            return false;
543
                        }
544
                    } else {
545 1
                        if ($negate) {
546 1
                            return false;
547
                        }
548
                    }
549 1
                } elseif (isset($value['lt']) || isset($value['gt']) || isset($value['lte']) || isset($value['gte'])) {
550 1
                    if (isset($value['lt']) && $vv >= $value['lt']) {
551 1
                        if (!$negate) {
552 1
                            return false;
553
                        }
554
                    } else {
555 1
                        if ($negate) {
556
                            return false;
557
                        }
558
                    }
559 1
                    if (isset($value['gt']) && $vv <= $value['gt']) {
560
                        if (!$negate) {
561
                            return false;
562
                        }
563
                    } else {
564 1
                        if ($negate) {
565
                            return false;
566
                        }
567
                    }
568 1
                    if (isset($value['lte']) && $vv > $value['lte']) {
569 1
                        if (!$negate) {
570 1
                            return false;
571
                        }
572
                    } else {
573 1
                        if ($negate) {
574
                            return false;
575
                        }
576
                    }
577 1
                    if (isset($value['gte']) && $vv < $value['gte']) {
578 1
                        if (!$negate) {
579 1
                            return false;
580
                        }
581
                    } else {
582 1
                        if ($negate) {
583 1
                            return false;
584
                        }
585
                    }
586
                } else {
587 1
                    if (!in_array($vv, $value, $strict)) {
588 1
                        if (!$negate) {
589 1
                            return false;
590
                        }
591
                    } else {
592 1
                        if ($negate) {
593 1
                            return false;
594
                        }
595
                    }
596
                }
597
            } else {
598 3
                if (($strict && $vv !== $value) || (!$strict && $vv != $value)) {
599 3
                    if (!$negate) {
600 3
                        return false;
601
                    }
602
                } else {
603 3
                    if ($negate) {
604 3
                        return false;
605
                    }
606
                }
607
            }
608
        }
609 3
        return true;
610
    }
611 1
    public function whereAll(array $criteria) : Collection
612
    {
613
        return $this->filter(function ($v) use ($criteria) {
614 1
            foreach ($criteria as $row) {
615 1
                if (!$this->whereCallback($v, $row)) {
616 1
                    return false;
617
                }
618
            }
619 1
            return true;
620 1
        });
621
    }
622 1
    public function whereAny(array $criteria) : Collection
623
    {
624
        return $this->filter(function ($v) use ($criteria) {
625 1
            foreach ($criteria as $row) {
626 1
                if ($this->whereCallback($v, $row)) {
627 1
                    return true;
628
                }
629
            }
630 1
            return false;
631 1
        });
632
    }
633
    /**
634
     * Filter items from the collection using key => value pairs
635
     * @param  array   $properties the key => value to check for in each item
636
     * @param  boolean $strict     should the comparison be strict
637
     * @return $this
638
     */
639
    public function where(array $properties, $strict = true) : Collection
640
    {
641 1
        return $this->filter(function ($v) use ($properties, $strict) {
642 1
            return $this->whereCallback($v, $properties, $strict);
643 1
        });
644
    }
645
    /**
646
     * Exclude all listed values from the collection (uses filter internally).
647
     * @param  iterable $values the values to exclude
648
     * @return $this
649
     */
650 2
    public function without($values) : Collection
651
    {
652 2
        return $this->difference($values);
653
    }
654
    /**
655
     * Combine all the values from the collection with a key
656
     * @param  iterable $keys the keys to use
657
     * @return Collection
658
     */
659 2
    public function zip($keys) : Collection
660
    {
661 2
        if (!is_array($keys)) {
662 1
            $keys = iterator_to_array($keys);
663
        }
664 2
        return new static(array_combine($keys, $this->toArray()));
665
    }
666
    /**
667
     * Reverse the collection order
668
     * @return Collection
669
     */
670 2
    public function reverse() : Collection
671
    {
672 2
        return new static(array_reverse($this->toArray()));
673
    }
674
675
    // accessors
676
    /**
677
     * Do all of the items in the collection match a given criteria
678
     * @param  callable $iterator the criteria - should return true / false
679
     * @return bool
680
     */
681 1
    public function all(callable $iterator) : bool
682
    {
683 1
        foreach ($this as $k => $v) {
684 1
            if (!call_user_func($iterator, $v, $k, $this)) {
685 1
                return false;
686
            }
687
        }
688 1
        return true;
689
    }
690
    /**
691
     * Do any of the items in the collection match a given criteria
692
     * @param  callable $iterator the criteria - should return true / false
693
     * @return bool
694
     */
695 1
    public function any(callable $iterator) : bool
696
    {
697 1
        foreach ($this as $k => $v) {
698 1
            if (call_user_func($iterator, $v, $k, $this)) {
699 1
                return true;
700
            }
701
        }
702 1
        return false;
703
    }
704
    /**
705
     * Does the collection contain a given value
706
     * @param  mixed $needle the value to check for
707
     * @return bool
708
     */
709 1
    public function contains($needle) : bool
710
    {
711 1
        foreach ($this as $k => $v) {
712 1
            if ($v === $needle) {
713 1
                return true;
714
            }
715
        }
716 1
        return false;
717
    }
718
    /**
719
     * Get the first element matching a given criteria (or null)
720
     * @param  callable $iterator the filter criteria
721
     * @return mixed
722
     */
723 1
    public function find(callable $iterator)
724
    {
725 1
        foreach ($this as $k => $v) {
726 1
            if (call_user_func($iterator, $v, $k, $this)) {
727 1
                return $v;
728
            }
729
        }
730 1
        return null;
731
    }
732
    /**
733
     * Get all the elements matching a given criteria (with the option to limit the number of results)
734
     * @param  callable $iterator the search criteria
735
     * @param  int|null $limit    optional limit to the number of results (default to null - no limit)
736
     * @return Collection
737
     */
738 1
    public function findAll(callable $iterator, int $limit = null) : Collection
739
    {
740 1
        $res = [];
741 1
        foreach ($this as $k => $v) {
742 1
            if (call_user_func($iterator, $v, $k, $this)) {
743 1
                $res[] = $v;
744
            }
745 1
            if ((int)$limit > 0 && count($res) >= $limit) {
746 1
                break;
747
            }
748
        }
749 1
        return new static($res);
750
    }
751
    /**
752
     * Get the key corresponding to a value (or false)
753
     * @param  mixed  $needle the value to search for
754
     * @return mixed
755
     */
756 1
    public function indexOf($needle)
757
    {
758 1
        return array_search($needle, $this->toArray(), true);
759
    }
760
    /**
761
     * Get the last key corresponding to a value (or false)
762
     * @param  mixed  $needle the value to search for
763
     * @return mixed
764
     */
765 1
    public function lastIndexOf($needle)
766
    {
767 1
        $res = null;
768 1
        foreach ($this as $k => $v) {
769 1
            if ($v === $needle) {
770 1
                $res = $k;
771
            }
772
        }
773 1
        return $res;
774
    }
775
    /**
776
     * Get the number of elements in the collection
777
     * @return int
778
     */
779 1
    public function size() : int
780
    {
781 1
        return $this->count();
782
    }
783
    /**
784
     * Get the minimal item in the collection
785
     * @return mixed
786
     */
787 1
    public function min()
788
    {
789 1
        $min = null;
790 1
        $first = false;
791 1
        foreach ($this as $v) {
792 1
            if (!$first || $v < $min) {
793 1
                $min = $v;
794 1
                $first = true;
795
            }
796
        }
797 1
        return $min;
798
    }
799
    /**
800
     * Get the maximum item in the collection
801
     * @return mixed
802
     */
803 1
    public function max()
804
    {
805 1
        $max = null;
806 1
        $first = false;
807 1
        foreach ($this as $v) {
808 1
            if (!$first || $v > $max) {
809 1
                $max = $v;
810 1
                $first = true;
811
            }
812
        }
813 1
        return $max;
814
    }
815
    /**
816
     * Does the collection contain a given key
817
     * @param  string|int  $key the key to check
818
     * @return bool
819
     */
820 1
    public function has($key) : bool
821
    {
822 1
        return $this->offsetExists($key);
823
    }
824
    /**
825
     * Reduce the collection to a single value
826
     * @param  callable $iterator the reducer (will recieve the carried value, the value, the key and the collection)
827
     * @param  mixed    $initial  the initial value
828
     * @return mixed the final value
829
     */
830 2
    public function reduce(callable $iterator, $initial = null)
831
    {
832 2
        foreach ($this as $k => $v) {
833 2
            $initial = $iterator($initial, $v, $k, $this);
834
        }
835 2
        return $initial;
836
    }
837
    /**
838
     * Reduce the collection to a single value, starting from the last element
839
     * @param  callable $iterator the reducer (will recieve the carried value, the value, the key and the collection)
840
     * @param  mixed    $initial  the initial value
841
     * @return mixed the final value
842
     */
843 1
    public function reduceRight(callable $iterator, $initial = null)
844
    {
845 1
        return $this->reverse()->reduce($iterator, $initial);
846
    }
847
}
848