Completed
Push — master ( e08a43...bd87cf )
by Lars
02:30
created

Arrayy::clean()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
/** @noinspection ClassReImplementsParentInterfaceInspection */
8
9
/**
10
 * Methods to manage arrays.
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \Countable
16
{
17
    /**
18
     * @var array
19
     */
20
    protected $array = [];
21
22
    /**
23
     * @var string
24
     */
25
    protected $iteratorClass = ArrayyIterator::class;
26
27
    /**
28
     * @var string
29
     */
30
    protected $pathSeparator = '.';
31
32
    /**
33
     * @var bool
34
     */
35
    protected $checkPropertyTypes = false;
36
37
    /**
38
     * @var bool
39
     */
40
    protected $checkForMissingPropertiesInConstructor = false;
41
42
    /**
43
     * @var bool
44
     */
45
    protected $checkPropertiesMismatchInConstructor = false;
46
47
    /**
48
     * @var array|Property[]
49
     */
50
    protected $properties = [];
51
52
    /** @noinspection MagicMethodsValidityInspection */
53
    /** @noinspection PhpMissingParentConstructorInspection */
54
55
    /**
56
     * Initializes
57
     *
58
     * @param mixed  $array                                  <p>
59
     *                                                       Should be an array, otherwise it will try to convert
60
     *                                                       it into an array.
61
     *                                                       </p>
62
     * @param string $iteratorClass                          optional <p>
63
     *                                                       You can overwrite the ArrayyIterator, but mostly you don't
64
     *                                                       need this option.
65
     *                                                       </p>
66
     * @param bool   $checkForMissingPropertiesInConstructor optional <p>
67
     *                                                       You need to extend the "Arrayy"-class and you need to set
68
     *                                                       the $checkPropertiesMismatchInConstructor class property to
69
     *                                                       true, otherwise this option didn't not work anyway.
70
     *                                                       </p>
71
     */
72 889
    public function __construct($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true)
73
    {
74 889
        $array = $this->fallbackForArray($array);
75
76 887
        parent::__construct($array, 0, $iteratorClass);
77
78
        if (
79 887
            $this->checkPropertyTypes === true
80
            ||
81
            (
82 878
                $this->checkForMissingPropertiesInConstructor === true
83
                &&
84 887
                $checkForMissingPropertiesInConstructor === true
85
            )
86
        ) {
87 9
            $this->properties = $this->getPublicProperties();
88
        }
89
90
        if (
91 887
            $this->checkPropertiesMismatchInConstructor === true
92
            &&
93 887
            \count($array) !== 0
94
            &&
95 887
            \count(\array_diff_key($this->properties, $array)) > 0
96
        ) {
97 1
            throw new \InvalidArgumentException('Property mismatch - input: ' . \print_r(\array_keys($array), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
98
        }
99
100 886
        foreach ($array as $key => $value) {
101 774
            $this->internalSet($key, $value);
102
        }
103
104 883
        $this->setIteratorClass($iteratorClass);
105 883
    }
106
107
    /**
108
     * Get a value by key.
109
     *
110
     * @param mixed $key
111
     *
112
     * @return mixed
113
     *                <p>Get a Value from the current array.</p>
114
     */
115 3
    public function &__get($key)
116
    {
117 3
        $return = $this->get($key);
118
119 3
        if (\is_array($return)) {
120
            return static::create($return, $this->iteratorClass, false);
121
        }
122
123 3
        return $return;
124
    }
125
126
    /**
127
     * Call object as function.
128
     *
129
     * @param mixed $key
130
     *
131
     * @return mixed
132
     */
133 1
    public function __invoke($key = null)
134
    {
135 1
        if ($key !== null) {
136 1
            return $this->array[$key] ?? false;
137
        }
138
139
        return (array) $this->array;
140
    }
141
142
    /**
143
     * Whether or not an element exists by key.
144
     *
145
     * @param mixed $key
146
     *
147
     * @return bool
148
     *              <p>True is the key/index exists, otherwise false.</p>
149
     */
150
    public function __isset($key)
151
    {
152
        return $this->offsetExists($key);
153
    }
154
155
    /**
156
     * Assigns a value to the specified element.
157
     *
158
     * @param mixed $key
159
     * @param mixed $value
160
     */
161 2
    public function __set($key, $value)
162
    {
163 2
        $this->internalSet($key, $value);
164 2
    }
165
166
    /**
167
     * magic to string
168
     *
169
     * @return string
170
     */
171 15
    public function __toString()
172
    {
173 15
        return $this->toString();
174
    }
175
176
    /**
177
     * Unset element by key.
178
     *
179
     * @param mixed $key
180
     */
181
    public function __unset($key)
182
    {
183
        $this->internalRemove($key);
184
    }
185
186
    /**
187
     * alias: for "Arrayy->append()"
188
     *
189
     * @see Arrayy::append()
190
     *
191
     * @param mixed $value
192
     *
193
     * @return static
194
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
195
     */
196 1
    public function add($value)
197
    {
198 1
        return $this->append($value);
199
    }
200
201
    /**
202
     * Append a (key) + value to the current array.
203
     *
204
     * @param mixed $value
205
     * @param mixed $key
206
     *
207
     * @return static
208
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
209
     */
210 9
    public function append($value, $key = null)
211
    {
212 9
        if ($key !== null) {
213
            if (
214
                isset($this->array[$key])
215
                &&
216
                \is_array($this->array[$key])
217
            ) {
218
                $this->array[$key][] = $value;
219
            } else {
220
                $this->array[$key] = $value;
221
            }
222
        } else {
223 9
            $this->array[] = $value;
224
        }
225
226 9
        return $this;
227
    }
228
229
    /**
230
     * Sort the entries by value.
231
     *
232
     * @param int $sort_flags [optional] <p>
233
     *                        You may modify the behavior of the sort using the optional
234
     *                        parameter sort_flags, for details
235
     *                        see sort.
236
     *                        </p>
237
     *
238
     * @return static
239
     *                <p>(Mutable) Return this Arrayy object.</p>
240
     */
241 4
    public function asort(int $sort_flags = 0)
242
    {
243 4
        \asort($this->array, $sort_flags);
244
245 4
        return $this;
246
    }
247
248
    /**
249
     * Counts all elements in an array, or something in an object.
250
     *
251
     * <p>
252
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
253
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
254
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
255
     * implemented and used in PHP.
256
     * </p>
257
     *
258
     * @see http://php.net/manual/en/function.count.php
259
     *
260
     * @param int $mode [optional] If the optional mode parameter is set to
261
     *                  COUNT_RECURSIVE (or 1), count
262
     *                  will recursively count the array. This is particularly useful for
263
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
264
     *
265
     * @return int
266
     *              <p>
267
     *              The number of elements in var, which is
268
     *              typically an array, since anything else will have one
269
     *              element.
270
     *              </p>
271
     *              <p>
272
     *              If var is not an array or an object with
273
     *              implemented Countable interface,
274
     *              1 will be returned.
275
     *              There is one exception, if var is &null;,
276
     *              0 will be returned.
277
     *              </p>
278
     *              <p>
279
     *              Caution: count may return 0 for a variable that isn't set,
280
     *              but it may also return 0 for a variable that has been initialized with an
281
     *              empty array. Use isset to test if a variable is set.
282
     *              </p>
283
     */
284 37
    public function count(int $mode = \COUNT_NORMAL): int
285
    {
286 37
        return \count($this->array, $mode);
287
    }
288
289
    /**
290
     * Exchange the array for another one.
291
     *
292
     * @param array|static $data
293
     *
294
     * @return array
295
     */
296 1
    public function exchangeArray($data): array
297
    {
298 1
        $this->array = $this->fallbackForArray($data);
299
300 1
        return $this->array;
301
    }
302
303
    /**
304
     * Creates a copy of the ArrayyObject.
305
     *
306
     * @return array
307
     */
308 1
    public function getArrayCopy(): array
309
    {
310 1
        return $this->array;
311
    }
312
313
    /**
314
     * Returns a new ArrayyIterator, thus implementing the \ArrayIterator interface.
315
     *
316
     * @return \ArrayIterator
317
     *                         <p>An iterator for the values in the array.</p>
318
     */
319 12
    public function getIterator(): \ArrayIterator
320
    {
321 12
        $iterator = $this->getIteratorClass();
322
323 12
        return new $iterator($this->array);
324
    }
325
326
    /**
327
     * Gets the iterator classname for the ArrayObject.
328
     *
329
     * @return string
330
     */
331 12
    public function getIteratorClass(): string
332
    {
333 12
        return $this->iteratorClass;
334
    }
335
336
    /**
337
     * Sort the entries by key
338
     *
339
     * @param int $sort_flags [optional] <p>
340
     *                        You may modify the behavior of the sort using the optional
341
     *                        parameter sort_flags, for details
342
     *                        see sort.
343
     *                        </p>
344
     *
345
     * @return static
346
     *                 <p>(Mutable) Return this Arrayy object.</p>
347
     */
348 4
    public function ksort(int $sort_flags = 0)
349
    {
350 4
        \ksort($this->array, $sort_flags);
351
352 4
        return $this;
353
    }
354
355
    /**
356
     * Sort an array using a case insensitive "natural order" algorithm
357
     *
358
     * @return static
359
     *                 <p>(Mutable) Return this Arrayy object.</p>
360
     */
361
    public function natcasesort()
362
    {
363
        \natcasesort($this->array);
364
365
        return $this;
366
    }
367
368
    /**
369
     * Sort entries using a "natural order" algorithm
370
     *
371
     * @return static
372
     *                 <p>(Mutable) Return this Arrayy object.</p>
373
     */
374 1
    public function natsort()
375
    {
376 1
        \natsort($this->array);
377
378 1
        return $this;
379
    }
380
381
    /**
382
     * Whether or not an offset exists.
383
     *
384
     * @param bool|float|int|string $offset
385
     *
386
     * @return bool
387
     */
388 44
    public function offsetExists($offset): bool
389
    {
390 44
        if ($this->isEmpty()) {
391 4
            return false;
392
        }
393
394
        // php cast "bool"-index into "int"-index
395 40
        if ((bool) $offset === $offset) {
396 1
            $offset = (int) $offset;
397
        }
398
399 40
        $tmpReturn = \array_key_exists($offset, $this->array);
400
401
        if (
402 40
            $tmpReturn === true
403
            ||
404
            (
405 16
                $tmpReturn === false
406
                &&
407 40
                \strpos((string) $offset, $this->pathSeparator) === false
408
            )
409
        ) {
410 38
            return $tmpReturn;
411
        }
412
413 3
        $offsetExists = false;
414
415 3
        if (\strpos((string) $offset, $this->pathSeparator) !== false) {
416 3
            $offsetExists = false;
417 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
418 3
            $lastOffset = \array_pop($explodedPath);
419 3
            $containerPath = \implode($this->pathSeparator, $explodedPath);
420
421 3
            $this->callAtPath(
422 3
                $containerPath,
423 3
                function ($container) use ($lastOffset, &$offsetExists) {
0 ignored issues
show
Documentation introduced by
function ($container) us...tOffset, $container); } is of type object<Closure>, but the function expects a object<callable>.

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...
424 3
                    $offsetExists = \array_key_exists($lastOffset, $container);
425 3
                }
426
            );
427
        }
428
429 3
        return $offsetExists;
430
    }
431
432
    /**
433
     * Returns the value at specified offset.
434
     *
435
     * @param float|int|string $offset
436
     *
437
     * @return mixed
438
     *                <p>Will return null if the offset did not exists.</p>
439
     */
440 30
    public function offsetGet($offset)
441
    {
442 30
        return $this->offsetExists($offset) ? $this->get($offset) : null;
443
    }
444
445
    /**
446
     * Assigns a value to the specified offset.
447
     *
448
     * @param int|string|null $offset
449
     * @param mixed           $value
450
     */
451 20
    public function offsetSet($offset, $value)
452
    {
453 20
        if ($offset === null) {
454 4
            $this->array[] = $value;
455
        } else {
456 16
            $this->internalSet($offset, $value);
457
        }
458 20
    }
459
460
    /**
461
     * Unset an offset.
462
     *
463
     * @param float|int|string $offset
464
     */
465 7
    public function offsetUnset($offset)
466
    {
467 7
        if ($this->isEmpty()) {
468 1
            return;
469
        }
470
471 6
        if (\array_key_exists($offset, $this->array)) {
472 4
            unset($this->array[$offset]);
473
474 4
            return;
475
        }
476
477 3
        if (\strpos((string) $offset, $this->pathSeparator) !== false) {
478 2
            $path = \explode($this->pathSeparator, (string) $offset);
479 2
            $pathToUnset = \array_pop($path);
480
481 2
            $this->callAtPath(
482 2
                \implode($this->pathSeparator, $path),
483 2
                function (&$offset) use ($pathToUnset) {
0 ignored issues
show
Documentation introduced by
function (&$offset) use(...ffset[$pathToUnset]); } is of type object<Closure>, but the function expects a object<callable>.

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...
484 2
                    unset($offset[$pathToUnset]);
485 2
                }
486
            );
487
        }
488 3
    }
489
490
    /** @noinspection SenselessProxyMethodInspection | can not add return type, because of the "Serializable" interface */
491
492
    /**
493
     * Serialize the current "Arrayy"-object.
494
     *
495
     * @return string
496
     */
497 1
    public function serialize()
498
    {
499 1
        return parent::serialize();
500
    }
501
502
    /**
503
     * Sets the iterator classname for the current "Arrayy"-object.
504
     *
505
     * @param string $class
506
     *
507
     * @throws \InvalidArgumentException
508
     *
509
     * @return void
510
     */
511 883
    public function setIteratorClass($class)
512
    {
513 883
        if (\class_exists($class)) {
514 883
            $this->iteratorClass = $class;
515
516 883
            return;
517
        }
518
519
        if (\strpos($class, '\\') === 0) {
520
            $class = '\\' . $class;
521
            if (\class_exists($class)) {
522
                $this->iteratorClass = $class;
523
524
                return;
525
            }
526
        }
527
528
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
529
    }
530
531
    /**
532
     * Sort the entries with a user-defined comparison function and maintain key association.
533
     *
534
     * @param \callable $function
535
     *
536
     * @throws \InvalidArgumentException
537
     *
538
     * @return static
539
     *                <p>(Mutable) Return this Arrayy object.</p>
540
     */
541 View Code Duplication
    public function uasort($function)
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...
542
    {
543
        if (!\is_callable($function)) {
544
            throw new \InvalidArgumentException(
545
                'Passed function must be callable'
546
            );
547
        }
548
549
        \uasort($this->array, $function);
550
551
        return $this;
552
    }
553
554
    /**
555
     * Sort the entries by keys using a user-defined comparison function.
556
     *
557
     * @param \callable $function
558
     *
559
     * @throws \InvalidArgumentException
560
     *
561
     * @return static
562
     *                <p>(Mutable) Return this Arrayy object.</p>
563
     */
564 5
    public function uksort($function)
565
    {
566 5
        return $this->customSortKeys($function);
567
    }
568
569
    /**
570
     * Unserialize an string and return this object.
571
     *
572
     * @param string $string
573
     *
574
     * @return static
575
     *                <p>(Mutable)</p>
576
     */
577 1
    public function unserialize($string)
578
    {
579 1
        parent::unserialize($string);
580
581 1
        return $this;
582
    }
583
584
    /**
585
     * Append a (key) + values to the current array.
586
     *
587
     * @param array $values
588
     * @param mixed $key
589
     *
590
     * @return static
591
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
592
     */
593 1
    public function appendArrayValues(array $values, $key = null)
594
    {
595 1
        if ($key !== null) {
596
            if (
597 1
                isset($this->array[$key])
598
                &&
599 1
                \is_array($this->array[$key])
600
            ) {
601 1
                foreach ($values as $value) {
602 1
                    $this->array[$key][] = $value;
603
                }
604
            } else {
605
                foreach ($values as $value) {
606 1
                    $this->array[$key] = $value;
607
                }
608
            }
609
        } else {
610
            foreach ($values as $value) {
611
                $this->array[] = $value;
612
            }
613
        }
614
615 1
        return $this;
616
    }
617
618
    /**
619
     * Add a suffix to each key.
620
     *
621
     * @param mixed $prefix
622
     *
623
     * @return static
624
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
625
     */
626 10 View Code Duplication
    public function appendToEachKey($prefix)
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...
627
    {
628
        // init
629 10
        $result = [];
630
631 10
        foreach ($this->getGenerator() as $key => $item) {
632 9
            if ($item instanceof self) {
633
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
634 9
            } elseif (\is_array($item)) {
635
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
636
                                              ->appendToEachKey($prefix)
637
                                              ->toArray();
638
            } else {
639 9
                $result[$prefix . $key] = $item;
640
            }
641
        }
642
643 10
        return self::create($result, $this->iteratorClass, false);
644
    }
645
646
    /**
647
     * Add a prefix to each value.
648
     *
649
     * @param mixed $prefix
650
     *
651
     * @return static
652
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
653
     */
654 10 View Code Duplication
    public function appendToEachValue($prefix)
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...
655
    {
656
        // init
657 10
        $result = [];
658
659 10
        foreach ($this->getGenerator() as $key => $item) {
660 9
            if ($item instanceof self) {
661
                $result[$key] = $item->appendToEachValue($prefix);
662 9
            } elseif (\is_array($item)) {
663
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
664 9
            } elseif (\is_object($item)) {
665 1
                $result[$key] = $item;
666
            } else {
667 9
                $result[$key] = $prefix . $item;
668
            }
669
        }
670
671 10
        return self::create($result, $this->iteratorClass, false);
672
    }
673
674
    /**
675
     * Convert an array into a object.
676
     *
677
     * @param array $array PHP array
678
     *
679
     * @return \stdClass
680
     */
681 4
    protected static function arrayToObject(array $array = []): \stdClass
682
    {
683
        // init
684 4
        $object = new \stdClass();
685
686 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
687 1
            return $object;
688
        }
689
690 3
        foreach ($array as $name => $value) {
691 3
            if (\is_array($value)) {
692 1
                $object->{$name} = self::arrayToObject($value);
693
            } else {
694 3
                $object->{$name} = $value;
695
            }
696
        }
697
698 3
        return $object;
699
    }
700
701
    /**
702
     * @param array|\Generator|null $input        <p>
703
     *                                            An array containing keys to return.
704
     *                                            </p>
705
     * @param mixed                 $search_value [optional] <p>
706
     *                                            If specified, then only keys containing these values are returned.
707
     *                                            </p>
708
     * @param bool                  $strict       [optional] <p>
709
     *                                            Determines if strict comparison (===) should be used during the
710
     *                                            search.
711
     *                                            </p>
712
     *
713
     * @return array
714
     *               <p>an array of all the keys in input</p>
715
     */
716 10
    protected function array_keys_recursive($input = null, $search_value = null, bool $strict = true): array
717
    {
718
        // init
719 10
        $keys = [];
720 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
721
722 10
        if ($input === null) {
723
            $input = $this->getGenerator();
724
        }
725
726 10
        foreach ($input as $key => $value) {
727
            if (
728 10
                $search_value === null
729
                ||
730
                (
731
                    \is_array($search_value) === true
732
                    &&
733 10
                    \in_array($key, $search_value, $strict)
734
                )
735
            ) {
736 10
                $keys[] = $key;
737
            }
738
739
            // check if recursive is needed
740 10
            if (\is_array($value) === true) {
741 10
                $keysTmp[] = $this->array_keys_recursive($value);
742
            }
743
        }
744
745 10
        return \array_merge($keys, ...$keysTmp);
746
    }
747
748
    /**
749
     * Sort an array in reverse order and maintain index association.
750
     *
751
     * @return static
752
     *                 <p>(Mutable) Return this Arrayy object.</p>
753
     */
754 4
    public function arsort()
755
    {
756 4
        \arsort($this->array);
757
758 4
        return $this;
759
    }
760
761
    /**
762
     * Iterate over the current array and execute a callback for each loop.
763
     *
764
     * @param \Closure $closure
765
     *
766
     * @return static
767
     *                <p>(Immutable)</p>
768
     */
769 2
    public function at(\Closure $closure)
770
    {
771 2
        $arrayy = clone $this;
772
773 2
        foreach ($arrayy->getGenerator() as $key => $value) {
774 2
            $closure($value, $key);
775
        }
776
777 2
        return static::create(
778 2
            $arrayy->toArray(),
779 2
            $this->iteratorClass,
780 2
            false
781
        );
782
    }
783
784
    /**
785
     * Returns the average value of the current array.
786
     *
787
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
788
     *
789
     * @return float|int
790
     *                   <p>The average value.</p>
791
     */
792 10
    public function average($decimals = 0)
793
    {
794 10
        $count = \count($this->array, \COUNT_NORMAL);
795
796 10
        if (!$count) {
797 2
            return 0;
798
        }
799
800 8
        if (!\is_int($decimals)) {
801 3
            $decimals = 0;
802
        }
803
804 8
        return \round(\array_sum($this->array) / $count, $decimals);
805
    }
806
807
    /**
808
     * @param mixed      $path
809
     * @param \callable  $callable
810
     * @param array|null $currentOffset
811
     */
812 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
813
    {
814 4
        if ($currentOffset === null) {
815 4
            $currentOffset = &$this->array;
816
        }
817
818 4
        $explodedPath = \explode($this->pathSeparator, $path);
819 4
        $nextPath = \array_shift($explodedPath);
820
821 4
        if (!isset($currentOffset[$nextPath])) {
822
            return;
823
        }
824
825 4
        if (!empty($explodedPath)) {
826 1
            $this->callAtPath(
827 1
                \implode($this->pathSeparator, $explodedPath),
828 1
                $callable,
829 1
                $currentOffset[$nextPath]
830
            );
831
        } else {
832 4
            $callable($currentOffset[$nextPath]);
833
        }
834 4
    }
835
836
    /**
837
     * Changes all keys in an array.
838
     *
839
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
840
     *                  or <strong>CASE_LOWER</strong> (default)</p>
841
     *
842
     * @return static
843
     *                 <p>(Immutable)</p>
844
     */
845 1
    public function changeKeyCase(int $case = \CASE_LOWER)
846
    {
847
        if (
848 1
            $case !== \CASE_LOWER
849
            &&
850 1
            $case !== \CASE_UPPER
851
        ) {
852
            $case = \CASE_LOWER;
853
        }
854
855 1
        $return = [];
856 1
        foreach ($this->array as $key => $value) {
857 1
            if ($case === \CASE_LOWER) {
858
                /** @noinspection PhpComposerExtensionStubsInspection */
859 1
                $key = \mb_strtolower((string) $key);
860
            } else {
861
                /** @noinspection PhpComposerExtensionStubsInspection */
862 1
                $key = \mb_strtoupper((string) $key);
863
            }
864
865 1
            $return[$key] = $value;
866
        }
867
868 1
        return static::create(
869 1
            $return,
870 1
            $this->iteratorClass,
871 1
            false
872
        );
873
    }
874
875
    /**
876
     * Change the path separator of the array wrapper.
877
     *
878
     * By default, the separator is: "."
879
     *
880
     * @param string $separator <p>Separator to set.</p>
881
     *
882
     * @return static
883
     *                <p>Mutable</p>
884
     */
885 1
    public function changeSeparator($separator)
886
    {
887 1
        $this->pathSeparator = $separator;
888
889 1
        return $this;
890
    }
891
892
    /**
893
     * Create a chunked version of the current array.
894
     *
895
     * @param int  $size         <p>Size of each chunk.</p>
896
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
897
     *
898
     * @return static
899
     *                <p>(Immutable) A new array of chunks from the original array.</p>
900
     */
901 4
    public function chunk($size, $preserveKeys = false)
902
    {
903 4
        return static::create(
904 4
            \array_chunk($this->array, $size, $preserveKeys),
905 4
            $this->iteratorClass,
906 4
            false
907
        );
908
    }
909
910
    /**
911
     * Clean all falsy values from the current array.
912
     *
913
     * @return static
914
     *                 <p>(Immutable)</p>
915
     */
916 8
    public function clean()
917
    {
918 8
        return $this->filter(
919 8
            function ($value) {
920 7
                return (bool) $value;
921 8
            }
922
        );
923
    }
924
925
    /**
926
     * WARNING!!! -> Clear the current array.
927
     *
928
     * @return static
929
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
930
     */
931 4
    public function clear()
932
    {
933 4
        $this->array = [];
934
935 4
        return $this;
936
    }
937
938
    /**
939
     * Check if an item is in the current array.
940
     *
941
     * @param float|int|string $value
942
     * @param bool             $recursive
943
     * @param bool             $strict
944
     *
945
     * @return bool
946
     */
947 22
    public function contains($value, $recursive = false, $strict = true): bool
948
    {
949 22
        if ($recursive === true) {
950 18
            return $this->in_array_recursive($value, $this->array, $strict);
951
        }
952
953 13
        return \in_array($value, $this->array, $strict);
954
    }
955
956
    /**
957
     * Check if an (case-insensitive) string is in the current array.
958
     *
959
     * @param string $value
960
     * @param bool   $recursive
961
     *
962
     * @return bool
963
     */
964 26
    public function containsCaseInsensitive($value, $recursive = false): bool
965
    {
966 26
        if ($recursive === true) {
967
            /** @noinspection PhpComposerExtensionStubsInspection */
968 26
            return $this->in_array_recursive(
969 26
                \mb_strtoupper((string) $value),
970 26
                $this->walk(
971 26
                    function (&$val) {
0 ignored issues
show
Documentation introduced by
function (&$val) { $...upper((string) $val); } is of type object<Closure>, but the function expects a object<callable>.

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...
972
                        /** @noinspection PhpComposerExtensionStubsInspection */
973 22
                        $val = \mb_strtoupper((string) $val);
974 26
                    },
975 26
                    true
976 26
                )->getArray(),
977 26
                true
978
            );
979
        }
980
981
        /** @noinspection PhpComposerExtensionStubsInspection */
982 13
        return \in_array(
983 13
            \mb_strtoupper((string) $value),
984 13
            $this->walk(
985 13
                function (&$val) {
0 ignored issues
show
Documentation introduced by
function (&$val) { $...upper((string) $val); } is of type object<Closure>, but the function expects a object<callable>.

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...
986
                    /** @noinspection PhpComposerExtensionStubsInspection */
987 11
                    $val = \mb_strtoupper((string) $val);
988 13
                },
989 13
                false
990 13
            )->getArray(),
991 13
            true
992
        );
993
    }
994
995
    /**
996
     * Check if the given key/index exists in the array.
997
     *
998
     * @param float|int|string $key <p>key/index to search for</p>
999
     *
1000
     * @return bool
1001
     *               <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1002
     */
1003 4
    public function containsKey($key): bool
1004
    {
1005 4
        return $this->offsetExists($key);
1006
    }
1007
1008
    /**
1009
     * Check if all given needles are present in the array as key/index.
1010
     *
1011
     * @param array $needles <p>The keys you are searching for.</p>
1012
     * @param bool  $recursive
1013
     *
1014
     * @return bool
1015
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1016
     */
1017 2
    public function containsKeys(array $needles, $recursive = false): bool
1018
    {
1019 2
        if ($recursive === true) {
1020 2
            return \count(
1021 2
                       \array_intersect($needles, $this->keys(true)->getArray()),
1022 2
                       \COUNT_RECURSIVE
1023
                   )
1024
                   ===
1025 2
                   \count(
1026 2
                       $needles,
1027 2
                       \COUNT_RECURSIVE
1028
                   );
1029
        }
1030
1031 1
        return \count(
1032 1
                   \array_intersect($needles, $this->keys()->getArray()),
1033 1
                   \COUNT_NORMAL
1034
               )
1035
               ===
1036 1
               \count(
1037 1
                   $needles,
1038 1
                   \COUNT_NORMAL
1039
               );
1040
    }
1041
1042
    /**
1043
     * Check if all given needles are present in the array as key/index.
1044
     *
1045
     * @param array $needles <p>The keys you are searching for.</p>
1046
     *
1047
     * @return bool
1048
     *               <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1049
     */
1050 1
    public function containsKeysRecursive(array $needles): bool
1051
    {
1052 1
        return $this->containsKeys($needles, true);
1053
    }
1054
1055
    /**
1056
     * alias: for "Arrayy->contains()"
1057
     *
1058
     * @see Arrayy::contains()
1059
     *
1060
     * @param float|int|string $value
1061
     *
1062
     * @return bool
1063
     */
1064 9
    public function containsValue($value): bool
1065
    {
1066 9
        return $this->contains($value);
1067
    }
1068
1069
    /**
1070
     * alias: for "Arrayy->contains($value, true)"
1071
     *
1072
     * @see Arrayy::contains()
1073
     *
1074
     * @param float|int|string $value
1075
     *
1076
     * @return bool
1077
     */
1078 18
    public function containsValueRecursive($value): bool
1079
    {
1080 18
        return $this->contains($value, true);
1081
    }
1082
1083
    /**
1084
     * Check if all given needles are present in the array.
1085
     *
1086
     * @param array $needles
1087
     *
1088
     * @return bool
1089
     *               <p>Returns true if all the given values exists in the array, false otherwise.</p>
1090
     */
1091 1
    public function containsValues(array $needles): bool
1092
    {
1093 1
        return \count(\array_intersect($needles, $this->array), \COUNT_NORMAL)
1094
               ===
1095 1
               \count($needles, \COUNT_NORMAL);
1096
    }
1097
1098
    /**
1099
     * Counts all the values of an array
1100
     *
1101
     * @see http://php.net/manual/en/function.array-count-values.php
1102
     *
1103
     * @return static
1104
     *                <p>
1105
     *                (Immutable)
1106
     *                An associative Arrayy-object of values from input as
1107
     *                keys and their count as value.
1108
     *                </p>
1109
     */
1110 1
    public function countValues(): self
1111
    {
1112 1
        return new static(\array_count_values($this->array));
1113
    }
1114
1115
    /**
1116
     * Creates an Arrayy object.
1117
     *
1118
     * @param mixed  $array
1119
     * @param string $iteratorClass
1120
     * @param bool   $checkForMissingPropertiesInConstructor
1121
     *
1122
     * @return static
1123
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1124
     */
1125 534
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1126
    {
1127 534
        return new static($array, $iteratorClass, $checkForMissingPropertiesInConstructor);
1128
    }
1129
1130
    /**
1131
     * WARNING: Creates an Arrayy object by reference.
1132
     *
1133
     * @param array $array
1134
     *
1135
     * @return static
1136
     *                 <p>(Mutable) Return this Arrayy object.</p>
1137
     */
1138 1
    public function createByReference(array &$array = []): self
1139
    {
1140 1
        $array = $this->fallbackForArray($array);
1141
1142 1
        $this->array = &$array;
1143
1144 1
        return $this;
1145
    }
1146
1147
    /**
1148
     * Create an new Arrayy object via JSON.
1149
     *
1150
     * @param string $json
1151
     *
1152
     * @return static
1153
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1154
     */
1155 5
    public static function createFromJson(string $json)
1156
    {
1157
        /** @noinspection PhpComposerExtensionStubsInspection */
1158 5
        return static::create(\json_decode($json, true));
1159
    }
1160
1161
    /**
1162
     * Create an new instance filled with values from an object that have implemented ArrayAccess.
1163
     *
1164
     * @param \ArrayAccess $object <p>Object that implements ArrayAccess</p>
1165
     *
1166
     * @return static
1167
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1168
     */
1169 4
    public static function createFromObject(\ArrayAccess $object)
1170
    {
1171
        // init
1172 4
        $array = new static();
1173
1174 4
        if ($object instanceof self) {
1175 4
            $objectArray = $object->getGenerator();
1176
        } else {
1177
            $objectArray = $object;
1178
        }
1179
1180 4
        foreach ($objectArray as $key => $value) {
1181 3
            $array[$key] = $value;
1182
        }
1183
1184 4
        return $array;
1185
    }
1186
1187
    /**
1188
     * Create an new instance filled with a copy of values from a "Generator"-object.
1189
     *
1190
     * @param \Generator $generator
1191
     *
1192
     * @return static
1193
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1194
     */
1195 4
    public static function createFromGeneratorImmutable(\Generator $generator)
1196
    {
1197
        // init
1198 4
        $array = new static();
1199
1200 4
        foreach ($generator as $key => $value) {
1201 3
            $array[$key] = $value;
1202
        }
1203
1204 4
        return $array;
1205
    }
1206
1207
    /**
1208
     * Create an new instance filled with values from an object.
1209
     *
1210
     * @param object $object
1211
     *
1212
     * @return static
1213
     *                 <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1214
     */
1215 5
    public static function createFromObjectVars($object): self
1216
    {
1217 5
        return new static(self::objectToArray($object));
1218
    }
1219
1220
    /**
1221
     * Create an new Arrayy object via string.
1222
     *
1223
     * @param string      $str       <p>The input string.</p>
1224
     * @param string|null $delimiter <p>The boundary string.</p>
1225
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1226
     *                               used.</p>
1227
     *
1228
     * @return static
1229
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1230
     */
1231 8
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null)
1232
    {
1233 8
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1234 1
            \preg_match_all($regEx, $str, $array);
1235
1236 1
            if (!empty($array)) {
1237 1
                $array = $array[0];
1238
            }
1239
        } else {
1240 7
            $array = \explode($delimiter, $str);
1241
        }
1242
1243
        // trim all string in the array
1244 8
        \array_walk(
1245
            $array,
1246 8
            function (&$val) {
1247
                /** @noinspection ReferenceMismatchInspection */
1248 8
                if (\is_string($val)) {
1249 8
                    $val = \trim($val);
1250
                }
1251 8
            }
1252
        );
1253
1254 8
        return static::create($array);
1255
    }
1256
1257
    /**
1258
     * Create an new instance containing a range of elements.
1259
     *
1260
     * @param mixed $low  <p>First value of the sequence.</p>
1261
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1262
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1263
     *
1264
     * @return static
1265
     *                 <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1266
     */
1267 1
    public static function createWithRange($low, $high, int $step = 1)
1268
    {
1269 1
        return static::create(\range($low, $high, $step));
1270
    }
1271
1272
    /**
1273
     * Custom sort by index via "uksort".
1274
     *
1275
     * @see http://php.net/manual/en/function.uksort.php
1276
     *
1277
     * @param \callable $function
1278
     *
1279
     * @throws \InvalidArgumentException
1280
     *
1281
     * @return static
1282
     *                 <p>(Mutable) Return this Arrayy object.</p>
1283
     */
1284 5 View Code Duplication
    public function customSortKeys($function)
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...
1285
    {
1286 5
        if (!\is_callable($function)) {
1287
            throw new \InvalidArgumentException(
1288
                'Passed function must be callable'
1289
            );
1290
        }
1291
1292 5
        \uksort($this->array, $function);
1293
1294 5
        return $this;
1295
    }
1296
1297
    /**
1298
     * Custom sort by value via "usort".
1299
     *
1300
     * @see http://php.net/manual/en/function.usort.php
1301
     *
1302
     * @param \callable $function
1303
     *
1304
     * @throws \InvalidArgumentException
1305
     *
1306
     * @return static
1307
     *                 <p>(Mutable) Return this Arrayy object.</p>
1308
     */
1309 5 View Code Duplication
    public function customSortValues($function)
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...
1310
    {
1311 5
        if (!\is_callable($function)) {
1312
            throw new \InvalidArgumentException(
1313
                'Passed function must be callable'
1314
            );
1315
        }
1316
1317 5
        \usort($this->array, $function);
1318
1319 5
        return $this;
1320
    }
1321
1322
    /**
1323
     * Return values that are only in the current array.
1324
     *
1325
     * @param array $array
1326
     *
1327
     * @return static
1328
     *                <p>(Immutable)</p>
1329
     */
1330 12
    public function diff(array $array = [])
1331
    {
1332 12
        $result = \array_diff($this->array, $array);
1333
1334 12
        return static::create($result, $this->iteratorClass, false);
1335
    }
1336
1337
    /**
1338
     * Return values that are only in the current multi-dimensional array.
1339
     *
1340
     * @param array      $array
1341
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1342
     *
1343
     * @return static
1344
     *                <p>(Immutable)</p>
1345
     */
1346 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null)
1347
    {
1348
        // init
1349 1
        $result = [];
1350
1351
        if (
1352 1
            $helperVariableForRecursion !== null
1353
            &&
1354 1
            \is_array($helperVariableForRecursion)
1355
        ) {
1356
            $arrayForTheLoop = $helperVariableForRecursion;
1357
        } else {
1358 1
            $arrayForTheLoop = $this->getGenerator();
1359
        }
1360
1361 1
        foreach ($arrayForTheLoop as $key => $value) {
1362 1
            if ($value instanceof self) {
1363
                $value = $value->getArray();
1364
            }
1365
1366 1
            if (\array_key_exists($key, $array)) {
1367 1
                if ($value !== $array[$key]) {
1368 1
                    $result[$key] = $value;
1369
                }
1370
            } else {
1371 1
                $result[$key] = $value;
1372
            }
1373
        }
1374
1375 1
        return static::create($result, $this->iteratorClass, false);
1376
    }
1377
1378
    /**
1379
     * Return values that are only in the new $array.
1380
     *
1381
     * @param array $array
1382
     *
1383
     * @return static
1384
     *                <p>(Immutable)</p>
1385
     */
1386 8
    public function diffReverse(array $array = [])
1387
    {
1388 8
        return static::create(
1389 8
            \array_diff($array, $this->array),
1390 8
            $this->iteratorClass,
1391 8
            false
1392
        );
1393
    }
1394
1395
    /**
1396
     * Divide an array into two arrays. One with keys and the other with values.
1397
     *
1398
     * @return static
1399
     *                <p>(Immutable)</p>
1400
     */
1401 1
    public function divide()
1402
    {
1403 1
        return static::create(
1404
            [
1405 1
                $this->keys(),
1406 1
                $this->values(),
1407
            ],
1408 1
            $this->iteratorClass,
1409 1
            false
1410
        );
1411
    }
1412
1413
    /**
1414
     * Iterate over the current array and modify the array's value.
1415
     *
1416
     * @param \Closure $closure
1417
     *
1418
     * @return static
1419
     *                <p>(Immutable)</p>
1420
     */
1421 4 View Code Duplication
    public function each(\Closure $closure)
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...
1422
    {
1423
        // init
1424 4
        $array = [];
1425
1426 4
        foreach ($this->getGenerator() as $key => $value) {
1427 4
            $array[$key] = $closure($value, $key);
1428
        }
1429
1430 4
        return static::create($array, $this->iteratorClass, false);
1431
    }
1432
1433
    /**
1434
     * Check if a value is in the current array using a closure.
1435
     *
1436
     * @param \Closure $closure
1437
     *
1438
     * @return bool
1439
     *              <p>Returns true if the given value is found, false otherwise.</p>
1440
     */
1441 4
    public function exists(\Closure $closure): bool
1442
    {
1443
        // init
1444 4
        $isExists = false;
1445
1446 4
        foreach ($this->getGenerator() as $key => $value) {
1447 3
            if ($closure($value, $key)) {
1448 1
                $isExists = true;
1449
1450 3
                break;
1451
            }
1452
        }
1453
1454 4
        return $isExists;
1455
    }
1456
1457
    /**
1458
     * create a fallback for array
1459
     *
1460
     * 1. use the current array, if it's a array
1461
     * 2. fallback to empty array, if there is nothing
1462
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
1463
     * 4. call "createFromObject()" on object, if there is a "\ArrayAccess"-object
1464
     * 5. call "__toArray()" on object, if the method exists
1465
     * 6. cast a string or object with "__toString()" into an array
1466
     * 7. throw a "InvalidArgumentException"-Exception
1467
     *
1468
     * @param mixed $array
1469
     *
1470
     * @throws \InvalidArgumentException
1471
     *
1472
     * @return array
1473
     */
1474 889
    protected function fallbackForArray(&$array): array
1475
    {
1476 889
        if (\is_array($array)) {
1477 886
            return $array;
1478
        }
1479
1480 11
        if (!$array) {
1481 6
            return [];
1482
        }
1483
1484 10
        $isObject = \is_object($array);
1485
1486 10
        if ($isObject && $array instanceof self) {
1487 1
            return $array->getArray();
1488
        }
1489
1490 9
        if ($isObject && $array instanceof \ArrayAccess) {
1491
            /** @noinspection ReferenceMismatchInspection */
1492
            return static::createFromObject($array)->getArray();
1493
        }
1494
1495 9
        if ($isObject && $array instanceof \ArrayObject) {
1496
            return $array->getArrayCopy();
1497
        }
1498
1499 9
        if ($isObject && \method_exists($array, '__toArray')) {
1500
            return (array) $array->__toArray();
1501
        }
1502
1503
        /** @noinspection ReferenceMismatchInspection */
1504
        if (
1505 9
            \is_string($array)
1506
            ||
1507 9
            ($isObject && \method_exists($array, '__toString'))
1508
        ) {
1509 7
            return [(string) $array];
1510
        }
1511
1512 2
        throw new \InvalidArgumentException(
1513 2
            'Passed value should be a array'
1514
        );
1515
    }
1516
1517
    /**
1518
     * Fill the array until "$num" with "$default" values.
1519
     *
1520
     * @param int   $num
1521
     * @param mixed $default
1522
     *
1523
     * @return static
1524
     *                <p>(Immutable)</p>
1525
     */
1526 8
    public function fillWithDefaults(int $num, $default = null)
1527
    {
1528 8
        if ($num < 0) {
1529 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1530
        }
1531
1532 7
        $tmpArray = $this->array;
1533
1534 7
        $count = \count($tmpArray);
1535
1536 7
        while ($count < $num) {
1537 4
            $tmpArray[] = $default;
1538 4
            $count++;
1539
        }
1540
1541 7
        return static::create($tmpArray, $this->iteratorClass, false);
1542
    }
1543
1544
    /**
1545
     * Find all items in an array that pass the truth test.
1546
     *
1547
     * @param \Closure|null $closure [optional] <p>
1548
     *                               The callback function to use
1549
     *                               </p>
1550
     *                               <p>
1551
     *                               If no callback is supplied, all entries of
1552
     *                               input equal to false (see
1553
     *                               converting to
1554
     *                               boolean) will be removed.
1555
     *                               </p>
1556
     *                               * @param int $flag [optional] <p>
1557
     *                               Flag determining what arguments are sent to <i>callback</i>:
1558
     *                               </p><ul>
1559
     *                               <li>
1560
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1561
     *                               to <i>callback</i> instead of the value</span>
1562
     *                               </li>
1563
     *                               <li>
1564
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1565
     *                               arguments to <i>callback</i> instead of the value</span>
1566
     *                               </li>
1567
     *                               </ul>
1568
     *
1569
     * @return static
1570
     *                <p>(Immutable)</p>
1571
     */
1572 10
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
1573
    {
1574 10
        if (!$closure) {
1575 1
            return $this->clean();
1576
        }
1577
1578 10
        return static::create(
1579 10
            \array_filter($this->array, $closure, $flag),
1580 10
            $this->iteratorClass,
1581 10
            false
1582
        );
1583
    }
1584
1585
    /**
1586
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1587
     * property within that.
1588
     *
1589
     * @param string          $property
1590
     * @param string|string[] $value
1591
     * @param string          $comparisonOp
1592
     *                            <p>
1593
     *                            'eq' (equals),<br />
1594
     *                            'gt' (greater),<br />
1595
     *                            'gte' || 'ge' (greater or equals),<br />
1596
     *                            'lt' (less),<br />
1597
     *                            'lte' || 'le' (less or equals),<br />
1598
     *                            'ne' (not equals),<br />
1599
     *                            'contains',<br />
1600
     *                            'notContains',<br />
1601
     *                            'newer' (via strtotime),<br />
1602
     *                            'older' (via strtotime),<br />
1603
     *                            </p>
1604
     *
1605
     * @return static
1606
     *                <p>(Immutable)</p>
1607
     */
1608 1
    public function filterBy(string $property, $value, string $comparisonOp = null)
1609
    {
1610 1
        if (!$comparisonOp) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $comparisonOp of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1611 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1612
        }
1613
1614
        $ops = [
1615 1
            'eq' => function ($item, $prop, $value) {
1616 1
                return $item[$prop] === $value;
1617 1
            },
1618 1
            'gt' => function ($item, $prop, $value) {
1619
                return $item[$prop] > $value;
1620 1
            },
1621 1
            'ge' => function ($item, $prop, $value) {
1622
                return $item[$prop] >= $value;
1623 1
            },
1624 1
            'gte' => function ($item, $prop, $value) {
1625
                return $item[$prop] >= $value;
1626 1
            },
1627 1
            'lt' => function ($item, $prop, $value) {
1628 1
                return $item[$prop] < $value;
1629 1
            },
1630 1
            'le' => function ($item, $prop, $value) {
1631
                return $item[$prop] <= $value;
1632 1
            },
1633 1
            'lte' => function ($item, $prop, $value) {
1634
                return $item[$prop] <= $value;
1635 1
            },
1636 1
            'ne' => function ($item, $prop, $value) {
1637
                return $item[$prop] !== $value;
1638 1
            },
1639 1
            'contains' => function ($item, $prop, $value) {
1640 1
                return \in_array($item[$prop], (array) $value, true);
1641 1
            },
1642 1
            'notContains' => function ($item, $prop, $value) {
1643
                return !\in_array($item[$prop], (array) $value, true);
1644 1
            },
1645 1
            'newer' => function ($item, $prop, $value) {
1646
                return \strtotime($item[$prop]) > \strtotime($value);
1647 1
            },
1648 1
            'older' => function ($item, $prop, $value) {
1649
                return \strtotime($item[$prop]) < \strtotime($value);
1650 1
            },
1651
        ];
1652
1653 1
        $result = \array_values(
1654 1
            \array_filter(
1655 1
                (array) $this->array,
1656 1
                function ($item) use (
1657 1
                    $property,
1658 1
                    $value,
1659 1
                    $ops,
1660 1
                    $comparisonOp
1661
                ) {
1662 1
                    $item = (array) $item;
1663 1
                    $itemArrayy = new self($item);
1664 1
                    $item[$property] = $itemArrayy->get($property, []);
1665
1666 1
                    return $ops[$comparisonOp]($item, $property, $value);
1667 1
                }
1668
            )
1669
        );
1670
1671 1
        return static::create($result, $this->iteratorClass, false);
1672
    }
1673
1674
    /**
1675
     * Find the first item in an array that passes the truth test,
1676
     *  otherwise return false
1677
     *
1678
     * @param \Closure $closure
1679
     *
1680
     * @return false|mixed
1681
     *                      <p>Return false if we did not find the value.</p>
1682
     */
1683 8
    public function find(\Closure $closure)
1684
    {
1685 8
        foreach ($this->getGenerator() as $key => $value) {
1686 6
            if ($closure($value, $key)) {
1687 6
                return $value;
1688
            }
1689
        }
1690
1691 3
        return false;
1692
    }
1693
1694
    /**
1695
     * find by ...
1696
     *
1697
     * @param string          $property
1698
     * @param string|string[] $value
1699
     * @param string          $comparisonOp
1700
     *
1701
     * @return static
1702
     *                 <p>(Immutable)</p>
1703
     */
1704
    public function findBy(string $property, $value, string $comparisonOp = 'eq')
1705
    {
1706
        return $this->filterBy($property, $value, $comparisonOp);
1707
    }
1708
1709
    /**
1710
     * Get the first value from the current array.
1711
     *
1712
     * @return mixed
1713
     *               <p>Return null if there wasn't a element.</p>
1714
     */
1715 13
    public function first()
1716
    {
1717 13
        $tmpArray = $this->array;
1718
1719 13
        return \array_shift($tmpArray);
1720
    }
1721
1722
    /**
1723
     * Get the first value(s) from the current array.
1724
     *
1725
     * @param int|null $number <p>How many values you will take?</p>
1726
     *
1727
     * @return static
1728
     *                <p>(Immutable)</p>
1729
     */
1730 28 View Code Duplication
    public function firstsImmutable(int $number = null)
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...
1731
    {
1732 28
        if ($number === null) {
1733 7
            $arrayTmp = $this->array;
1734 7
            $array = (array) \array_shift($arrayTmp);
1735
        } else {
1736 21
            $number = (int) $number;
1737 21
            $arrayTmp = $this->array;
1738 21
            $array = \array_splice($arrayTmp, 0, $number);
1739
        }
1740
1741 28
        return static::create($array, $this->iteratorClass, false);
1742
    }
1743
1744
    /**
1745
     * Get the first value(s) from the current array.
1746
     *
1747
     * @param int|null $number <p>How many values you will take?</p>
1748
     *
1749
     * @return static
1750
     *                <p>(Mutable)</p>
1751
     */
1752 26
    public function firstsMutable(int $number = null)
1753
    {
1754 26
        if ($number === null) {
1755 11
            $this->array = (array) \array_shift($this->array);
1756
        } else {
1757 15
            $number = (int) $number;
1758 15
            $this->array = \array_splice($this->array, 0, $number);
1759
        }
1760
1761 26
        return $this;
1762
    }
1763
1764
    /**
1765
     * Exchanges all keys with their associated values in an array.
1766
     *
1767
     * @return static
1768
     *                <p>(Immutable)</p>
1769
     */
1770 1
    public function flip()
1771
    {
1772 1
        return static::create(
1773 1
            \array_flip($this->array),
1774 1
            $this->iteratorClass,
1775 1
            false
1776
        );
1777
    }
1778
1779
    /**
1780
     * Get a value from an array (optional using dot-notation).
1781
     *
1782
     * @param mixed $key       <p>The key to look for.</p>
1783
     * @param mixed $fallback  <p>Value to fallback to.</p>
1784
     * @param array $array     <p>The array to get from, if it's set to "null" we use the current array from the
1785
     *                         class.</p>
1786
     *
1787
     * @return mixed|static
1788
     */
1789 68
    public function get($key, $fallback = null, array $array = null)
1790
    {
1791 68
        if ($array !== null) {
1792 4
            $usedArray = $array;
1793
        } else {
1794 64
            $usedArray = $this->array;
1795
        }
1796
1797 68
        if ($key === null) {
1798 1
            return static::create($usedArray, $this->iteratorClass, false);
1799
        }
1800
1801
        // php cast "bool"-index into "int"-index
1802 68
        if ((bool) $key === $key) {
1803 3
            $key = (int) $key;
1804
        }
1805
1806 68
        if (\array_key_exists($key, $usedArray) === true) {
1807 58
            if (\is_array($usedArray[$key])) {
1808 8
                return static::create($usedArray[$key], $this->iteratorClass, false);
1809
            }
1810
1811 52
            return $usedArray[$key];
1812
        }
1813
1814
        // Crawl through array, get key according to object or not
1815 22
        foreach (\explode($this->pathSeparator, (string) $key) as $segment) {
1816 22
            if (!isset($usedArray[$segment])) {
1817 21
                return $fallback instanceof \Closure ? $fallback() : $fallback;
1818
            }
1819
1820 6
            $usedArray = $usedArray[$segment];
1821
        }
1822
1823 6
        if (\is_array($usedArray)) {
1824 1
            return static::create($usedArray, $this->iteratorClass, false);
1825
        }
1826
1827 6
        return $usedArray;
1828
    }
1829
1830
    /**
1831
     * Get the current array from the "Arrayy"-object.
1832
     *
1833
     * @return array
1834
     */
1835 580
    public function getArray(): array
1836
    {
1837
        // init
1838 580
        $array = [];
1839
1840 580
        foreach ($this->array as $key => &$value) {
1841 477
            if ($value instanceof self) {
1842
                $array[$key] = $value->getArray();
1843
            } else {
1844 477
                $array[$key] = $value;
1845
            }
1846
        }
1847
1848 580
        return $array;
1849
    }
1850
1851
    /**
1852
     * Get the current array from the "Arrayy"-object as generator.
1853
     *
1854
     * @return \Generator
1855
     */
1856 125
    public function getGenerator(): \Generator
1857
    {
1858 125
        yield from $this->array;
1859 93
    }
1860
1861
    /**
1862
     * Returns the values from a single column of the input array, identified by
1863
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1864
     *
1865
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1866
     * array by the values from the $indexKey column in the input array.
1867
     *
1868
     * @param mixed $columnKey
1869
     * @param mixed $indexKey
1870
     *
1871
     * @return static
1872
     *                <p>(Immutable)</p>
1873
     */
1874 1
    public function getColumn($columnKey = null, $indexKey = null)
1875
    {
1876 1
        return static::create(
1877 1
            \array_column($this->array, $columnKey, $indexKey),
1878 1
            $this->iteratorClass,
1879 1
            false
1880
        );
1881
    }
1882
1883
    /**
1884
     * Get correct PHP constant for direction.
1885
     *
1886
     * @param int|string $direction
1887
     *
1888
     * @return int
1889
     */
1890 39
    protected function getDirection($direction): int
1891
    {
1892 39
        if (\is_string($direction)) {
1893 10
            $direction = \strtolower($direction);
1894
1895 10
            if ($direction === 'desc') {
1896 2
                $direction = \SORT_DESC;
1897
            } else {
1898 8
                $direction = \SORT_ASC;
1899
            }
1900
        }
1901
1902
        if (
1903 39
            $direction !== \SORT_DESC
1904
            &&
1905 39
            $direction !== \SORT_ASC
1906
        ) {
1907
            $direction = \SORT_ASC;
1908
        }
1909
1910 39
        return $direction;
1911
    }
1912
1913
    /**
1914
     * alias: for "Arrayy->keys()"
1915
     *
1916
     * @see Arrayy::keys()
1917
     *
1918
     * @return static
1919
     *                <p>(Immutable)</p>
1920
     */
1921 1
    public function getKeys()
1922
    {
1923 1
        return $this->keys();
1924
    }
1925
1926
    /**
1927
     * Get the current array from the "Arrayy"-object as object.
1928
     *
1929
     * @return \stdClass
1930
     */
1931 4
    public function getObject(): \stdClass
1932
    {
1933 4
        return self::arrayToObject($this->getArray());
1934
    }
1935
1936
    /**
1937
     * @return array|Property[]
1938
     */
1939 9
    protected function getPublicProperties(): array
1940
    {
1941 9
        static $PROPERTY_CACHE = [];
1942 9
        $cacheKey = 'Class::' . static::class;
1943
1944 9
        if (isset($PROPERTY_CACHE[$cacheKey])) {
1945 8
            return $PROPERTY_CACHE[$cacheKey];
1946
        }
1947
1948
        // init
1949 2
        $properties = [];
1950
1951 2
        $reflector = new \ReflectionClass($this);
1952 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
1953 2
        $docblock = $factory->create($reflector->getDocComment());
1954 2
        foreach ($docblock->getTagsByName('property') as $tag) {
1955
            /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
1956 2
            $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
1957
        }
1958
1959 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
1960
    }
1961
1962
    /**
1963
     * alias: for "Arrayy->randomImmutable()"
1964
     *
1965
     * @see Arrayy::randomImmutable()
1966
     *
1967
     * @return static
1968
     *                <p>(Immutable)</p>
1969
     */
1970 4
    public function getRandom()
1971
    {
1972 4
        return $this->randomImmutable();
1973
    }
1974
1975
    /**
1976
     * alias: for "Arrayy->randomKey()"
1977
     *
1978
     * @see Arrayy::randomKey()
1979
     *
1980
     * @return mixed
1981
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
1982
     */
1983 3
    public function getRandomKey()
1984
    {
1985 3
        return $this->randomKey();
1986
    }
1987
1988
    /**
1989
     * alias: for "Arrayy->randomKeys()"
1990
     *
1991
     * @see Arrayy::randomKeys()
1992
     *
1993
     * @param int $number
1994
     *
1995
     * @return static
1996
     *                 <p>(Immutable)</p>
1997
     */
1998 8
    public function getRandomKeys(int $number)
1999
    {
2000 8
        return $this->randomKeys($number);
2001
    }
2002
2003
    /**
2004
     * alias: for "Arrayy->randomValue()"
2005
     *
2006
     * @see Arrayy::randomValue()
2007
     *
2008
     * @return mixed
2009
     *                <p>Get a random value or null if there wasn't a value.</p>
2010
     */
2011 3
    public function getRandomValue()
2012
    {
2013 3
        return $this->randomValue();
2014
    }
2015
2016
    /**
2017
     * alias: for "Arrayy->randomValues()"
2018
     *
2019
     * @see Arrayy::randomValues()
2020
     *
2021
     * @param int $number
2022
     *
2023
     * @return static
2024
     *                 <p>(Immutable)</p>
2025
     */
2026 6
    public function getRandomValues(int $number)
2027
    {
2028 6
        return $this->randomValues($number);
2029
    }
2030
2031
    /**
2032
     * Group values from a array according to the results of a closure.
2033
     *
2034
     * @param \callable $grouper <p>A callable function name.</p>
2035
     * @param bool      $saveKeys
2036
     *
2037
     * @return static
2038
     *                 <p>(Immutable)</p>
2039
     */
2040 4
    public function group($grouper, bool $saveKeys = false)
2041
    {
2042
        // init
2043 4
        $result = [];
2044
2045
        // Iterate over values, group by property/results from closure.
2046 4
        foreach ($this->getGenerator() as $key => $value) {
2047 4
            $groupKey = \is_callable($grouper) ? $grouper($value, $key) : $this->get($grouper, null, $this->array);
2048 4
            $newValue = $this->get($groupKey, null, $result);
2049
2050 4
            if ($groupKey instanceof self) {
2051
                $groupKey = $groupKey->getArray();
2052
            }
2053
2054 4
            if ($newValue instanceof self) {
2055 4
                $newValue = $newValue->getArray();
2056
            }
2057
2058
            // Add to results.
2059 4
            if ($groupKey !== null) {
2060 3
                if ($saveKeys) {
2061 2
                    $result[$groupKey] = $newValue;
2062 2
                    $result[$groupKey][$key] = $value;
2063
                } else {
2064 1
                    $result[$groupKey] = $newValue;
2065 4
                    $result[$groupKey][] = $value;
2066
                }
2067
            }
2068
        }
2069
2070 4
        return static::create($result, $this->iteratorClass, false);
2071
    }
2072
2073
    /**
2074
     * Check if an array has a given key.
2075
     *
2076
     * @param mixed $key
2077
     *
2078
     * @return bool
2079
     */
2080 23
    public function has($key): bool
2081
    {
2082 23
        static $UN_FOUND = null;
2083
2084 23
        if ($UN_FOUND === null) {
2085
            // Generate unique string to use as marker.
2086 1
            $UN_FOUND = \uniqid('arrayy', true);
2087
        }
2088
2089 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2090
    }
2091
2092
    /**
2093
     * Implodes the values of this array.
2094
     *
2095
     * @param string $glue
2096
     *
2097
     * @return string
2098
     */
2099 27
    public function implode(string $glue = ''): string
2100
    {
2101 27
        return $this->implode_recursive($glue, $this->array, false);
2102
    }
2103
2104
    /**
2105
     * Implodes the keys of this array.
2106
     *
2107
     * @param string $glue
2108
     *
2109
     * @return string
2110
     */
2111 8
    public function implodeKeys(string $glue = ''): string
2112
    {
2113 8
        return $this->implode_recursive($glue, $this->array, true);
2114
    }
2115
2116
    /**
2117
     * @param mixed               $glue
2118
     * @param array|static|string $pieces
2119
     * @param bool                $useKeys
2120
     *
2121
     * @return string
2122
     */
2123 35
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
2124
    {
2125 35
        if ($pieces instanceof self) {
2126 1
            $pieces = $pieces->getArray();
2127
        }
2128
2129 35
        if (\is_array($pieces)) {
2130 35
            $pieces_count = \count($pieces, \COUNT_NORMAL);
2131 35
            $pieces_count_not_zero = $pieces_count > 0;
2132
2133 35
            return \implode(
2134 35
                $glue,
2135 35
                \array_map(
2136 35
                    [$this, 'implode_recursive'],
2137 35
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
2138 35
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
2139
                )
2140
            );
2141
        }
2142
2143 35
        return (string) $pieces;
2144
    }
2145
2146
    /**
2147
     * @param mixed                 $needle   <p>
2148
     *                                        The searched value.
2149
     *                                        </p>
2150
     *                                        <p>
2151
     *                                        If needle is a string, the comparison is done
2152
     *                                        in a case-sensitive manner.
2153
     *                                        </p>
2154
     * @param array|\Generator|null $haystack <p>
2155
     *                                        The array.
2156
     *                                        </p>
2157
     * @param bool                  $strict   [optional] <p>
2158
     *                                        If the third parameter strict is set to true
2159
     *                                        then the in_array function will also check the
2160
     *                                        types of the
2161
     *                                        needle in the haystack.
2162
     *                                        </p>
2163
     *
2164
     * @return bool
2165
     *               <p>true if needle is found in the array, false otherwise</p>
2166
     */
2167 44
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
2168
    {
2169 44
        if ($haystack === null) {
2170
            $haystack = $this->getGenerator();
2171
        }
2172
2173 44
        foreach ($haystack as $item) {
2174 36
            if (\is_array($item) === true) {
2175 8
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
2176
            } else {
2177 36
                $returnTmp = ($strict === true ? $item === $needle : $item == $needle);
2178
            }
2179
2180 36
            if ($returnTmp === true) {
2181 36
                return true;
2182
            }
2183
        }
2184
2185 18
        return false;
2186
    }
2187
2188
    /**
2189
     * Given a list and an iterate-function that returns
2190
     * a key for each element in the list (or a property name),
2191
     * returns an object with an index of each item.
2192
     *
2193
     * @param mixed $key
2194
     *
2195
     * @return static
2196
     *                <p>(Immutable)</p>
2197
     */
2198 4
    public function indexBy($key)
2199
    {
2200
        // init
2201 4
        $results = [];
2202
2203 4
        foreach ($this->getGenerator() as $a) {
2204 4
            if (\array_key_exists($key, $a) === true) {
2205 4
                $results[$a[$key]] = $a;
2206
            }
2207
        }
2208
2209 4
        return static::create($results, $this->iteratorClass, false);
2210
    }
2211
2212
    /**
2213
     * alias: for "Arrayy->searchIndex()"
2214
     *
2215
     * @see Arrayy::searchIndex()
2216
     *
2217
     * @param mixed $value <p>The value to search for.</p>
2218
     *
2219
     * @return mixed
2220
     */
2221 4
    public function indexOf($value)
2222
    {
2223 4
        return $this->searchIndex($value);
2224
    }
2225
2226
    /**
2227
     * Get everything but the last..$to items.
2228
     *
2229
     * @param int $to
2230
     *
2231
     * @return static
2232
     *                 <p>(Immutable)</p>
2233
     */
2234 12
    public function initial(int $to = 1)
2235
    {
2236 12
        return $this->firstsImmutable(\count($this->array, \COUNT_NORMAL) - $to);
2237
    }
2238
2239
    /**
2240
     * @param mixed $value
2241
     */
2242
    protected function internalGetArray(&$value)
2243
    {
2244
        if ($value instanceof self) {
2245
            $valueTmp = $value->getArray();
2246
            if (\count($valueTmp, \COUNT_NORMAL) === 0) {
2247
                $value = [];
2248
            } else {
2249
                /** @noinspection PhpUnusedLocalVariableInspection */
2250
                $value = &$valueTmp;
2251
            }
2252
        }
2253
2254
        /** @noinspection PhpComposerExtensionStubsInspection */
2255
        /** @noinspection NotOptimalIfConditionsInspection */
2256
        if (
2257
            \class_exists('JsonSerializable')
2258
            &&
2259
            $value instanceof \JsonSerializable
0 ignored issues
show
Bug introduced by
The class JsonSerializable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
2260
        ) {
2261
2262
            /** @noinspection PhpUnusedLocalVariableInspection */
2263
            $value = &$value->jsonSerialize();
2264
        }
2265
    }
2266
2267
    /**
2268
     * Internal mechanics of remove method.
2269
     *
2270
     * @param mixed $key
2271
     *
2272
     * @return bool
2273
     */
2274 18
    protected function internalRemove($key): bool
2275
    {
2276 18
        $path = \explode($this->pathSeparator, (string) $key);
2277
2278
        // Crawl though the keys
2279 18
        while (\count($path, \COUNT_NORMAL) > 1) {
2280
            $key = \array_shift($path);
2281
2282
            if (!$this->has($key)) {
2283
                return false;
2284
            }
2285
2286
            $this->array = &$this->array[$key];
2287
        }
2288
2289 18
        $key = \array_shift($path);
2290
2291 18
        unset($this->array[$key]);
2292
2293 18
        return true;
2294
    }
2295
2296
    /**
2297
     * Internal mechanic of set method.
2298
     *
2299
     * @param string|null $key
2300
     * @param mixed       $value
2301
     *
2302
     * @return bool
2303
     */
2304 778
    protected function internalSet($key, $value): bool
2305
    {
2306 778
        if ($this->checkPropertyTypes === true) {
2307 7
            if (isset($this->properties[$key]) === false) {
2308
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
2309
            }
2310
2311 7
            $this->properties[$key]->checkType($value);
2312
        }
2313
2314 777
        if ($key === null) {
2315
            return false;
2316
        }
2317
2318
        // init
2319 777
        $array = &$this->array;
2320 777
        $path = \explode($this->pathSeparator, (string) $key);
2321
2322
        // Crawl through the keys
2323 777
        while (\count($path, \COUNT_NORMAL) > 1) {
2324 3
            $key = \array_shift($path);
2325
2326 3
            $array = &$array[$key];
2327
        }
2328
2329 777
        $array[\array_shift($path)] = $value;
2330
2331 777
        return true;
2332
    }
2333
2334
    /**
2335
     * Return an array with all elements found in input array.
2336
     *
2337
     * @param array $search
2338
     *
2339
     * @return static
2340
     *                 <p>(Immutable)</p>
2341
     */
2342 2
    public function intersection(array $search)
2343
    {
2344 2
        return static::create(
2345 2
            \array_values(\array_intersect($this->array, $search)),
2346 2
            $this->iteratorClass,
2347 2
            false
2348
        );
2349
    }
2350
2351
    /**
2352
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2353
     *
2354
     * @param array $search
2355
     *
2356
     * @return bool
2357
     */
2358 1
    public function intersects(array $search): bool
2359
    {
2360 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2361
    }
2362
2363
    /**
2364
     * Invoke a function on all of an array's values.
2365
     *
2366
     * @param mixed $callable
2367
     * @param mixed $arguments
2368
     *
2369
     * @return static
2370
     *                 <p>(Immutable)</p>
2371
     */
2372 1
    public function invoke($callable, $arguments = [])
2373
    {
2374
        // If one argument given for each iteration, create an array for it.
2375 1
        if (!\is_array($arguments)) {
2376 1
            $arguments = StaticArrayy::repeat(
2377 1
                $arguments,
2378 1
                \count($this->array, \COUNT_NORMAL)
2379 1
            )->getArray();
2380
        }
2381
2382
        // If the callable has arguments, pass them.
2383 1
        if ($arguments) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arguments 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...
2384 1
            $array = \array_map($callable, $this->array, $arguments);
2385
        } else {
2386 1
            $array = \array_map($callable, $this->array);
2387
        }
2388
2389 1
        return static::create($array, $this->iteratorClass, false);
2390
    }
2391
2392
    /**
2393
     * Check whether array is associative or not.
2394
     *
2395
     * @param bool $recursive
2396
     *
2397
     * @return bool
2398
     *              <p>Returns true if associative, false otherwise.</p>
2399
     */
2400 15
    public function isAssoc(bool $recursive = false): bool
2401
    {
2402 15
        if ($this->isEmpty()) {
2403 3
            return false;
2404
        }
2405
2406 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2407 13
            if (!\is_string($key)) {
2408 13
                return false;
2409
            }
2410
        }
2411
2412 3
        return true;
2413
    }
2414
2415
    /**
2416
     * Check whether the array is empty or not.
2417
     *
2418
     * @return bool
2419
     *              <p>Returns true if empty, false otherwise.</p>
2420
     */
2421 92
    public function isEmpty(): bool
2422
    {
2423 92
        return !$this->array;
2424
    }
2425
2426
    /**
2427
     * Check if the current array is equal to the given "$array" or not.
2428
     *
2429
     * @param array $array
2430
     *
2431
     * @return bool
2432
     */
2433
    public function isEqual(array $array): bool
2434
    {
2435
        return $this->array === $array;
2436
    }
2437
2438
    /**
2439
     * Check if the current array is a multi-array.
2440
     *
2441
     * @return bool
2442
     */
2443 14
    public function isMultiArray(): bool
2444
    {
2445
        return !(
2446 14
            \count($this->array, \COUNT_NORMAL)
2447
            ===
2448 14
            \count($this->array, \COUNT_RECURSIVE)
2449
        );
2450
    }
2451
2452
    /**
2453
     * Check whether array is numeric or not.
2454
     *
2455
     * @return bool
2456
     *              <p>Returns true if numeric, false otherwise.</p>
2457
     */
2458 5
    public function isNumeric(): bool
2459
    {
2460 5
        if ($this->isEmpty()) {
2461 2
            return false;
2462
        }
2463
2464 4
        foreach ($this->keys()->getGenerator() as $key) {
2465 4
            if (!\is_int($key)) {
2466 4
                return false;
2467
            }
2468
        }
2469
2470 2
        return true;
2471
    }
2472
2473
    /**
2474
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2475
     *
2476
     * @param bool $recursive
2477
     *
2478
     * @return bool
2479
     */
2480 1
    public function isSequential(bool $recursive = false): bool
2481
    {
2482
2483
        // recursive
2484
2485 1
        if ($recursive === true) {
2486
            return $this->array_keys_recursive($this->array)
2487
                   ===
2488
                   \range(0, \count($this->array, \COUNT_RECURSIVE) - 1);
2489
        }
2490
2491
        // non recursive
2492
2493 1
        return \array_keys($this->array)
2494
               ===
2495 1
               \range(0, \count($this->array, \COUNT_NORMAL) - 1);
2496
    }
2497
2498
    /**
2499
     * @return array
2500
     */
2501
    public function jsonSerialize(): array
2502
    {
2503
        return $this->getArray();
2504
    }
2505
2506
    /**
2507
     * Get all keys from the current array.
2508
     *
2509
     * @param bool  $recursive    [optional] <p>
2510
     *                            Get all keys, also from all sub-arrays from an multi-dimensional array.
2511
     *                            </p>
2512
     * @param mixed $search_value [optional] <p>
2513
     *                            If specified, then only keys containing these values are returned.
2514
     *                            </p>
2515
     * @param bool  $strict       [optional] <p>
2516
     *                            Determines if strict comparison (===) should be used during the search.
2517
     *                            </p>
2518
     *
2519
     * @return static
2520
     *                  <p>(Immutable) An array of all the keys in input.</p>
2521
     */
2522 26
    public function keys(bool $recursive = false, $search_value = null, bool $strict = true)
2523
    {
2524
2525
        // recursive
2526
2527 26
        if ($recursive === true) {
2528 3
            if ($search_value === null) {
2529 3
                $array = $this->array_keys_recursive($this->array);
2530
            } else {
2531
                $array = $this->array_keys_recursive($this->array, $search_value, $strict);
2532
            }
2533
2534 3
            return static::create($array, $this->iteratorClass, false);
2535
        }
2536
2537
        // non recursive
2538
2539 25
        if ($search_value === null) {
2540 25
            $array = \array_keys($this->array);
2541
        } else {
2542
            $array = \array_keys($this->array, $search_value, $strict);
2543
        }
2544
2545 25
        return static::create($array, $this->iteratorClass, false);
2546
    }
2547
2548
    /**
2549
     * Sort an array by key in reverse order.
2550
     *
2551
     * @param int $sort_flags [optional] <p>
2552
     *                        You may modify the behavior of the sort using the optional
2553
     *                        parameter sort_flags, for details
2554
     *                        see sort.
2555
     *                        </p>
2556
     *
2557
     * @return static
2558
     *                 <p>(Mutable) Return this Arrayy object.</p>
2559
     */
2560 4
    public function krsort(int $sort_flags = 0)
2561
    {
2562 4
        \krsort($this->array, $sort_flags);
2563
2564 4
        return $this;
2565
    }
2566
2567
    /**
2568
     * Get the last value from the current array.
2569
     *
2570
     * @return mixed
2571
     *                <p>Return null if there wasn't a element.</p>
2572
     */
2573 4
    public function last()
2574
    {
2575 4
        return $this->pop();
2576
    }
2577
2578
    /**
2579
     * Get the last value(s) from the current array.
2580
     *
2581
     * @param int|null $number
2582
     *
2583
     * @return static
2584
     *                <p>(Immutable)</p>
2585
     */
2586 13
    public function lastsImmutable(int $number = null)
2587
    {
2588 13
        if ($this->isEmpty()) {
2589 1
            return static::create([], $this->iteratorClass, false);
2590
        }
2591
2592 12
        if ($number === null) {
2593 8
            $poppedValue = $this->pop();
2594
2595 8
            if ($poppedValue === null) {
2596 1
                $poppedValue = [$poppedValue];
2597
            } else {
2598 7
                $poppedValue = (array) $poppedValue;
2599
            }
2600
2601 8
            $arrayy = static::create($poppedValue, $this->iteratorClass, false);
2602
        } else {
2603 4
            $number = (int) $number;
2604 4
            $arrayy = $this->rest(-$number);
2605
        }
2606
2607 12
        return $arrayy;
2608
    }
2609
2610
    /**
2611
     * Get the last value(s) from the current array.
2612
     *
2613
     * @param int|null $number
2614
     *
2615
     * @return static
2616
     *                 <p>(Mutable)</p>
2617
     */
2618 13
    public function lastsMutable(int $number = null)
2619
    {
2620 13
        if ($this->isEmpty()) {
2621 1
            return $this;
2622
        }
2623
2624 12
        if ($number === null) {
2625 8
            $poppedValue = $this->pop();
2626
2627 8
            if ($poppedValue === null) {
2628 1
                $poppedValue = [$poppedValue];
2629
            } else {
2630 7
                $poppedValue = (array) $poppedValue;
2631
            }
2632
2633 8
            $this->array = static::create($poppedValue, $this->iteratorClass, false)->array;
2634
        } else {
2635 4
            $number = (int) $number;
2636 4
            $this->array = $this->rest(-$number)->array;
2637
        }
2638
2639 12
        return $this;
2640
    }
2641
2642
    /**
2643
     * Count the values from the current array.
2644
     *
2645
     * alias: for "Arrayy->count()"
2646
     *
2647
     * @see Arrayy::count()
2648
     *
2649
     * @param int $mode
2650
     *
2651
     * @return int
2652
     */
2653 20
    public function length(int $mode = \COUNT_NORMAL): int
2654
    {
2655 20
        return $this->count($mode);
2656
    }
2657
2658
    /**
2659
     * Apply the given function to the every element of the array,
2660
     * collecting the results.
2661
     *
2662
     * @param \callable $callable
2663
     *
2664
     * @return static
2665
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2666
     */
2667 4
    public function map($callable)
2668
    {
2669 4
        return static::create(
2670 4
            \array_map($callable, $this->array),
2671 4
            $this->iteratorClass,
2672 4
            false
2673
        );
2674
    }
2675
2676
    /**
2677
     * Check if all items in current array match a truth test.
2678
     *
2679
     * @param \Closure $closure
2680
     *
2681
     * @return bool
2682
     */
2683 15 View Code Duplication
    public function matches(\Closure $closure): bool
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...
2684
    {
2685 15
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2686 2
            return false;
2687
        }
2688
2689 13
        foreach ($this->getGenerator() as $key => $value) {
2690 13
            $value = $closure($value, $key);
2691
2692 13
            if ($value === false) {
2693 13
                return false;
2694
            }
2695
        }
2696
2697 7
        return true;
2698
    }
2699
2700
    /**
2701
     * Check if any item in the current array matches a truth test.
2702
     *
2703
     * @param \Closure $closure
2704
     *
2705
     * @return bool
2706
     */
2707 14 View Code Duplication
    public function matchesAny(\Closure $closure): bool
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...
2708
    {
2709 14
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2710 2
            return false;
2711
        }
2712
2713 12
        foreach ($this->getGenerator() as $key => $value) {
2714 12
            $value = $closure($value, $key);
2715
2716 12
            if ($value === true) {
2717 12
                return true;
2718
            }
2719
        }
2720
2721 4
        return false;
2722
    }
2723
2724
    /**
2725
     * Get the max value from an array.
2726
     *
2727
     * @return mixed
2728
     */
2729 10 View Code Duplication
    public function max()
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...
2730
    {
2731 10
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2732 1
            return false;
2733
        }
2734
2735 9
        return \max($this->array);
2736
    }
2737
2738
    /**
2739
     * Merge the new $array into the current array.
2740
     *
2741
     * - keep key,value from the current array, also if the index is in the new $array
2742
     *
2743
     * @param array $array
2744
     * @param bool  $recursive
2745
     *
2746
     * @return static
2747
     *                <p>(Immutable)</p>
2748
     */
2749 25 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false)
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...
2750
    {
2751 25
        if ($recursive === true) {
2752 4
            $result = \array_replace_recursive($this->array, $array);
2753
        } else {
2754 21
            $result = \array_replace($this->array, $array);
2755
        }
2756
2757 25
        return static::create($result, $this->iteratorClass, false);
2758
    }
2759
2760
    /**
2761
     * Merge the new $array into the current array.
2762
     *
2763
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2764
     * - create new indexes
2765
     *
2766
     * @param array $array
2767
     * @param bool  $recursive
2768
     *
2769
     * @return static
2770
     *                <p>(Immutable)</p>
2771
     */
2772 16 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false)
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...
2773
    {
2774 16
        if ($recursive === true) {
2775 4
            $result = \array_merge_recursive($this->array, $array);
2776
        } else {
2777 12
            $result = \array_merge($this->array, $array);
2778
        }
2779
2780 16
        return static::create($result, $this->iteratorClass, false);
2781
    }
2782
2783
    /**
2784
     * Merge the the current array into the $array.
2785
     *
2786
     * - use key,value from the new $array, also if the index is in the current array
2787
     *
2788
     * @param array $array
2789
     * @param bool  $recursive
2790
     *
2791
     * @return static
2792
     *                <p>(Immutable)</p>
2793
     */
2794 16 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false)
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...
2795
    {
2796 16
        if ($recursive === true) {
2797 4
            $result = \array_replace_recursive($array, $this->array);
2798
        } else {
2799 12
            $result = \array_replace($array, $this->array);
2800
        }
2801
2802 16
        return static::create($result, $this->iteratorClass, false);
2803
    }
2804
2805
    /**
2806
     * Merge the current array into the new $array.
2807
     *
2808
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2809
     * - create new indexes
2810
     *
2811
     * @param array $array
2812
     * @param bool  $recursive
2813
     *
2814
     * @return static
2815
     *                <p>(Immutable)</p>
2816
     */
2817 17 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false)
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...
2818
    {
2819 17
        if ($recursive === true) {
2820 4
            $result = \array_merge_recursive($array, $this->array);
2821
        } else {
2822 13
            $result = \array_merge($array, $this->array);
2823
        }
2824
2825 17
        return static::create($result, $this->iteratorClass, false);
2826
    }
2827
2828
    /**
2829
     * @return ArrayyMeta|static
2830
     */
2831 8
    public static function meta()
2832
    {
2833 8
        return (new ArrayyMeta())->getMetaObject(static::class);
2834
    }
2835
2836
    /**
2837
     * Get the min value from an array.
2838
     *
2839
     * @return mixed
2840
     */
2841 10 View Code Duplication
    public function min()
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...
2842
    {
2843 10
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2844 1
            return false;
2845
        }
2846
2847 9
        return \min($this->array);
2848
    }
2849
2850
    /**
2851
     * Move an array element to a new index.
2852
     *
2853
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2854
     *
2855
     * @param int|string $from
2856
     * @param int|string $to
2857
     *
2858
     * @return static
2859
     *                 <p>(Immutable)</p>
2860
     */
2861 1
    public function moveElement($from, $to)
2862
    {
2863 1
        $array = $this->array;
2864
2865 1
        if (\is_int($from)) {
2866 1
            $tmp = \array_splice($array, $from, 1);
2867 1
            \array_splice($array, $to, 0, $tmp);
2868 1
            $output = $array;
2869 1
        } elseif (\is_string($from)) {
2870 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2871 1
            $itemToMove = $array[$from];
2872 1
            \array_splice($array, $indexToMove, 1);
2873 1
            $i = 0;
2874 1
            $output = [];
2875 1
            foreach ($array as $key => $item) {
2876 1
                if ($i === $to) {
2877 1
                    $output[$from] = $itemToMove;
2878
                }
2879 1
                $output[$key] = $item;
2880 1
                $i++;
2881
            }
2882
        } else {
2883
            $output = [];
2884
        }
2885
2886 1
        return static::create($output, $this->iteratorClass, false);
2887
    }
2888
2889
    /**
2890
     * Convert a object into an array.
2891
     *
2892
     * @param object $object
2893
     *
2894
     * @return mixed
2895
     */
2896 5
    protected static function objectToArray($object)
2897
    {
2898 5
        if (!\is_object($object)) {
2899 4
            return $object;
2900
        }
2901
2902 5
        if (\is_object($object)) {
2903 5
            $object = \get_object_vars($object);
2904
        }
2905
2906 5
        return \array_map(['self', 'objectToArray'], $object);
2907
    }
2908
2909
    /**
2910
     * Get a subset of the items from the given array.
2911
     *
2912
     * @param mixed[] $keys
2913
     *
2914
     * @return static
2915
     *                <p>(Immutable)</p>
2916
     */
2917
    public function only(array $keys)
2918
    {
2919
        $array = $this->array;
2920
2921
        return static::create(
2922
            \array_intersect_key($array, \array_flip($keys)),
2923
            $this->iteratorClass,
2924
            false
2925
        );
2926
    }
2927
2928
    /**
2929
     * Pad array to the specified size with a given value.
2930
     *
2931
     * @param int   $size  <p>Size of the result array.</p>
2932
     * @param mixed $value <p>Empty value by default.</p>
2933
     *
2934
     * @return static
2935
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
2936
     */
2937 4
    public function pad(int $size, $value)
2938
    {
2939 4
        return static::create(
2940 4
            \array_pad($this->array, $size, $value),
2941 4
            $this->iteratorClass,
2942 4
            false
2943
        );
2944
    }
2945
2946
    /**
2947
     * Pop a specified value off the end of the current array.
2948
     *
2949
     * @return mixed
2950
     *               <p>(Mutable) The popped element from the current array.</p>
2951
     */
2952 16
    public function pop()
2953
    {
2954 16
        return \array_pop($this->array);
2955
    }
2956
2957
    /**
2958
     * Prepend a (key) + value to the current array.
2959
     *
2960
     * @param mixed $value
2961
     * @param mixed $key
2962
     *
2963
     * @return static
2964
     *                 <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
2965
     */
2966 8
    public function prepend($value, $key = null)
2967
    {
2968 8
        if ($key === null) {
2969 8
            \array_unshift($this->array, $value);
2970
        } else {
2971
            /** @noinspection AdditionOperationOnArraysInspection */
2972 1
            $this->array = [$key => $value] + $this->array;
2973
        }
2974
2975 8
        return $this;
2976
    }
2977
2978
    /**
2979
     * Add a suffix to each key.
2980
     *
2981
     * @param mixed $suffix
2982
     *
2983
     * @return static
2984
     *                 <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
2985
     */
2986 10 View Code Duplication
    public function prependToEachKey($suffix)
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...
2987
    {
2988
        // init
2989 10
        $result = [];
2990
2991 10
        foreach ($this->getGenerator() as $key => $item) {
2992 9
            if ($item instanceof self) {
2993
                $result[$key] = $item->prependToEachKey($suffix);
2994 9
            } elseif (\is_array($item)) {
2995
                $result[$key] = self::create($item, $this->iteratorClass, false)->prependToEachKey($suffix)->toArray();
2996
            } else {
2997 9
                $result[$key . $suffix] = $item;
2998
            }
2999
        }
3000
3001 10
        return self::create($result, $this->iteratorClass, false);
3002
    }
3003
3004
    /**
3005
     * Add a suffix to each value.
3006
     *
3007
     * @param mixed $suffix
3008
     *
3009
     * @return static
3010
     *                 <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3011
     */
3012 10 View Code Duplication
    public function prependToEachValue($suffix)
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...
3013
    {
3014
        // init
3015 10
        $result = [];
3016
3017 10
        foreach ($this->getGenerator() as $key => $item) {
3018 9
            if ($item instanceof self) {
3019
                $result[$key] = $item->prependToEachValue($suffix);
3020 9
            } elseif (\is_array($item)) {
3021
                $result[$key] = self::create($item, $this->iteratorClass, false)
3022
                                    ->prependToEachValue($suffix)
3023
                                    ->toArray();
3024 9
            } elseif (\is_object($item)) {
3025 1
                $result[$key] = $item;
3026
            } else {
3027 9
                $result[$key] = $item . $suffix;
3028
            }
3029
        }
3030
3031 10
        return self::create($result, $this->iteratorClass, false);
3032
    }
3033
3034
    /**
3035
     * Push one or more values onto the end of array at once.
3036
     *
3037
     * @return static
3038
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3039
     */
3040 4 View Code Duplication
    public function push(/* variadic arguments allowed */)
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...
3041
    {
3042 4
        if (\func_num_args()) {
3043 4
            $args = \array_merge([&$this->array], \func_get_args());
3044 4
            \array_push(...$args);
3045
        }
3046
3047 4
        return $this;
3048
    }
3049
3050
    /**
3051
     * Get a random value from the current array.
3052
     *
3053
     * @param int|null $number <p>How many values you will take?</p>
3054
     *
3055
     * @return static
3056
     *                <p>(Immutable)</p>
3057
     */
3058 18
    public function randomImmutable(int $number = null)
3059
    {
3060 18 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3061 1
            return static::create([], $this->iteratorClass, false);
3062
        }
3063
3064 17 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3065
            /** @noinspection NonSecureArrayRandUsageInspection */
3066 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3067
3068 13
            return static::create($arrayRandValue, $this->iteratorClass, false);
3069
        }
3070
3071 5
        $arrayTmp = $this->array;
3072
        /** @noinspection NonSecureShuffleUsageInspection */
3073 5
        \shuffle($arrayTmp);
3074
3075 5
        return static::create($arrayTmp, $this->iteratorClass, false)->firstsImmutable($number);
3076
    }
3077
3078
    /**
3079
     * Pick a random key/index from the keys of this array.
3080
     *
3081
     * @throws \RangeException If array is empty
3082
     *
3083
     * @return mixed
3084
     *                <p>Get a key/index or null if there wasn't a key/index.</p>
3085
     */
3086 4
    public function randomKey()
3087
    {
3088 4
        $result = $this->randomKeys(1);
3089
3090 4
        if (!isset($result[0])) {
3091
            $result[0] = null;
3092
        }
3093
3094 4
        return $result[0];
3095
    }
3096
3097
    /**
3098
     * Pick a given number of random keys/indexes out of this array.
3099
     *
3100
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3101
     *
3102
     * @throws \RangeException If array is empty
3103
     *
3104
     * @return static
3105
     *                 <p>(Immutable)</p>
3106
     */
3107 13
    public function randomKeys(int $number)
3108
    {
3109 13
        $count = \count($this->array, \COUNT_NORMAL);
3110
3111 13
        if ($number === 0 || $number > $count) {
3112 2
            throw new \RangeException(
3113 2
                \sprintf(
3114 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3115 2
                    $number,
3116 2
                    $count
3117
                )
3118
            );
3119
        }
3120
3121 11
        $result = (array) \array_rand($this->array, $number);
3122
3123 11
        return static::create($result, $this->iteratorClass, false);
3124
    }
3125
3126
    /**
3127
     * Get a random value from the current array.
3128
     *
3129
     * @param int|null $number <p>How many values you will take?</p>
3130
     *
3131
     * @return static
3132
     *                 <p>(Mutable)</p>
3133
     */
3134 17
    public function randomMutable(int $number = null)
3135
    {
3136 17 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3137
            return static::create([], $this->iteratorClass, false);
3138
        }
3139
3140 17 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3141
            /** @noinspection NonSecureArrayRandUsageInspection */
3142 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3143 7
            $this->array = $arrayRandValue;
3144
3145 7
            return $this;
3146
        }
3147
3148
        /** @noinspection NonSecureShuffleUsageInspection */
3149 11
        \shuffle($this->array);
3150
3151 11
        return $this->firstsMutable($number);
3152
    }
3153
3154
    /**
3155
     * Pick a random value from the values of this array.
3156
     *
3157
     * @return mixed
3158
     *                <p>Get a random value or null if there wasn't a value.</p>
3159
     */
3160 4
    public function randomValue()
3161
    {
3162 4
        $result = $this->randomImmutable();
3163
3164 4
        if (!isset($result[0])) {
3165
            $result[0] = null;
3166
        }
3167
3168 4
        return $result[0];
3169
    }
3170
3171
    /**
3172
     * Pick a given number of random values out of this array.
3173
     *
3174
     * @param int $number
3175
     *
3176
     * @return static
3177
     *                 <p>(Mutable)</p>
3178
     */
3179 7
    public function randomValues(int $number)
3180
    {
3181 7
        return $this->randomMutable($number);
3182
    }
3183
3184
    /**
3185
     * Get a random value from an array, with the ability to skew the results.
3186
     *
3187
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3188
     *
3189
     * @param array    $array
3190
     * @param int|null $number <p>How many values you will take?</p>
3191
     *
3192
     * @return static
3193
     *                 <p>(Immutable)</p>
3194
     */
3195 9
    public function randomWeighted(array $array, int $number = null)
3196
    {
3197
        // init
3198 9
        $options = [];
3199
3200 9
        foreach ($array as $option => $weight) {
3201 9
            if ($this->searchIndex($option) !== false) {
3202 9
                for ($i = 0; $i < $weight; ++$i) {
3203 1
                    $options[] = $option;
3204
                }
3205
            }
3206
        }
3207
3208 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3209
    }
3210
3211
    /**
3212
     * Reduce the current array via callable e.g. anonymous-function.
3213
     *
3214
     * @param \callable $callable
3215
     * @param array     $init
3216
     *
3217
     * @return static
3218
     *                 <p>(Immutable)</p>
3219
     */
3220 4
    public function reduce($callable, array $init = [])
3221
    {
3222 4
        $result = \array_reduce($this->array, $callable, $init);
3223
3224 4
        if ($result === null) {
3225
            $this->array = [];
3226
        } else {
3227 4
            $this->array = (array) $result;
3228
        }
3229
3230 4
        return static::create($this->array, $this->iteratorClass, false);
3231
    }
3232
3233
    /**
3234
     * Create a numerically re-indexed Arrayy object.
3235
     *
3236
     * @return static
3237
     *                 <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3238
     */
3239 9
    public function reindex()
3240
    {
3241 9
        $this->array = \array_values($this->array);
3242
3243 9
        return $this;
3244
    }
3245
3246
    /**
3247
     * Return all items that fail the truth test.
3248
     *
3249
     * @param \Closure $closure
3250
     *
3251
     * @return static
3252
     *                <p>(Immutable)</p>
3253
     */
3254 1 View Code Duplication
    public function reject(\Closure $closure)
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...
3255
    {
3256 1
        $filtered = [];
3257
3258 1
        foreach ($this->getGenerator() as $key => $value) {
3259 1
            if (!$closure($value, $key)) {
3260 1
                $filtered[$key] = $value;
3261
            }
3262
        }
3263
3264 1
        return static::create($filtered, $this->iteratorClass, false);
3265
    }
3266
3267
    /**
3268
     * Remove a value from the current array (optional using dot-notation).
3269
     *
3270
     * @param mixed $key
3271
     *
3272
     * @return static
3273
     *                <p>(Immutable)</p>
3274
     */
3275 18
    public function remove($key)
3276
    {
3277
        // recursive call
3278 18
        if (\is_array($key)) {
3279
            foreach ($key as $k) {
3280
                $this->internalRemove($k);
3281
            }
3282
3283
            return static::create($this->array, $this->iteratorClass, false);
3284
        }
3285
3286 18
        $this->internalRemove($key);
3287
3288 18
        return static::create($this->array, $this->iteratorClass, false);
3289
    }
3290
3291
    /**
3292
     * Remove the first value from the current array.
3293
     *
3294
     * @return static
3295
     *                <p>(Immutable)</p>
3296
     */
3297 7
    public function removeFirst()
3298
    {
3299 7
        $tmpArray = $this->array;
3300 7
        \array_shift($tmpArray);
3301
3302 7
        return static::create($tmpArray, $this->iteratorClass, false);
3303
    }
3304
3305
    /**
3306
     * Remove the last value from the current array.
3307
     *
3308
     * @return static
3309
     *                <p>(Immutable)</p>
3310
     */
3311 7
    public function removeLast()
3312
    {
3313 7
        $tmpArray = $this->array;
3314 7
        \array_pop($tmpArray);
3315
3316 7
        return static::create($tmpArray, $this->iteratorClass, false);
3317
    }
3318
3319
    /**
3320
     * Removes a particular value from an array (numeric or associative).
3321
     *
3322
     * @param mixed $value
3323
     *
3324
     * @return static
3325
     *                <p>(Immutable)</p>
3326
     */
3327 7
    public function removeValue($value)
3328
    {
3329 7
        $isNumericArray = true;
3330 7
        foreach ($this->getGenerator() as $key => $item) {
3331 6
            if ($item === $value) {
3332 6
                if (!\is_int($key)) {
3333
                    $isNumericArray = false;
3334
                }
3335 6
                unset($this->array[$key]);
3336
            }
3337
        }
3338
3339 7
        if ($isNumericArray) {
3340 7
            $this->array = \array_values($this->array);
3341
        }
3342
3343 7
        return static::create($this->array, $this->iteratorClass, false);
3344
    }
3345
3346
    /**
3347
     * Generate array of repeated arrays.
3348
     *
3349
     * @param int $times <p>How many times has to be repeated.</p>
3350
     *
3351
     * @return static
3352
     *                <p>(Immutable)</p>
3353
     */
3354 1
    public function repeat($times): self
3355
    {
3356 1
        if ($times === 0) {
3357 1
            return new static();
3358
        }
3359
3360 1
        return static::create(
3361 1
            \array_fill(0, (int) $times, $this->array),
3362 1
            $this->iteratorClass,
3363 1
            false
3364
        );
3365
    }
3366
3367
    /**
3368
     * Replace a key with a new key/value pair.
3369
     *
3370
     * @param mixed $replace
3371
     * @param mixed $key
3372
     * @param mixed $value
3373
     *
3374
     * @return static
3375
     *                 <p>(Immutable)</p>
3376
     */
3377 2
    public function replace($replace, $key, $value)
3378
    {
3379 2
        $that = $this->remove($replace);
3380
3381 2
        return $that->set($key, $value);
3382
    }
3383
3384
    /**
3385
     * Create an array using the current array as values and the other array as keys.
3386
     *
3387
     * @param array $keys <p>An array of keys.</p>
3388
     *
3389
     * @return static
3390
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3391
     */
3392 2
    public function replaceAllKeys(array $keys)
3393
    {
3394 2
        return static::create(
3395 2
            \array_combine($keys, $this->array),
3396 2
            $this->iteratorClass,
3397 2
            false
3398
        );
3399
    }
3400
3401
    /**
3402
     * Create an array using the current array as keys and the other array as values.
3403
     *
3404
     * @param array $array <p>An array o values.</p>
3405
     *
3406
     * @return static
3407
     *                 <p>(Immutable) Arrayy object with values from the other array.</p>
3408
     */
3409 2
    public function replaceAllValues(array $array)
3410
    {
3411 2
        return static::create(
3412 2
            \array_combine($this->array, $array),
3413 2
            $this->iteratorClass,
3414 2
            false
3415
        );
3416
    }
3417
3418
    /**
3419
     * Replace the keys in an array with another set.
3420
     *
3421
     * @param array $keys <p>An array of keys matching the array's size</p>
3422
     *
3423
     * @return static
3424
     *                <p>(Immutable)</p>
3425
     */
3426 1
    public function replaceKeys(array $keys)
3427
    {
3428 1
        $values = \array_values($this->array);
3429 1
        $result = \array_combine($keys, $values);
3430
3431 1
        return static::create($result, $this->iteratorClass, false);
3432
    }
3433
3434
    /**
3435
     * Replace the first matched value in an array.
3436
     *
3437
     * @param mixed $search      <p>The value to replace.</p>
3438
     * @param mixed $replacement <p>The value to replace.</p>
3439
     *
3440
     * @return static
3441
     *                 <p>(Immutable)</p>
3442
     */
3443 3
    public function replaceOneValue($search, $replacement = '')
3444
    {
3445 3
        $array = $this->array;
3446 3
        $key = \array_search($search, $array, true);
3447
3448 3
        if ($key !== false) {
3449 3
            $array[$key] = $replacement;
3450
        }
3451
3452 3
        return static::create($array, $this->iteratorClass, false);
3453
    }
3454
3455
    /**
3456
     * Replace values in the current array.
3457
     *
3458
     * @param mixed $search      <p>The value to replace.</p>
3459
     * @param mixed $replacement <p>What to replace it with.</p>
3460
     *
3461
     * @return static
3462
     *                 <p>(Immutable)</p>
3463
     */
3464 1
    public function replaceValues($search, $replacement = '')
3465
    {
3466 1
        $array = $this->each(
3467 1
            function ($value) use ($search, $replacement) {
3468 1
                return \str_replace($search, $replacement, $value);
3469 1
            }
3470
        );
3471
3472 1
        return $array;
3473
    }
3474
3475
    /**
3476
     * Get the last elements from index $from until the end of this array.
3477
     *
3478
     * @param int $from
3479
     *
3480
     * @return static
3481
     *                 <p>(Immutable)</p>
3482
     */
3483 15
    public function rest(int $from = 1)
3484
    {
3485 15
        $tmpArray = $this->array;
3486
3487 15
        return static::create(
3488 15
            \array_splice($tmpArray, $from),
3489 15
            $this->iteratorClass,
3490 15
            false
3491
        );
3492
    }
3493
3494
    /**
3495
     * Return the array in the reverse order.
3496
     *
3497
     * @return static
3498
     *                 <p>(Mutable) Return this Arrayy object.</p>
3499
     */
3500 8
    public function reverse()
3501
    {
3502 8
        $this->array = \array_reverse($this->array);
3503
3504 8
        return $this;
3505
    }
3506
3507
    /**
3508
     * Sort an array in reverse order.
3509
     *
3510
     * @param int $sort_flags [optional] <p>
3511
     *                        You may modify the behavior of the sort using the optional
3512
     *                        parameter sort_flags, for details
3513
     *                        see sort.
3514
     *                        </p>
3515
     *
3516
     * @return static
3517
     *                 <p>(Mutable) Return this Arrayy object.</p>
3518
     */
3519 4
    public function rsort(int $sort_flags = 0)
3520
    {
3521 4
        \rsort($this->array, $sort_flags);
3522
3523 4
        return $this;
3524
    }
3525
3526
    /**
3527
     * Search for the first index of the current array via $value.
3528
     *
3529
     * @param mixed $value
3530
     *
3531
     * @return float|int|string
3532
     */
3533 20
    public function searchIndex($value)
3534
    {
3535 20
        return \array_search($value, $this->array, true);
3536
    }
3537
3538
    /**
3539
     * Search for the value of the current array via $index.
3540
     *
3541
     * @param mixed $index
3542
     *
3543
     * @return static
3544
     *                 <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3545
     */
3546 9
    public function searchValue($index)
3547
    {
3548
        // init
3549 9
        $return = [];
3550
3551 9
        if ($this->isEmpty()) {
3552
            return static::create([], $this->iteratorClass, false);
3553
        }
3554
3555
        // php cast "bool"-index into "int"-index
3556 9
        if ((bool) $index === $index) {
3557 1
            $index = (int) $index;
3558
        }
3559
3560 9
        if (\array_key_exists($index, $this->array) === true) {
3561 7
            $return = [$this->array[$index]];
3562
        }
3563
3564 9
        return static::create($return, $this->iteratorClass, false);
3565
    }
3566
3567
    /**
3568
     * Set a value for the current array (optional using dot-notation).
3569
     *
3570
     * @param string $key   <p>The key to set.</p>
3571
     * @param mixed  $value <p>Its value.</p>
3572
     *
3573
     * @return static
3574
     *                <p>(Mutable)</p>
3575
     */
3576 17
    public function set($key, $value)
3577
    {
3578 17
        $this->internalSet($key, $value);
3579
3580 17
        return $this;
3581
    }
3582
3583
    /**
3584
     * Get a value from a array and set it if it was not.
3585
     *
3586
     * WARNING: this method only set the value, if the $key is not already set
3587
     *
3588
     * @param mixed $key      <p>The key</p>
3589
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3590
     *
3591
     * @return mixed
3592
     *                <p>(Mutable)</p>
3593
     */
3594 11
    public function setAndGet($key, $fallback = null)
3595
    {
3596
        // If the key doesn't exist, set it.
3597 11
        if (!$this->has($key)) {
3598 4
            $this->array = $this->set($key, $fallback)->getArray();
3599
        }
3600
3601 11
        return $this->get($key);
3602
    }
3603
3604
    /**
3605
     * Shifts a specified value off the beginning of array.
3606
     *
3607
     * @return mixed
3608
     *               <p>(Mutable) A shifted element from the current array.</p>
3609
     */
3610 4
    public function shift()
3611
    {
3612 4
        return \array_shift($this->array);
3613
    }
3614
3615
    /**
3616
     * Shuffle the current array.
3617
     *
3618
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3619
     * @param array $array  [optional]
3620
     *
3621
     * @return static
3622
     *                <p>(Immutable)</p>
3623
     */
3624 1
    public function shuffle(bool $secure = false, array $array = null)
3625
    {
3626 1
        if ($array === null) {
3627 1
            $array = $this->array;
3628
        }
3629
3630 1
        if ($secure !== true) {
3631
            /** @noinspection NonSecureShuffleUsageInspection */
3632 1
            \shuffle($array);
3633
        } else {
3634 1
            $size = \count($array, \COUNT_NORMAL);
3635 1
            $keys = \array_keys($array);
3636 1
            for ($i = $size - 1; $i > 0; --$i) {
3637
                try {
3638 1
                    $r = \random_int(0, $i);
3639
                } catch (\Exception $e) {
3640
                    $r = \mt_rand();
3641
                }
3642 1
                if ($r !== $i) {
3643 1
                    $temp = $array[$keys[$r]];
3644 1
                    $array[$keys[$r]] = $array[$keys[$i]];
3645 1
                    $array[$keys[$i]] = $temp;
3646
                }
3647
            }
3648
3649
            // reset indices
3650 1
            $array = \array_values($array);
3651
        }
3652
3653 1
        foreach ($array as $key => $value) {
3654
            // check if recursive is needed
3655 1
            if (\is_array($value) === true) {
3656 1
                $array[$key] = $this->shuffle($secure, $value);
3657
            }
3658
        }
3659
3660 1
        return static::create($array, $this->iteratorClass, false);
3661
    }
3662
3663
    /**
3664
     * Count the values from the current array.
3665
     *
3666
     * alias: for "Arrayy->count()"
3667
     *
3668
     * @param int $mode
3669
     *
3670
     * @return int
3671
     */
3672 20
    public function size(int $mode = \COUNT_NORMAL): int
3673
    {
3674 20
        return $this->count($mode);
3675
    }
3676
3677
    /**
3678
     * Counts all elements in an array, or something in an object.
3679
     *
3680
     * <p>
3681
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
3682
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
3683
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
3684
     * implemented and used in PHP.
3685
     * </p>
3686
     *
3687
     * @return int
3688
     *              <p>
3689
     *              The number of elements in var, which is
3690
     *              typically an array, since anything else will have one
3691
     *              element.
3692
     *              </p>
3693
     *              <p>
3694
     *              If var is not an array or an object with
3695
     *              implemented Countable interface,
3696
     *              1 will be returned.
3697
     *              There is one exception, if var is &null;,
3698
     *              0 will be returned.
3699
     *              </p>
3700
     *              <p>
3701
     *              Caution: count may return 0 for a variable that isn't set,
3702
     *              but it may also return 0 for a variable that has been initialized with an
3703
     *              empty array. Use isset to test if a variable is set.
3704
     *              </p>
3705
     */
3706 10
    public function sizeRecursive(): int
3707
    {
3708 10
        return \count($this->array, \COUNT_RECURSIVE);
3709
    }
3710
3711
    /**
3712
     * Extract a slice of the array.
3713
     *
3714
     * @param int      $offset       <p>Slice begin index.</p>
3715
     * @param int|null $length       <p>Length of the slice.</p>
3716
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
3717
     *
3718
     * @return static
3719
     *                 <p>A slice of the original array with length $length.</p>
3720
     */
3721 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
3722
    {
3723 4
        return static::create(
3724 4
            \array_slice($this->array, $offset, $length, $preserveKeys),
3725 4
            $this->iteratorClass,
3726 4
            false
3727
        );
3728
    }
3729
3730
    /**
3731
     * Sort the current array and optional you can keep the keys.
3732
     *
3733
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3734
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
3735
     *                              <strong>SORT_NATURAL</strong></p>
3736
     * @param bool       $keepKeys
3737
     *
3738
     * @return static
3739
     *                 <p>(Mutable) Return this Arrayy object.</p>
3740
     */
3741 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false)
3742
    {
3743 20
        return $this->sorting($this->array, $direction, $strategy, $keepKeys);
3744
    }
3745
3746
    /**
3747
     * Sort the current array by key.
3748
     *
3749
     * @see http://php.net/manual/en/function.ksort.php
3750
     * @see http://php.net/manual/en/function.krsort.php
3751
     *
3752
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3753
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3754
     *                              <strong>SORT_NATURAL</strong></p>
3755
     *
3756
     * @return static
3757
     *                 <p>(Mutable) Return this Arrayy object.</p>
3758
     */
3759 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3760
    {
3761
        /** @noinspection UnusedFunctionResultInspection */
3762 18
        $this->sorterKeys($this->array, $direction, $strategy);
3763
3764 18
        return $this;
3765
    }
3766
3767
    /**
3768
     * Sort the current array by value.
3769
     *
3770
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3771
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3772
     *                              <strong>SORT_NATURAL</strong></p>
3773
     *
3774
     * @return static
3775
     *                <p>(Mutable)</p>
3776
     */
3777 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3778
    {
3779 1
        return $this->sort($direction, $strategy, true);
3780
    }
3781
3782
    /**
3783
     * Sort the current array by value.
3784
     *
3785
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3786
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3787
     *                              <strong>SORT_NATURAL</strong></p>
3788
     *
3789
     * @return static
3790
     *                <p>(Mutable)</p>
3791
     */
3792 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3793
    {
3794 1
        return $this->sort($direction, $strategy, false);
3795
    }
3796
3797
    /**
3798
     * Sort a array by value, by a closure or by a property.
3799
     *
3800
     * - If the sorter is null, the array is sorted naturally.
3801
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
3802
     *
3803
     * @param \callable|null $sorter
3804
     * @param int|string     $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3805
     * @param int            $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3806
     *                                  <strong>SORT_NATURAL</strong></p>
3807
     *
3808
     * @return static
3809
     *                <p>(Immutable)</p>
3810
     */
3811 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3812
    {
3813 1
        $array = (array) $this->array;
3814 1
        $direction = $this->getDirection($direction);
3815
3816
        // Transform all values into their results.
3817 1
        if ($sorter) {
3818 1
            $arrayy = static::create($array, $this->iteratorClass, false);
3819
3820 1
            $that = $this;
3821 1
            $results = $arrayy->each(
3822 1
                function ($value) use ($sorter, $that) {
3823 1
                    return \is_callable($sorter) ? $sorter($value) : $that->get($sorter, null, $value);
3824 1
                }
3825
            );
3826
3827 1
            $results = $results->getArray();
3828
        } else {
3829 1
            $results = $array;
3830
        }
3831
3832
        // Sort by the results and replace by original values
3833 1
        \array_multisort($results, $direction, $strategy, $array);
3834
3835 1
        return static::create($array, $this->iteratorClass, false);
3836
    }
3837
3838
    /**
3839
     * sorting keys
3840
     *
3841
     * @param array      $elements
3842
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3843
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3844
     *                              <strong>SORT_NATURAL</strong></p>
3845
     *
3846
     * @return static
3847
     *                <p>(Mutable) Return this Arrayy object.</p>
3848
     */
3849 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3850
    {
3851 18
        $direction = $this->getDirection($direction);
3852
3853
        switch ($direction) {
3854 18
            case 'desc':
3855 18
            case \SORT_DESC:
3856 6
                \krsort($elements, $strategy);
3857
3858 6
                break;
3859 13
            case 'asc':
3860 13
            case \SORT_ASC:
3861
            default:
3862 13
                \ksort($elements, $strategy);
3863
        }
3864
3865 18
        return $this;
3866
    }
3867
3868
    /**
3869
     * @param array       $elements <p>Warning: used as reference</p>
3870
     * @param int|string  $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3871
     * @param int         $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3872
     *                               <strong>SORT_NATURAL</strong></p>
3873
     * @param bool        $keepKeys
3874
     *
3875
     * @return static
3876
     *                <p>(Mutable) Return this Arrayy object.</p>
3877
     */
3878 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false)
3879
    {
3880 20
        $direction = $this->getDirection($direction);
3881
3882 20
        if (!$strategy) {
3883 20
            $strategy = \SORT_REGULAR;
3884
        }
3885
3886
        switch ($direction) {
3887 20
            case 'desc':
3888 20
            case \SORT_DESC:
3889 9
                if ($keepKeys) {
3890 5
                    \arsort($elements, $strategy);
3891
                } else {
3892 4
                    \rsort($elements, $strategy);
3893
                }
3894
3895 9
                break;
3896 11
            case 'asc':
3897 11
            case \SORT_ASC:
3898
            default:
3899 11
                if ($keepKeys) {
3900 4
                    \asort($elements, $strategy);
3901
                } else {
3902 7
                    \sort($elements, $strategy);
3903
                }
3904
        }
3905
3906 20
        return $this;
3907
    }
3908
3909
    /**
3910
     * Split an array in the given amount of pieces.
3911
     *
3912
     * @param int  $numberOfPieces
3913
     * @param bool $keepKeys
3914
     *
3915
     * @return static
3916
     *                <p>(Immutable)</p>
3917
     */
3918 1 View Code Duplication
    public function split(int $numberOfPieces = 2, bool $keepKeys = false)
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...
3919
    {
3920 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
3921
3922 1
        if ($arrayCount === 0) {
3923 1
            $result = [];
3924
        } else {
3925 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
3926 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
3927
        }
3928
3929 1
        return static::create($result, $this->iteratorClass, false);
3930
    }
3931
3932
    /**
3933
     * Stripe all empty items.
3934
     *
3935
     * @return static
3936
     *                 <p>(Immutable)</p>
3937
     */
3938 1
    public function stripEmpty()
3939
    {
3940 1
        return $this->filter(
3941 1
            function ($item) {
3942 1
                if ($item === null) {
3943 1
                    return false;
3944
                }
3945
3946 1
                return (bool) \trim((string) $item);
3947 1
            }
3948
        );
3949
    }
3950
3951
    /**
3952
     * Swap two values between positions by key.
3953
     *
3954
     * @param int|string $swapA <p>a key in the array</p>
3955
     * @param int|string $swapB <p>a key in the array</p>
3956
     *
3957
     * @return static
3958
     *                <p>(Immutable)</p>
3959
     */
3960 1
    public function swap($swapA, $swapB)
3961
    {
3962 1
        $array = $this->array;
3963
3964 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
3965
3966 1
        return static::create($array, $this->iteratorClass, false);
3967
    }
3968
3969
    /**
3970
     * alias: for "Arrayy->getArray()"
3971
     *
3972
     * @see Arrayy::getArray()
3973
     */
3974 192
    public function toArray()
3975
    {
3976 192
        return $this->getArray();
3977
    }
3978
3979
    /**
3980
     * Convert the current array to JSON.
3981
     *
3982
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
3983
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
3984
     *
3985
     * @return string
3986
     */
3987 6
    public function toJson(int $options = 0, int $depth = 512): string
3988
    {
3989
        /** @noinspection PhpComposerExtensionStubsInspection */
3990 6
        return \json_encode($this->array, $options, $depth);
3991
    }
3992
3993
    /**
3994
     * Implodes array to a string with specified separator.
3995
     *
3996
     * @param string $separator [optional] <p>The element's separator.</p>
3997
     *
3998
     * @return string
3999
     *                <p>The string representation of array, separated by ",".</p>
4000
     */
4001 19
    public function toString(string $separator = ','): string
4002
    {
4003 19
        return $this->implode($separator);
4004
    }
4005
4006
    /**
4007
     * Return a duplicate free copy of the current array.
4008
     *
4009
     * @return static
4010
     *                <p>(Mutable)</p>
4011
     */
4012 10
    public function unique()
4013
    {
4014
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4015
4016 10
        $this->array = \array_reduce(
0 ignored issues
show
Documentation Bug introduced by
It seems like \array_reduce($this->arr...esultArray; }, array()) of type * is incompatible with the declared type array of property $array.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
4017 10
            $this->array,
4018 10
            function ($resultArray, $value) {
4019 9
                if (!\in_array($value, $resultArray, true)) {
4020 9
                    $resultArray[] = $value;
4021
                }
4022
4023 9
                return $resultArray;
4024 10
            },
4025 10
            []
4026
        );
4027
4028 10 View Code Duplication
        if ($this->array === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
4029
            $this->array = [];
4030
        } else {
4031 10
            $this->array = (array) $this->array;
4032
        }
4033
4034 10
        return $this;
4035
    }
4036
4037
    /**
4038
     * Return a duplicate free copy of the current array. (with the old keys)
4039
     *
4040
     * @return static
4041
     *                 <p>(Mutable)</p>
4042
     */
4043 11
    public function uniqueKeepIndex()
4044
    {
4045
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4046
4047
        // init
4048 11
        $array = $this->array;
4049
4050 11
        $this->array = \array_reduce(
0 ignored issues
show
Documentation Bug introduced by
It seems like \array_reduce(\array_key...esultArray; }, array()) of type * is incompatible with the declared type array of property $array.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
4051 11
            \array_keys($array),
4052 11
            function ($resultArray, $key) use ($array) {
4053 10
                if (!\in_array($array[$key], $resultArray, true)) {
4054 10
                    $resultArray[$key] = $array[$key];
4055
                }
4056
4057 10
                return $resultArray;
4058 11
            },
4059 11
            []
4060
        );
4061
4062 11 View Code Duplication
        if ($this->array === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
4063
            $this->array = [];
4064
        } else {
4065 11
            $this->array = (array) $this->array;
4066
        }
4067
4068 11
        return $this;
4069
    }
4070
4071
    /**
4072
     * alias: for "Arrayy->unique()"
4073
     *
4074
     * @see Arrayy::unique()
4075
     *
4076
     * @return static
4077
     *                 <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4078
     */
4079 10
    public function uniqueNewIndex()
4080
    {
4081 10
        return $this->unique();
4082
    }
4083
4084
    /**
4085
     * Prepends one or more values to the beginning of array at once.
4086
     *
4087
     * @return static
4088
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4089
     */
4090 4 View Code Duplication
    public function unshift(/* variadic arguments allowed */)
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...
4091
    {
4092 4
        if (\func_num_args()) {
4093 4
            $args = \array_merge([&$this->array], \func_get_args());
4094 4
            \array_unshift(...$args);
4095
        }
4096
4097 4
        return $this;
4098
    }
4099
4100
    /**
4101
     * Get all values from a array.
4102
     *
4103
     * @return static
4104
     *                 <p>(Immutable)</p>
4105
     */
4106 2
    public function values()
4107
    {
4108 2
        return static::create(
4109 2
            \array_values((array) $this->array),
4110 2
            $this->iteratorClass,
4111 2
            false
4112
        );
4113
    }
4114
4115
    /**
4116
     * Apply the given function to every element in the array, discarding the results.
4117
     *
4118
     * @param \callable $callable
4119
     * @param bool      $recursive <p>Whether array will be walked recursively or no</p>
4120
     *
4121
     * @return static
4122
     *                 <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4123
     */
4124 37
    public function walk($callable, bool $recursive = false)
4125
    {
4126 37
        if ($recursive === true) {
4127 32
            \array_walk_recursive($this->array, $callable);
4128
        } else {
4129 18
            \array_walk($this->array, $callable);
4130
        }
4131
4132 37
        return $this;
4133
    }
4134
}
4135