Issues (1)

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 (1 issue)

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
 * This file is part of Dimsh\Models\Collections;
4
 * (c) Abdulrahman Dimashki <[email protected]>
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Dimsh\Models\Collections;
11
12
/**
13
 * Class Collection
14
 *
15
 * @method {} offsetGet();
16
 * @method {} @current()
17
 * @method {} @last()
18
 * @method {} @first()
19
 *
20
 * @package Dimsh\Models\Collections;
21
 */
22
class Collection implements \Iterator, \ArrayAccess, \Countable
23
{
24
    /**
25
     * @var array
26
     */
27
    protected $list = [];
28
29
    /**
30
     * @var int
31
     */
32
    protected $position = 0;
33
34
    /**
35
     * this is required for the unset() <=> offsetUnset() where an exception is
36
     * thrown if next() or rewind() is not called after unset.
37
     * @var bool
38
     */
39
    protected $next_or_rewind_required = false;
40
41 87
    public function __construct()
42
    {
43 87
        $this->position = 0;
44 87
    }
45
46
    /**
47
     * @return int
48
     */
49 12
    public function count()
50
    {
51 12
        return count($this->list);
52
    }
53
54
    /**
55
     * This method is meant to be overridden by child Classes in order to throw
56
     * an Exception if the value can't be added, else it will be added normally.
57
     *
58
     * @param mixed $value
59
     */
60 66
    protected function preAdd($value)
61
    {
62 66
    }
63
64
    /**
65
     * Get the next numeric offset.
66
     *
67
     * we get the MAX numeric offset we have and increment it by 1.
68
     */
69 57
    protected function nextOffset()
70
    {
71 57
        $keys = array_keys($this->list);
72 57
        $keys = array_filter($keys, 'is_numeric'); // get numeric keys only
73 57
        if ($keys) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $keys of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
74 42
            $last_key = max($keys);
75 14
        } else {
76 57
            $last_key = -1;
77
        }
78 57
        return $last_key + 1;
79
    }
80
81
    /**
82
     * @param string|int $offset
83
     * @param mixed      $value
84
     *
85
     * @throws \Exception
86
     */
87 72
    public function offsetSet($offset, $value)
88
    {
89 72
        if ($offset === null) {
90
            // a value is appended using [] array append operator.
91 57
            $offset = $this->nextOffset();
92 19
        }
93 72
        $this->preAdd($value);
94 69
        $this->list[$offset] = &$value;
95 69
    }
96
97
    /**
98
     * This is not in the interface, add an item.
99
     * @return self
100
     * @throws \Exception
101
     */
102 12
    public function add($item)
103
    {
104 12
        $this->offsetSet(null, $item);
105 12
        return $this;
106
    }
107
108
    /**
109
     * This is not in the interface, add an item passing it by reference.
110
     *
111
     * This will only accept variables as input.
112
     *
113
     * @return self
114
     * @throws \Exception
115
     */
116 3
    public function addByReference(&$item)
117
    {
118 3
        $this->preAdd($item);
119 3
        $this->list[$this->nextOffset()] = &$item;
120 3
        return $this;
121
    }
122
123
    /**
124
     * @param string|int $offset
125
     *
126
     * @return mixed|null
127
     */
128 21
    public function &offsetGet($offset)
129
    {
130 21
        if (array_key_exists($offset, $this->list)) {
131 18
            return $this->list[$offset];
132
        }
133 3
        $null = null;
134 3
        return $null;
135
    }
136
137
    /**
138
     * @return bool
139
     */
140 6
    public function offsetExists($offset)
141
    {
142 6
        return (array_key_exists($offset, $this->list));
143
    }
144
145 12
    public function offsetUnset($offset)
146
    {
147 12
        $offset_pos = $this->offsetPosition($offset);
148 12
        if ($offset_pos !== false && $this->position === $offset_pos) {
149 12
            $this->next_or_rewind_required = true;
150 12
            if ($this->position > 0) {
151
                $this->position--;
152
            }
153 4
        }
154 12
        unset($this->list[$offset]);
155 12
    }
156
157 18
    public function rewind()
158
    {
159 18
        $this->position                = 0;
160 18
        $this->next_or_rewind_required = false;
161 18
    }
162
163 9
    public function next()
164
    {
165 9
        ++$this->position;
166 9
        $this->next_or_rewind_required = false;
167 9
    }
168
169
    /**
170
     * This is not in the interface, go back in the array.
171
     */
172 3
    public function prev()
173
    {
174 3
        --$this->position;
175 3
        $this->next_or_rewind_required = false;
176 3
    }
177
178
    /**
179
     * @return bool
180
     */
181 12
    public function valid()
182
    {
183 12
        if (empty($this->list)) {
184 6
            return false;
185
        }
186 6
        return array_key_exists($this->position, array_keys($this->list));
187
    }
188
189 21
    public function current()
190
    {
191 21
        if ($this->next_or_rewind_required) {
192 3
            throw new \Exception('Calling next or rewind is required after call to unset, current item can\'t be accessed otherwise because it is removed.');
193
        }
194 18
        return $this->list[$this->key()];
195
    }
196
197
    /**
198
     * @return string
199
     */
200 18
    public function key()
201
    {
202 18
        $keys   = array_keys($this->list);
203 18
        $offset = $keys[$this->position];
204 18
        return $offset;
205
    }
206
207
    /**
208
     * @return int
209
     */
210 3
    public function position()
211
    {
212 3
        return $this->position;
213
    }
214
215
    /**
216
     * This is not in the interface, Get the last item.
217
     */
218 6
    public function last()
219
    {
220 6
        $keys   = array_keys($this->list);
221 6
        $offset = array_pop($keys);
222 6
        if (array_key_exists($offset, $this->list)) {
223 3
            return $this->list[$offset];
224
        }
225 3
        $null = null;
226 3
        return $null;
227
    }
228
229
    /**
230
     * This is not in the interface, Get the first item.
231
     */
232 6
    public function first()
233
    {
234 6
        $keys   = array_keys($this->list);
235 6
        $offset = array_shift($keys);
236 6
        if (array_key_exists($offset, $this->list)) {
237 3
            return $this->list[$offset];
238
        }
239 3
        $null = null;
240 3
        return $null;
241
    }
242
243
    /**
244
     * This is not in the interface, Return the keys of the internal list.
245
     *
246
     * @return array
247
     */
248 9
    public function keys()
249
    {
250 9
        return array_keys($this->list);
251
    }
252
253
    /**
254
     * This is not in the interface, cast the internal list to an array.
255
     *
256
     * @return array
257
     */
258 24
    public function toArray()
259
    {
260 24
        return $this->list;
261
    }
262
263
264
    /**
265
     * This is not in the interface, merge this object with another object and act
266
     * like array_merge, numeric indexes are added, other indexes are overridden.
267
     *
268
     * This method will alter the current object.
269
     *
270
     * @param Collection $another_list
271
     *
272
     * @return self
273
     *
274
     * @throws \Exception
275
     *
276
     */
277 3
    public function merge(Collection $another_list)
278
    {
279 3
        for ($another_list->rewind(); $another_list->valid(); $another_list->next()) {
280
            $key = $another_list->key();
281
            try {
282
                $this->offsetSet(is_numeric($key) ? null : $key, $another_list->current());
283
            } catch (\Exception $e) {
284
                throw $e;
285
            }
286
        }
287 3
        return $this;
288
    }
289
290
    /**
291
     * Change the position of item at $offset1 in the internal array to be at
292
     * the position of item at $offset2 and vise versa.
293
     *
294
     * @param $offset1
295
     * @param $offset2
296
     *
297
     * @return $this
298
     * @throws \Exception
299
     */
300 9
    public function swap($offset1, $offset2)
301
    {
302 9
        $pos1 = $this->offsetPosition($offset1);
303 9
        $pos2 = $this->offsetPosition($offset2);
304 9
        if ($pos1 === false || $pos2 === false) {
305
            // offset does not exist
306 3
            throw new \Exception("Trying to swap non exist offsets");
307
        }
308 6
        if ($pos1 == $pos2) {
309 3
            return $this;
310
        }
311 3
        $temp           = $this[$offset1];
312 3
        $this[$offset1] = $this[$offset2];
313 3
        $this[$offset2] = $temp;
314
315 3
        return $this;
316
    }
317
318
    /**
319
     * Change the position of item at $offset1 in the internal array to be at
320
     * the position of item at $offset2 and vise versa using re-order and refilling
321
     * the array
322
     *
323
     * @param $offset1
324
     * @param $offset2
325
     *
326
     * @return self
327
     *
328
     * @throws \Exception
329
     */
330 9
    public function swapReorder($offset1, $offset2)
331
    {
332 9
        $pos1 = $this->offsetPosition($offset1);
333 9
        $pos2 = $this->offsetPosition($offset2);
334 9
        if ($pos1 === false || $pos2 === false) {
335
            // offset does not exist
336 3
            throw new \Exception("Trying to swap non exist offsets");
337
        }
338 6
        if ($pos1 == $pos2) {
339 3
            return $this;
340
        }
341
342 3
        $clone = clone $this;
343 3
        for ($clone->rewind(); $clone->valid(); $clone->next()) {
344 3
            $key      = $clone->key();
345 3
            $curr_pos = $clone->position();
346 3
            if ($curr_pos === $pos1) {
347 3
                $this[$key] = $clone->offsetGet($offset2);
348 3
            } elseif ($curr_pos === $pos2) {
349 3
                $this[$key] = $clone->offsetGet($offset1);
350 1
            } else {
351 3
                $this[$key] = $clone->current();
352
            }
353 1
        }
354
355 3
        return $this;
356
    }
357
358
359
    /**
360
     * Get the item by internal pointer numeric position and not by the offset.
361
     *
362
     * This is not in the interface.
363
     *
364
     * @param int $pos
365
     *
366
     * @return null|mixed
367
     */
368 6
    public function byPositionGet($pos)
369
    {
370 6
        $keys = $this->keys();
371 6
        if (array_key_exists($pos, $keys)) {
372 3
            $offset = $keys[$pos];
373 3
            return $this->list[$offset];
374
        }
375
376 3
        return null;
377
    }
378
379
    /**
380
     * Get the internal pointer numeric position which corresponds to the
381
     * offset passed.
382
     * this is not in the interface
383
     *
384
     * @param $offset
385
     *
386
     * @return int|FALSE
387
     */
388 27
    public function offsetPosition($offset)
389
    {
390 27
        $keys = array_keys($this->list);
391 27
        $key  = array_search($offset, $keys);
392 27
        return $key;
393
    }
394
395
    /**
396
     * Check if this container is empty.
397
     *
398
     * This is not in the interface
399
     * @return bool
400
     */
401 6
    public function isEmpty()
402
    {
403 6
        return count($this) === 0;
404
    }
405
406
    /**
407
     * Perform array_diff_key against another list where the first parameter is
408
     * this collection and the second is the new list.
409
     *
410
     * This will return a new Collection.
411
     *
412
     * @param Collection $another_list
413
     *
414
     * @return Collection
415
     */
416 3
    public function diffKey(Collection $another_list)
417
    {
418 3
        $diff     = array_diff_key($this->toArray(), $another_list->toArray());
419 3
        $new_list = new static();
420 3
        foreach ($diff as $key => $value) {
421 3
            $new_list[$key] = $value;
422 1
        }
423 3
        return $new_list;
424
    }
425
426
    /**
427
     * A version of php array_diff_assoc which is recursive.
428
     *
429
     * array_diff_assoc — Computes the difference of arrays with additional index check
430
     *
431
     * @param array $array1
432
     * @param array $array2
433
     *
434
     * @return array
435
     */
436 9
    protected static function arrayDiffRecursiveAssoc(array $array1, array $array2)
437
    {
438 9
        $ret = array();
439
440 9
        foreach ($array1 as $key1 => $value1) {
441 9
            if (!array_key_exists($key1, $array2)) {
442 6
                $ret[$key1] = $value1;
443 6
                continue;
444
            }
445 3
            if (is_array($value1) && is_array($array2[$key1])) {
446
                $recursive_diff = self::arrayDiffRecursiveAssoc($value1, $array2[$key1]);
447
                if (count($recursive_diff)) {
448
                    $ret[$key1] = $recursive_diff;
449
                }
450 3
            } elseif ($value1 instanceof Collection && $array2[$key1] instanceof Collection) {
451 3
                $recursive_diff = $value1->diffRecursiveAssoc($array2[$key1]);
452 3
                if ($recursive_diff->count()) {
453 2
                    $ret[$key1] = $recursive_diff;
454
                }
455 3
            } elseif (!is_array($value1) && !is_array($array2[$key1])) {
456 3
                if ($value1 !== $array2[$key1]) {
457 2
                    $ret[$key1] = $value1;
458
                }
459 1
            } else {
460 1
                $ret[$key1] = $value1;
461
            }
462 3
        }
463 9
        return $ret;
464
    }
465
466
    /**
467
     * Return Recursive diff from another list with index check.
468
     *
469
     * The first array is this collection and compute the diff against the passed collection.
470
     *
471
     * This will return a new Collection.
472
     *
473
     * @param Collection $another_list
474
     *
475
     * @return static
476
     */
477 9
    public function diffRecursiveAssoc(Collection $another_list)
478
    {
479 9
        $ret  = new static();
480 9
        $diff = static::arrayDiffRecursiveAssoc($this->toArray(), $another_list->toArray());
481 9
        foreach ($diff as $key => $value) {
482 6
            $ret[$key] = $value;
483 3
        }
484 9
        return $ret;
485
    }
486
487
    /**
488
     * A version of php array_diff which is recursive.
489
     *
490
     * array_diff — Computes the difference of arrays
491
     *
492
     * This function is costy because it uses array_search
493
     *
494
     * @param array $array1
495
     * @param array $array2
496
     *
497
     * @return array
498
     */
499 3
    protected static function arrayDiffRecursive(array $array1, array $array2)
500
    {
501 3
        $ret = array();
502
503 3
        foreach ($array1 as $key1 => $value1) {
504 3
            if (false === ($key2 = array_search($value1, $array2, true))) {
505 3
                $ret[$key1] = $value1;
506 3
                continue;
507
            }
508 3
            if (is_array($value1) && is_array($array2[$key2])) {
509
                $recursive_diff = self::arrayDiffRecursive($value1, $array2[$key2]);
510
                if (count($recursive_diff)) {
511
                    $ret[$key1] = $recursive_diff;
512
                }
513 3
            } elseif ($value1 instanceof Collection && $array2[$key2] instanceof Collection) {
514
                $recursive_diff = $value1->diffRecursive($array2[$key2]);
515
                if ($recursive_diff->count()) {
516
                    $ret[$key1] = $recursive_diff;
517
                }
518 3
            } elseif (!is_array($value1) && !is_array($array2[$key2])) {
519 3
                if ($value1 !== $array2[$key2]) {
520 2
                    $ret[$key1] = $value1;
521
                }
522 1
            } else {
523 1
                $ret[$key1] = $value1;
524
            }
525 1
        }
526 3
        return $ret;
527
    }
528
529
    /**
530
     * Return Recursive diff from another list.
531
     *
532
     * The first array is this collection and compute the diff against the passed collection.
533
     *
534
     * This will return a new Collection.
535
     *
536
     * @param Collection $another_list
537
     *
538
     * @return static
539
     */
540 3
    public function diffRecursive(Collection $another_list)
541
    {
542 3
        $ret  = new static();
543 3
        $diff = static::arrayDiffRecursive($this->toArray(), $another_list->toArray());
544 3
        foreach ($diff as $key => $value) {
545 3
            $ret[$key] = $value;
546 1
        }
547 3
        return $ret;
548
    }
549
550
    /**
551
     * Implementation of array_intesect_key against another list.
552
     *
553
     * This will return a new Collection.
554
     *
555
     * @param Collection $another_list
556
     *
557
     * @return static
558
     */
559 3
    public function intersectKey(Collection $another_list)
560
    {
561 3
        $ret = new static();
562 3
        foreach ($this as $key => $value) {
563 3
            if ($another_list->offsetExists($key)) {
564 3
                $ret[$key] = $value;
565 1
            }
566 1
        }
567 3
        return $ret;
568
    }
569
}
570