Completed
Push — master ( 8507e2...2d694e )
by Lars
01:39
created

Arrayy::checkType()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5.0342

Importance

Changes 0
Metric Value
cc 5
nc 4
nop 2
dl 0
loc 16
ccs 8
cts 9
cp 0.8889
crap 5.0342
rs 9.4222
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
use Arrayy\TypeCheck\TypeCheckArray;
8
use Arrayy\TypeCheck\TypeCheckInterface;
9
use Arrayy\TypeCheck\TypeCheckPhpDoc;
10
11
/** @noinspection ClassReImplementsParentInterfaceInspection */
12
13
/**
14
 * Methods to manage arrays.
15
 *
16
 * For the full copyright and license information, please view the LICENSE
17
 * file that was distributed with this source code.
18
 */
19
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
20
{
21
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
22
23
    /**
24
     * @var array
25
     */
26
    protected $array = [];
27
28
    /**
29
     * @var ArrayyRewindableGenerator|null
30
     */
31
    protected $generator;
32
33
    /**
34
     * @var string
35
     */
36
    protected $iteratorClass = ArrayyIterator::class;
37
38
    /**
39
     * @var string
40
     */
41
    protected $pathSeparator = '.';
42
43
    /**
44
     * @var bool
45
     */
46
    protected $checkPropertyTypes = false;
47
48
    /**
49
     * @var bool
50
     */
51
    protected $checkForMissingPropertiesInConstructor = false;
52
53
    /**
54
     * @var bool
55
     */
56
    protected $checkPropertiesMismatchInConstructor = false;
57
58
    /**
59
     * @var bool
60
     */
61
    protected $checkPropertiesMismatch = true;
62
63
    /**
64
     * @var array|TypeCheckArray|TypeCheckInterface[]
65
     */
66
    protected $properties = [];
67
68
    /**
69
     * Initializes
70
     *
71
     * @param mixed  $data                         <p>
72
     *                                             Should be an array or a generator, otherwise it will try
73
     *                                             to convert it into an array.
74
     *                                             </p>
75
     * @param string $iteratorClass                optional <p>
76
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
77
     *                                             need this option.
78
     *                                             </p>
79
     * @param bool   $checkPropertiesInConstructor optional <p>
80
     *                                             You need to extend the "Arrayy"-class and you need to set
81
     *                                             the $checkPropertiesMismatchInConstructor class property
82
     *                                             to
83
     *                                             true, otherwise this option didn't not work anyway.
84
     *                                             </p>
85
     */
86 1050
    public function __construct(
87
        $data = [],
88
        string $iteratorClass = ArrayyIterator::class,
89
        bool $checkPropertiesInConstructor = true
90
    ) {
91 1050
        $data = $this->fallbackForArray($data);
92
93
        // used only for serialize + unserialize, all other methods are overwritten
94 1048
        parent::__construct([], 0, $iteratorClass);
95
96 1048
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
97
98 1041
        $this->setIteratorClass($iteratorClass);
99 1041
    }
100
101
    /**
102
     * Call object as function.
103
     *
104
     * @param mixed $key
105
     *
106
     * @return mixed
107
     */
108 1
    public function __invoke($key = null)
109
    {
110 1
        if ($key !== null) {
111 1
            $this->generatorToArray();
112
113 1
            return $this->array[$key] ?? false;
114
        }
115
116
        return $this->getArray();
117
    }
118
119
    /**
120
     * Whether or not an element exists by key.
121
     *
122
     * @param mixed $key
123
     *
124
     * @return bool
125
     *              <p>True is the key/index exists, otherwise false.</p>
126
     */
127
    public function __isset($key): bool
128
    {
129
        return $this->offsetExists($key);
130
    }
131
132
    /**
133
     * Assigns a value to the specified element.
134
     *
135
     * @param mixed $key
136
     * @param mixed $value
137
     */
138 2
    public function __set($key, $value)
139
    {
140 2
        $this->internalSet($key, $value);
141 2
    }
142
143
    /**
144
     * magic to string
145
     *
146
     * @return string
147
     */
148 16
    public function __toString(): string
149
    {
150 16
        return $this->toString();
151
    }
152
153
    /**
154
     * Unset element by key.
155
     *
156
     * @param mixed $key
157
     */
158
    public function __unset($key)
159
    {
160
        $this->internalRemove($key);
161
    }
162
163
    /**
164
     * Get a value by key.
165
     *
166
     * @param mixed $key
167
     *
168
     * @return mixed
169
     *               <p>Get a Value from the current array.</p>
170
     */
171 4
    public function &__get($key)
172
    {
173 4
        $return = $this->get($key);
174
175 4
        if (\is_array($return) === true) {
176
            return static::create($return, $this->iteratorClass, false);
177
        }
178
179 4
        return $return;
180
    }
181
182
    /**
183
     * alias: for "Arrayy->append()"
184
     *
185
     * @param mixed $value
186
     *
187
     * @return static
188
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
189
     *
190
     * @see Arrayy::append()
191
     */
192 3
    public function add($value): self
193
    {
194 3
        return $this->append($value);
195
    }
196
197
    /**
198
     * Append a (key) + value to the current array.
199
     *
200
     * @param mixed $value
201
     * @param mixed $key
202
     *
203
     * @return static
204
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
205
     */
206 13
    public function append($value, $key = null): self
207
    {
208 13
        $this->generatorToArray();
209
210 13
        if ($this->properties !== []) {
211 4
            $this->checkType($key, $value);
212
        }
213
214 12
        if ($key !== null) {
215
            if (
216
                isset($this->array[$key])
217
                &&
218
                \is_array($this->array[$key]) === true
219
            ) {
220
                $this->array[$key][] = $value;
221
            } else {
222
                $this->array[$key] = $value;
223
            }
224
        } else {
225 12
            $this->array[] = $value;
226
        }
227
228 12
        return $this;
229
    }
230
231
    /**
232
     * Sort the entries by value.
233
     *
234
     * @param int $sort_flags [optional] <p>
235
     *                        You may modify the behavior of the sort using the optional
236
     *                        parameter sort_flags, for details
237
     *                        see sort.
238
     *                        </p>
239
     *
240
     * @return static
241
     *                <p>(Mutable) Return this Arrayy object.</p>
242
     */
243 4
    public function asort(int $sort_flags = 0): self
244
    {
245 4
        $this->generatorToArray();
246
247 4
        \asort($this->array, $sort_flags);
248
249 4
        return $this;
250
    }
251
252
    /**
253
     * Counts all elements in an array, or something in an object.
254
     *
255
     * <p>
256
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
257
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
258
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
259
     * implemented and used in PHP.
260
     * </p>
261
     *
262
     * @see http://php.net/manual/en/function.count.php
263
     *
264
     * @param int $mode [optional] If the optional mode parameter is set to
265
     *                  COUNT_RECURSIVE (or 1), count
266
     *                  will recursively count the array. This is particularly useful for
267
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
268
     *
269
     * @return int
270
     *             <p>
271
     *             The number of elements in var, which is
272
     *             typically an array, since anything else will have one
273
     *             element.
274
     *             </p>
275
     *             <p>
276
     *             If var is not an array or an object with
277
     *             implemented Countable interface,
278
     *             1 will be returned.
279
     *             There is one exception, if var is &null;,
280
     *             0 will be returned.
281
     *             </p>
282
     *             <p>
283
     *             Caution: count may return 0 for a variable that isn't set,
284
     *             but it may also return 0 for a variable that has been initialized with an
285
     *             empty array. Use isset to test if a variable is set.
286
     *             </p>
287
     */
288 50
    public function count(int $mode = \COUNT_NORMAL): int
289
    {
290 50
        return \count($this->getArray(), $mode);
291
    }
292
293
    /**
294
     * Exchange the array for another one.
295
     *
296
     * @param array|static $data
297
     *
298
     * @return array
299
     */
300 1
    public function exchangeArray($data): array
301
    {
302 1
        $this->array = $this->fallbackForArray($data);
303
304 1
        return $this->array;
305
    }
306
307
    /**
308
     * Creates a copy of the ArrayyObject.
309
     *
310
     * @return array
311
     */
312 5
    public function getArrayCopy(): array
313
    {
314 5
        $this->generatorToArray();
315
316 5
        return $this->array;
317
    }
318
319
    /**
320
     * Returns a new iterator, thus implementing the \Iterator interface.
321
     *
322
     * @return \Iterator
323
     *                   <p>An iterator for the values in the array.</p>
324
     */
325 25
    public function getIterator(): \Iterator
326
    {
327 25
        if ($this->generator instanceof ArrayyRewindableGenerator) {
328 1
            return $this->generator;
329
        }
330
331 24
        $iterator = $this->getIteratorClass();
332
333 24
        if ($iterator === ArrayyIterator::class) {
334 24
            return new $iterator($this->getArray(), 0, static::class);
335
        }
336
337
        return new $iterator($this->getArray());
338
    }
339
340
    /**
341
     * Gets the iterator classname for the ArrayObject.
342
     *
343
     * @return string
344
     */
345 24
    public function getIteratorClass(): string
346
    {
347 24
        return $this->iteratorClass;
348
    }
349
350
    /**
351
     * Sort the entries by key
352
     *
353
     * @param int $sort_flags [optional] <p>
354
     *                        You may modify the behavior of the sort using the optional
355
     *                        parameter sort_flags, for details
356
     *                        see sort.
357
     *                        </p>
358
     *
359
     * @return static
360
     *                <p>(Mutable) Return this Arrayy object.</p>
361
     */
362 4
    public function ksort(int $sort_flags = 0): self
363
    {
364 4
        $this->generatorToArray();
365
366 4
        \ksort($this->array, $sort_flags);
367
368 4
        return $this;
369
    }
370
371
    /**
372
     * Sort an array using a case insensitive "natural order" algorithm
373
     *
374
     * @return static
375
     *                <p>(Mutable) Return this Arrayy object.</p>
376
     */
377
    public function natcasesort(): self
378
    {
379
        $this->generatorToArray();
380
381
        \natcasesort($this->array);
382
383
        return $this;
384
    }
385
386
    /**
387
     * Sort entries using a "natural order" algorithm
388
     *
389
     * @return static
390
     *                <p>(Mutable) Return this Arrayy object.</p>
391
     */
392 1
    public function natsort(): self
393
    {
394 1
        $this->generatorToArray();
395
396 1
        \natsort($this->array);
397
398 1
        return $this;
399
    }
400
401
    /**
402
     * Whether or not an offset exists.
403
     *
404
     * @param bool|int|string $offset
405
     *
406
     * @return bool
407
     */
408 125
    public function offsetExists($offset): bool
409
    {
410 125
        $this->generatorToArray();
411
412 125
        if ($this->array === []) {
413 5
            return false;
414
        }
415
416
        // php cast "bool"-index into "int"-index
417 120
        if ((bool) $offset === $offset) {
418 1
            $offset = (int) $offset;
419
        }
420
421 120
        $tmpReturn = $this->keyExists($offset);
422
423
        if (
424 120
            $tmpReturn === true
425
            ||
426
            (
427 87
                $tmpReturn === false
428
                &&
429 120
                \strpos((string) $offset, $this->pathSeparator) === false
430
            )
431
        ) {
432 118
            return $tmpReturn;
433
        }
434
435 3
        $offsetExists = false;
436
437
        if (
438 3
            $this->pathSeparator
439
            &&
440 3
            (string) $offset === $offset
441
            &&
442 3
            \strpos($offset, $this->pathSeparator) !== false
443
        ) {
444 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
445 3
            if ($explodedPath !== false) {
446 3
                $lastOffset = \array_pop($explodedPath);
447 3
                if ($lastOffset !== null) {
448 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
449
450 3
                    $this->callAtPath(
451 3
                        $containerPath,
452
                        static function ($container) use ($lastOffset, &$offsetExists) {
453 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
454 3
                        }
455
                    );
456
                }
457
            }
458
        }
459
460 3
        return $offsetExists;
461
    }
462
463
    /**
464
     * Returns the value at specified offset.
465
     *
466
     * @param int|string $offset
467
     *
468
     * @return mixed
469
     *               <p>Will return null if the offset did not exists.</p>
470
     */
471 96
    public function offsetGet($offset)
472
    {
473 96
        return $this->offsetExists($offset) ? $this->get($offset) : null;
474
    }
475
476
    /**
477
     * Assigns a value to the specified offset.
478
     *
479
     * @param int|string|null $offset
480
     * @param mixed           $value
481
     */
482 21
    public function offsetSet($offset, $value)
483
    {
484 21
        $this->generatorToArray();
485
486 21
        if ($offset === null) {
487 5
            if ($this->properties !== []) {
488 1
                $this->checkType(null, $value);
489
            }
490
491 4
            $this->array[] = $value;
492
        } else {
493 16
            $this->internalSet(
494 16
                $offset,
495 16
                $value,
496 16
                true
497
            );
498
        }
499 20
    }
500
501
    /**
502
     * Unset an offset.
503
     *
504
     * @param int|string $offset
505
     */
506 12
    public function offsetUnset($offset)
507
    {
508 12
        $this->generatorToArray();
509
510 12
        if ($this->array === []) {
511 3
            return;
512
        }
513
514 10
        if ($this->keyExists($offset)) {
515 7
            unset($this->array[$offset]);
516
517 7
            return;
518
        }
519
520
        if (
521 5
            $this->pathSeparator
522
            &&
523 5
            (string) $offset === $offset
524
            &&
525 5
            \strpos($offset, $this->pathSeparator) !== false
526
        ) {
527 2
            $path = \explode($this->pathSeparator, (string) $offset);
528
529 2
            if ($path !== false) {
530 2
                $pathToUnset = \array_pop($path);
531
532 2
                $this->callAtPath(
533 2
                    \implode($this->pathSeparator, $path),
534
                    static function (&$offset) use ($pathToUnset) {
535 2
                        unset($offset[$pathToUnset]);
536 2
                    }
537
                );
538
            }
539
        }
540
541 5
        unset($this->array[$offset]);
542 5
    }
543
544
    /**
545
     * Serialize the current "Arrayy"-object.
546
     *
547
     * @return string
548
     */
549 2
    public function serialize(): string
550
    {
551 2
        $this->generatorToArray();
552
553 2
        if (\PHP_VERSION_ID < 70400) {
554 2
            return parent::serialize();
555
        }
556
557
        return \serialize($this);
558
    }
559
560
    /**
561
     * Sets the iterator classname for the current "Arrayy"-object.
562
     *
563
     * @param string $class
564
     *
565
     * @throws \InvalidArgumentException
566
     */
567 1041
    public function setIteratorClass($class)
568
    {
569 1041
        if (\class_exists($class)) {
570 1041
            $this->iteratorClass = $class;
571
572 1041
            return;
573
        }
574
575
        if (\strpos($class, '\\') === 0) {
576
            $class = '\\' . $class;
577
            if (\class_exists($class)) {
578
                $this->iteratorClass = $class;
579
580
                return;
581
            }
582
        }
583
584
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
585
    }
586
587
    /**
588
     * Sort the entries with a user-defined comparison function and maintain key association.
589
     *
590
     * @param callable $function
591
     *
592
     * @throws \InvalidArgumentException
593
     *
594
     * @return static
595
     *                <p>(Mutable) Return this Arrayy object.</p>
596
     */
597 View Code Duplication
    public function uasort($function): self
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...
598
    {
599
        if (!\is_callable($function)) {
600
            throw new \InvalidArgumentException('Passed function must be callable');
601
        }
602
603
        $this->generatorToArray();
604
605
        \uasort($this->array, $function);
606
607
        return $this;
608
    }
609
610
    /**
611
     * Sort the entries by keys using a user-defined comparison function.
612
     *
613
     * @param callable $function
614
     *
615
     * @throws \InvalidArgumentException
616
     *
617
     * @return static
618
     *                <p>(Mutable) Return this Arrayy object.</p>
619
     */
620 5
    public function uksort($function): self
621
    {
622 5
        return $this->customSortKeys($function);
623
    }
624
625
    /**
626
     * Unserialize an string and return the instance of the "Arrayy"-class.
627
     *
628
     * @param string $string
629
     *
630
     * @return static
631
     */
632 2
    public function unserialize($string): self
633
    {
634 2
        if (\PHP_VERSION_ID < 70400) {
635 2
            parent::unserialize($string);
636
637 2
            return $this;
638
        }
639
640
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
641
    }
642
643
    /**
644
     * Append a (key) + values to the current array.
645
     *
646
     * @param array $values
647
     * @param mixed $key
648
     *
649
     * @return static
650
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
651
     */
652 1
    public function appendArrayValues(array $values, $key = null): self
653
    {
654 1
        $this->generatorToArray();
655
656 1
        if ($key !== null) {
657
            if (
658 1
                isset($this->array[$key])
659
                &&
660 1
                \is_array($this->array[$key]) === true
661
            ) {
662 1
                foreach ($values as $value) {
663 1
                    $this->array[$key][] = $value;
664
                }
665
            } else {
666 1
                foreach ($values as $value) {
667
                    $this->array[$key] = $value;
668
                }
669
            }
670
        } else {
671
            foreach ($values as $value) {
672
                $this->array[] = $value;
673
            }
674
        }
675
676 1
        return $this;
677
    }
678
679
    /**
680
     * Add a suffix to each key.
681
     *
682
     * @param mixed $prefix
683
     *
684
     * @return static
685
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
686
     */
687 10 View Code Duplication
    public function appendToEachKey($prefix): self
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...
688
    {
689
        // init
690 10
        $result = [];
691
692 10
        foreach ($this->getGenerator() as $key => $item) {
693 9
            if ($item instanceof self) {
694
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
695 9
            } elseif (\is_array($item) === true) {
696
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
697
                    ->appendToEachKey($prefix)
698
                    ->toArray();
699
            } else {
700 9
                $result[$prefix . $key] = $item;
701
            }
702
        }
703
704 10
        return self::create($result, $this->iteratorClass, false);
705
    }
706
707
    /**
708
     * Add a prefix to each value.
709
     *
710
     * @param mixed $prefix
711
     *
712
     * @return static
713
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
714
     */
715 10 View Code Duplication
    public function appendToEachValue($prefix): self
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...
716
    {
717
        // init
718 10
        $result = [];
719
720 10
        foreach ($this->getGenerator() as $key => $item) {
721 9
            if ($item instanceof self) {
722
                $result[$key] = $item->appendToEachValue($prefix);
723 9
            } elseif (\is_array($item) === true) {
724
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
725 9
            } elseif (\is_object($item) === true) {
726 1
                $result[$key] = $item;
727
            } else {
728 8
                $result[$key] = $prefix . $item;
729
            }
730
        }
731
732 10
        return self::create($result, $this->iteratorClass, false);
733
    }
734
735
    /**
736
     * Sort an array in reverse order and maintain index association.
737
     *
738
     * @return static
739
     *                <p>(Mutable) Return this Arrayy object.</p>
740
     */
741 10
    public function arsort(): self
742
    {
743 10
        $this->generatorToArray();
744
745 10
        \arsort($this->array);
746
747 10
        return $this;
748
    }
749
750
    /**
751
     * Iterate over the current array and execute a callback for each loop.
752
     *
753
     * @param \Closure $closure
754
     *
755
     * @return static
756
     *                <p>(Immutable)</p>
757
     */
758 2
    public function at(\Closure $closure): self
759
    {
760 2
        $arrayy = clone $this;
761
762 2
        foreach ($arrayy->getGenerator() as $key => $value) {
763 2
            $closure($value, $key);
764
        }
765
766 2
        return static::create(
767 2
            $arrayy->toArray(),
768 2
            $this->iteratorClass,
769 2
            false
770
        );
771
    }
772
773
    /**
774
     * Returns the average value of the current array.
775
     *
776
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
777
     *
778
     * @return float|int
779
     *                   <p>The average value.</p>
780
     */
781 10
    public function average($decimals = 0)
782
    {
783 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
784
785 10
        if (!$count) {
786 2
            return 0;
787
        }
788
789 8
        if ((int) $decimals !== $decimals) {
790 3
            $decimals = 0;
791
        }
792
793 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
794
    }
795
796
    /**
797
     * Changes all keys in an array.
798
     *
799
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
800
     *                  or <strong>CASE_LOWER</strong> (default)</p>
801
     *
802
     * @return static
803
     *                <p>(Immutable)</p>
804
     */
805 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
806
    {
807
        if (
808 1
            $case !== \CASE_LOWER
809
            &&
810 1
            $case !== \CASE_UPPER
811
        ) {
812
            $case = \CASE_LOWER;
813
        }
814
815 1
        $return = [];
816 1
        foreach ($this->getGenerator() as $key => $value) {
817 1
            if ($case === \CASE_LOWER) {
818 1
                $key = \mb_strtolower((string) $key);
819
            } else {
820 1
                $key = \mb_strtoupper((string) $key);
821
            }
822
823 1
            $return[$key] = $value;
824
        }
825
826 1
        return static::create(
827 1
            $return,
828 1
            $this->iteratorClass,
829 1
            false
830
        );
831
    }
832
833
    /**
834
     * Change the path separator of the array wrapper.
835
     *
836
     * By default, the separator is: "."
837
     *
838
     * @param string $separator <p>Separator to set.</p>
839
     *
840
     * @return static
841
     *                <p>Mutable</p>
842
     */
843 10
    public function changeSeparator($separator): self
844
    {
845 10
        $this->pathSeparator = $separator;
846
847 10
        return $this;
848
    }
849
850
    /**
851
     * Create a chunked version of the current array.
852
     *
853
     * @param int  $size         <p>Size of each chunk.</p>
854
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
855
     *
856
     * @return static
857
     *                <p>(Immutable) A new array of chunks from the original array.</p>
858
     */
859 5
    public function chunk($size, $preserveKeys = false): self
860
    {
861 5
        return static::create(
862 5
            \array_chunk($this->getArray(), $size, $preserveKeys),
863 5
            $this->iteratorClass,
864 5
            false
865
        );
866
    }
867
868
    /**
869
     * Clean all falsy values from the current array.
870
     *
871
     * @return static
872
     *                <p>(Immutable)</p>
873
     */
874 8
    public function clean(): self
875
    {
876 8
        return $this->filter(
877
            static function ($value) {
878 7
                return (bool) $value;
879 8
            }
880
        );
881
    }
882
883
    /**
884
     * WARNING!!! -> Clear the current array.
885
     *
886
     * @return static
887
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
888
     */
889 4
    public function clear(): self
890
    {
891 4
        $this->array = [];
892 4
        $this->generator = null;
893
894 4
        return $this;
895
    }
896
897
    /**
898
     * Check if an item is in the current array.
899
     *
900
     * @param float|int|string $value
901
     * @param bool             $recursive
902
     * @param bool             $strict
903
     *
904
     * @return bool
905
     */
906 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
907
    {
908 23
        if ($recursive === true) {
909 18
            return $this->in_array_recursive($value, $this->getArray(), $strict);
910
        }
911
912 14
        foreach ($this->getGenerator() as $valueFromArray) {
913 11
            if ($strict) {
914 11
                if ($value === $valueFromArray) {
915 11
                    return true;
916
                }
917
            } else {
918
                /** @noinspection NestedPositiveIfStatementsInspection */
919
                if ($value == $valueFromArray) {
920
                    return true;
921
                }
922
            }
923
        }
924
925 7
        return false;
926
    }
927
928
    /**
929
     * Check if an (case-insensitive) string is in the current array.
930
     *
931
     * @param string $value
932
     * @param bool   $recursive
933
     *
934
     * @return bool
935
     */
936 26
    public function containsCaseInsensitive($value, $recursive = false): bool
937
    {
938 26
        if ($recursive === true) {
939 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
940 22
                if (\is_array($valueTmp) === true) {
941 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
942 5
                    if ($return === true) {
943 5
                        return $return;
944
                    }
945 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
946 16
                    return true;
947
                }
948
            }
949
950 10
            return false;
951
        }
952
953 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
954 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
955 8
                return true;
956
            }
957
        }
958
959 5
        return false;
960
    }
961
962
    /**
963
     * Check if the given key/index exists in the array.
964
     *
965
     * @param int|string $key <p>key/index to search for</p>
966
     *
967
     * @return bool
968
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
969
     */
970 4
    public function containsKey($key): bool
971
    {
972 4
        return $this->offsetExists($key);
973
    }
974
975
    /**
976
     * Check if all given needles are present in the array as key/index.
977
     *
978
     * @param array $needles   <p>The keys you are searching for.</p>
979
     * @param bool  $recursive
980
     *
981
     * @return bool
982
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
983
     */
984 2
    public function containsKeys(array $needles, $recursive = false): bool
985
    {
986 2
        if ($recursive === true) {
987
            return
988 2
                \count(
989 2
                    \array_intersect(
990 2
                        $needles,
991 2
                        $this->keys(true)->getArray()
992
                    ),
993 2
                    \COUNT_RECURSIVE
994
                )
995
                ===
996 2
                \count(
997 2
                    $needles,
998 2
                    \COUNT_RECURSIVE
999
                );
1000
        }
1001
1002 1
        return \count(
1003 1
            \array_intersect($needles, $this->keys()->getArray()),
1004 1
            \COUNT_NORMAL
1005
        )
1006
               ===
1007 1
               \count(
1008 1
                   $needles,
1009 1
                   \COUNT_NORMAL
1010
               );
1011
    }
1012
1013
    /**
1014
     * Check if all given needles are present in the array as key/index.
1015
     *
1016
     * @param array $needles <p>The keys you are searching for.</p>
1017
     *
1018
     * @return bool
1019
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1020
     */
1021 1
    public function containsKeysRecursive(array $needles): bool
1022
    {
1023 1
        return $this->containsKeys($needles, true);
1024
    }
1025
1026
    /**
1027
     * alias: for "Arrayy->contains()"
1028
     *
1029
     * @param float|int|string $value
1030
     *
1031
     * @return bool
1032
     *
1033
     * @see Arrayy::contains()
1034
     */
1035 9
    public function containsValue($value): bool
1036
    {
1037 9
        return $this->contains($value);
1038
    }
1039
1040
    /**
1041
     * alias: for "Arrayy->contains($value, true)"
1042
     *
1043
     * @param float|int|string $value
1044
     *
1045
     * @return bool
1046
     *
1047
     * @see Arrayy::contains()
1048
     */
1049 18
    public function containsValueRecursive($value): bool
1050
    {
1051 18
        return $this->contains($value, true);
1052
    }
1053
1054
    /**
1055
     * Check if all given needles are present in the array.
1056
     *
1057
     * @param array $needles
1058
     *
1059
     * @return bool
1060
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1061
     */
1062 1
    public function containsValues(array $needles): bool
1063
    {
1064 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1065
               ===
1066 1
               \count($needles, \COUNT_NORMAL);
1067
    }
1068
1069
    /**
1070
     * Counts all the values of an array
1071
     *
1072
     * @see http://php.net/manual/en/function.array-count-values.php
1073
     *
1074
     * @return static
1075
     *                <p>
1076
     *                (Immutable)
1077
     *                An associative Arrayy-object of values from input as
1078
     *                keys and their count as value.
1079
     *                </p>
1080
     */
1081 7
    public function countValues(): self
1082
    {
1083 7
        return self::create(\array_count_values($this->getArray()), $this->iteratorClass);
1084
    }
1085
1086
    /**
1087
     * Creates an Arrayy object.
1088
     *
1089
     * @param mixed  $data
1090
     * @param string $iteratorClass
1091
     * @param bool   $checkPropertiesInConstructor
1092
     *
1093
     * @return static
1094
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1095
     */
1096 657
    public static function create(
1097
        $data = [],
1098
        string $iteratorClass = ArrayyIterator::class,
1099
        bool $checkPropertiesInConstructor = true
1100
    ): self {
1101 657
        return new static(
1102 657
            $data,
1103 657
            $iteratorClass,
1104 657
            $checkPropertiesInConstructor
1105
        );
1106
    }
1107
1108
    /**
1109
     * WARNING: Creates an Arrayy object by reference.
1110
     *
1111
     * @param array $array
1112
     *
1113
     * @return static
1114
     *                <p>(Mutable) Return this Arrayy object.</p>
1115
     */
1116 1
    public function createByReference(array &$array = []): self
1117
    {
1118 1
        $array = $this->fallbackForArray($array);
1119
1120 1
        $this->array = &$array;
1121
1122 1
        return $this;
1123
    }
1124
1125
    /**
1126
     * Create an new instance from a callable function which will return an Generator.
1127
     *
1128
     * @param callable():Generator $generatorFunction
0 ignored issues
show
Documentation introduced by
The doc-type callable():Generator could not be parsed: Expected "|" or "end of type", but got "(" at position 8. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1129
     *
1130
     * @return static
1131
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1132
     */
1133 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1134
    {
1135 5
        return self::create($generatorFunction);
1136
    }
1137
1138
    /**
1139
     * Create an new instance filled with a copy of values from a "Generator"-object.
1140
     *
1141
     * @param \Generator $generator
1142
     *
1143
     * @return static
1144
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1145
     */
1146 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1147
    {
1148 4
        return self::create(\iterator_to_array($generator, true));
1149
    }
1150
1151
    /**
1152
     * Create an new Arrayy object via JSON.
1153
     *
1154
     * @param string $json
1155
     *
1156
     * @return static
1157
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1158
     */
1159 5
    public static function createFromJson(string $json): self
1160
    {
1161 5
        return static::create(\json_decode($json, true));
1162
    }
1163
1164
    /**
1165
     * Create an new instance filled with values from an object that is iterable.
1166
     *
1167
     * @param \Traversable $object <p>iterable object</p>
1168
     *
1169
     * @return static
1170
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1171
     */
1172 4
    public static function createFromObject(\Traversable $object): self
1173
    {
1174
        // init
1175 4
        $array = self::create();
1176
1177 4
        if ($object instanceof self) {
1178 4
            $objectArray = $object->getGenerator();
1179
        } else {
1180
            $objectArray = $object;
1181
        }
1182
1183 4
        foreach ($objectArray as $key => $value) {
1184 3
            $array[$key] = $value;
1185
        }
1186
1187 4
        return $array;
1188
    }
1189
1190
    /**
1191
     * Create an new instance filled with values from an object.
1192
     *
1193
     * @param object $object
1194
     *
1195
     * @return static
1196
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1197
     */
1198 5
    public static function createFromObjectVars($object): self
1199
    {
1200 5
        return self::create(self::objectToArray($object));
1201
    }
1202
1203
    /**
1204
     * Create an new Arrayy object via string.
1205
     *
1206
     * @param string      $str       <p>The input string.</p>
1207
     * @param string|null $delimiter <p>The boundary string.</p>
1208
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1209
     *                               used.</p>
1210
     *
1211
     * @return static
1212
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1213
     */
1214 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1215
    {
1216 10
        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...
1217 1
            \preg_match_all($regEx, $str, $array);
1218
1219 1
            if (!empty($array)) {
1220 1
                $array = $array[0];
1221
            }
1222
        } else {
1223
            /** @noinspection NestedPositiveIfStatementsInspection */
1224 9
            if ($delimiter !== null) {
1225 7
                $array = \explode($delimiter, $str);
1226
            } else {
1227 2
                $array = [$str];
1228
            }
1229
        }
1230
1231
        // trim all string in the array
1232 10
        \array_walk(
1233 10
            $array,
1234
            static function (&$val) {
1235 10
                if ((string) $val === $val) {
1236 10
                    $val = \trim($val);
1237
                }
1238 10
            }
1239
        );
1240
1241 10
        return static::create($array);
1242
    }
1243
1244
    /**
1245
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1246
     *
1247
     * @param \Traversable $traversable
1248
     *
1249
     * @return static
1250
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1251
     */
1252 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1253
    {
1254 1
        return self::create(\iterator_to_array($traversable, true));
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 2
    public static function createWithRange($low, $high, int $step = 1): self
1268
    {
1269 2
        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): self
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) === false) {
1287
            throw new \InvalidArgumentException('Passed function must be callable');
1288
        }
1289
1290 5
        $this->generatorToArray();
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 6 View Code Duplication
    public function customSortValues($function): self
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 6
        if (\is_callable($function) === false) {
1312
            throw new \InvalidArgumentException('Passed function must be callable');
1313
        }
1314
1315 6
        $this->generatorToArray();
1316
1317 6
        \usort($this->array, $function);
1318
1319 6
        return $this;
1320
    }
1321
1322
    /**
1323
     * Delete the given key or keys.
1324
     *
1325
     * @param int|int[]|string|string[] $keyOrKeys
1326
     */
1327 4
    public function delete($keyOrKeys)
1328
    {
1329 4
        $keyOrKeys = (array) $keyOrKeys;
1330
1331 4
        foreach ($keyOrKeys as $key) {
1332 4
            $this->offsetUnset($key);
1333
        }
1334 4
    }
1335
1336
    /**
1337
     * Return values that are only in the current array.
1338
     *
1339
     * @param array ...$array
1340
     *
1341
     * @return static
1342
     *                <p>(Immutable)</p>
1343
     */
1344 13
    public function diff(...$array): self
1345
    {
1346 13
        return static::create(
1347 13
            \array_diff($this->getArray(), ...$array),
1348 13
            $this->iteratorClass,
1349 13
            false
1350
        );
1351
    }
1352
1353
    /**
1354
     * Return values that are only in the current array.
1355
     *
1356
     * @param array ...$array
1357
     *
1358
     * @return static
1359
     *                <p>(Immutable)</p>
1360
     */
1361 8
    public function diffKey(...$array): self
1362
    {
1363 8
        return static::create(
1364 8
            \array_diff_key($this->getArray(), ...$array),
1365 8
            $this->iteratorClass,
1366 8
            false
1367
        );
1368
    }
1369
1370
    /**
1371
     * Return values and Keys that are only in the current array.
1372
     *
1373
     * @param array $array
1374
     *
1375
     * @return static
1376
     *                <p>(Immutable)</p>
1377
     */
1378 8
    public function diffKeyAndValue(array $array = []): self
1379
    {
1380 8
        return static::create(
1381 8
            \array_diff_assoc($this->getArray(), $array),
1382 8
            $this->iteratorClass,
1383 8
            false
1384
        );
1385
    }
1386
1387
    /**
1388
     * Return values that are only in the current multi-dimensional array.
1389
     *
1390
     * @param array      $array
1391
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1392
     *
1393
     * @return static
1394
     *                <p>(Immutable)</p>
1395
     */
1396 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1397
    {
1398
        // init
1399 1
        $result = [];
1400
1401
        if (
1402 1
            $helperVariableForRecursion !== null
1403
            &&
1404 1
            \is_array($helperVariableForRecursion) === true
1405
        ) {
1406
            $arrayForTheLoop = $helperVariableForRecursion;
1407
        } else {
1408 1
            $arrayForTheLoop = $this->getGenerator();
1409
        }
1410
1411 1
        foreach ($arrayForTheLoop as $key => $value) {
1412 1
            if ($value instanceof self) {
1413
                $value = $value->getArray();
1414
            }
1415
1416 1
            if (\array_key_exists($key, $array)) {
1417 1
                if ($value !== $array[$key]) {
1418 1
                    $result[$key] = $value;
1419
                }
1420
            } else {
1421 1
                $result[$key] = $value;
1422
            }
1423
        }
1424
1425 1
        return static::create(
1426 1
            $result,
1427 1
            $this->iteratorClass,
1428 1
            false
1429
        );
1430
    }
1431
1432
    /**
1433
     * Return values that are only in the new $array.
1434
     *
1435
     * @param array $array
1436
     *
1437
     * @return static
1438
     *                <p>(Immutable)</p>
1439
     */
1440 8
    public function diffReverse(array $array = []): self
1441
    {
1442 8
        return static::create(
1443 8
            \array_diff($array, $this->getArray()),
1444 8
            $this->iteratorClass,
1445 8
            false
1446
        );
1447
    }
1448
1449
    /**
1450
     * Divide an array into two arrays. One with keys and the other with values.
1451
     *
1452
     * @return static
1453
     *                <p>(Immutable)</p>
1454
     */
1455 1
    public function divide(): self
1456
    {
1457 1
        return static::create(
1458
            [
1459 1
                $this->keys(),
1460 1
                $this->values(),
1461
            ],
1462 1
            $this->iteratorClass,
1463 1
            false
1464
        );
1465
    }
1466
1467
    /**
1468
     * Iterate over the current array and modify the array's value.
1469
     *
1470
     * @param \Closure $closure
1471
     *
1472
     * @return static
1473
     *                <p>(Immutable)</p>
1474
     */
1475 5 View Code Duplication
    public function each(\Closure $closure): self
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...
1476
    {
1477
        // init
1478 5
        $array = [];
1479
1480 5
        foreach ($this->getGenerator() as $key => $value) {
1481 5
            $array[$key] = $closure($value, $key);
1482
        }
1483
1484 5
        return static::create(
1485 5
            $array,
1486 5
            $this->iteratorClass,
1487 5
            false
1488
        );
1489
    }
1490
1491
    /**
1492
     * Check if a value is in the current array using a closure.
1493
     *
1494
     * @param \Closure $closure
1495
     *
1496
     * @return bool
1497
     *              <p>Returns true if the given value is found, false otherwise.</p>
1498
     */
1499 4
    public function exists(\Closure $closure): bool
1500
    {
1501
        // init
1502 4
        $isExists = false;
1503
1504 4
        foreach ($this->getGenerator() as $key => $value) {
1505 3
            if ($closure($value, $key)) {
1506 1
                $isExists = true;
1507
1508 1
                break;
1509
            }
1510
        }
1511
1512 4
        return $isExists;
1513
    }
1514
1515
    /**
1516
     * Fill the array until "$num" with "$default" values.
1517
     *
1518
     * @param int   $num
1519
     * @param mixed $default
1520
     *
1521
     * @return static
1522
     *                <p>(Immutable)</p>
1523
     */
1524 8
    public function fillWithDefaults(int $num, $default = null): self
1525
    {
1526 8
        if ($num < 0) {
1527 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1528
        }
1529
1530 7
        $this->generatorToArray();
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(
1542 7
            $tmpArray,
1543 7
            $this->iteratorClass,
1544 7
            false
1545
        );
1546
    }
1547
1548
    /**
1549
     * Find all items in an array that pass the truth test.
1550
     *
1551
     * @param \Closure|null $closure [optional] <p>
1552
     *                               The callback function to use
1553
     *                               </p>
1554
     *                               <p>
1555
     *                               If no callback is supplied, all entries of
1556
     *                               input equal to false (see
1557
     *                               converting to
1558
     *                               boolean) will be removed.
1559
     *                               </p>
1560
     *                               * @param int $flag [optional] <p>
1561
     *                               Flag determining what arguments are sent to <i>callback</i>:
1562
     *                               </p><ul>
1563
     *                               <li>
1564
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1565
     *                               to <i>callback</i> instead of the value</span>
1566
     *                               </li>
1567
     *                               <li>
1568
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1569
     *                               arguments to <i>callback</i> instead of the value</span>
1570
     *                               </li>
1571
     *                               </ul>
1572
     *
1573
     * @return static
1574
     *                <p>(Immutable)</p>
1575
     */
1576 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1577
    {
1578 12
        if (!$closure) {
1579 1
            return $this->clean();
1580
        }
1581
1582 12
        return static::create(
1583 12
            \array_filter($this->getArray(), $closure, $flag),
1584 12
            $this->iteratorClass,
1585 12
            false
1586
        );
1587
    }
1588
1589
    /**
1590
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1591
     * property within that.
1592
     *
1593
     * @param string          $property
1594
     * @param string|string[] $value
1595
     * @param string          $comparisonOp
1596
     *                                      <p>
1597
     *                                      'eq' (equals),<br />
1598
     *                                      'gt' (greater),<br />
1599
     *                                      'gte' || 'ge' (greater or equals),<br />
1600
     *                                      'lt' (less),<br />
1601
     *                                      'lte' || 'le' (less or equals),<br />
1602
     *                                      'ne' (not equals),<br />
1603
     *                                      'contains',<br />
1604
     *                                      'notContains',<br />
1605
     *                                      'newer' (via strtotime),<br />
1606
     *                                      'older' (via strtotime),<br />
1607
     *                                      </p>
1608
     *
1609
     * @return static
1610
     *                <p>(Immutable)</p>
1611
     */
1612 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1613
    {
1614 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...
1615 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
1616
        }
1617
1618
        $ops = [
1619
            'eq' => static function ($item, $prop, $value) {
1620 1
                return $item[$prop] === $value;
1621 1
            },
1622
            'gt' => static function ($item, $prop, $value) {
1623
                return $item[$prop] > $value;
1624 1
            },
1625
            'ge' => static function ($item, $prop, $value) {
1626
                return $item[$prop] >= $value;
1627 1
            },
1628
            'gte' => static function ($item, $prop, $value) {
1629
                return $item[$prop] >= $value;
1630 1
            },
1631
            'lt' => static function ($item, $prop, $value) {
1632 1
                return $item[$prop] < $value;
1633 1
            },
1634
            'le' => static function ($item, $prop, $value) {
1635
                return $item[$prop] <= $value;
1636 1
            },
1637
            'lte' => static function ($item, $prop, $value) {
1638
                return $item[$prop] <= $value;
1639 1
            },
1640
            'ne' => static function ($item, $prop, $value) {
1641
                return $item[$prop] !== $value;
1642 1
            },
1643
            'contains' => static function ($item, $prop, $value) {
1644 1
                return \in_array($item[$prop], (array) $value, true);
1645 1
            },
1646
            'notContains' => static function ($item, $prop, $value) {
1647
                return !\in_array($item[$prop], (array) $value, true);
1648 1
            },
1649
            'newer' => static function ($item, $prop, $value) {
1650
                return \strtotime($item[$prop]) > \strtotime($value);
1651 1
            },
1652
            'older' => static function ($item, $prop, $value) {
1653
                return \strtotime($item[$prop]) < \strtotime($value);
1654 1
            },
1655
        ];
1656
1657 1
        $result = \array_values(
1658 1
            \array_filter(
1659 1
                $this->getArray(),
1660
                static function ($item) use (
1661 1
                    $property,
1662 1
                    $value,
1663 1
                    $ops,
1664 1
                    $comparisonOp
1665
                ) {
1666 1
                    $item = (array) $item;
1667 1
                    $itemArrayy = static::create($item);
1668 1
                    $item[$property] = $itemArrayy->get($property, []);
1669
1670 1
                    return $ops[$comparisonOp]($item, $property, $value);
1671 1
                }
1672
            )
1673
        );
1674
1675 1
        return static::create(
1676 1
            $result,
1677 1
            $this->iteratorClass,
1678 1
            false
1679
        );
1680
    }
1681
1682
    /**
1683
     * Find the first item in an array that passes the truth test,
1684
     *  otherwise return false
1685
     *
1686
     * @param \Closure $closure
1687
     *
1688
     * @return false|mixed
1689
     *                     <p>Return false if we did not find the value.</p>
1690
     */
1691 8
    public function find(\Closure $closure)
1692
    {
1693 8
        foreach ($this->getGenerator() as $key => $value) {
1694 6
            if ($closure($value, $key)) {
1695 5
                return $value;
1696
            }
1697
        }
1698
1699 3
        return false;
1700
    }
1701
1702
    /**
1703
     * find by ...
1704
     *
1705
     * @param string          $property
1706
     * @param string|string[] $value
1707
     * @param string          $comparisonOp
1708
     *
1709
     * @return static
1710
     *                <p>(Immutable)</p>
1711
     */
1712 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1713
    {
1714 1
        return $this->filterBy($property, $value, $comparisonOp);
1715
    }
1716
1717
    /**
1718
     * Get the first value from the current array.
1719
     *
1720
     * @return mixed
1721
     *               <p>Return null if there wasn't a element.</p>
1722
     */
1723 21
    public function first()
1724
    {
1725 21
        $key_first = $this->firstKey();
1726 21
        if ($key_first === null) {
1727 3
            return null;
1728
        }
1729
1730 18
        return $this->get($key_first);
1731
    }
1732
1733
    /**
1734
     * Get the first key from the current array.
1735
     *
1736
     * @return mixed
1737
     *               <p>Return null if there wasn't a element.</p>
1738
     */
1739 28
    public function firstKey()
1740
    {
1741 28
        $this->generatorToArray();
1742
1743 28
        return \array_key_first($this->array);
1744
    }
1745
1746
    /**
1747
     * Get the first value(s) from the current array.
1748
     * And will return an empty array if there was no first entry.
1749
     *
1750
     * @param int|null $number <p>How many values you will take?</p>
1751
     *
1752
     * @return static
1753
     *                <p>(Immutable)</p>
1754
     */
1755 37 View Code Duplication
    public function firstsImmutable(int $number = null): self
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...
1756
    {
1757 37
        $arrayTmp = $this->getArray();
1758
1759 37
        if ($number === null) {
1760 14
            $array = (array) \array_shift($arrayTmp);
1761
        } else {
1762 23
            $number = (int) $number;
1763 23
            $array = \array_splice($arrayTmp, 0, $number);
1764
        }
1765
1766 37
        return static::create(
1767 37
            $array,
1768 37
            $this->iteratorClass,
1769 37
            false
1770
        );
1771
    }
1772
1773
    /**
1774
     * Get the first value(s) from the current array.
1775
     * And will return an empty array if there was no first entry.
1776
     *
1777
     * @param int|null $number <p>How many values you will take?</p>
1778
     *
1779
     * @return static
1780
     *                <p>(Immutable)</p>
1781
     */
1782 3 View Code Duplication
    public function firstsKeys(int $number = null): self
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...
1783
    {
1784 3
        $arrayTmp = $this->keys()->getArray();
1785
1786 3
        if ($number === null) {
1787
            $array = (array) \array_shift($arrayTmp);
1788
        } else {
1789 3
            $number = (int) $number;
1790 3
            $array = \array_splice($arrayTmp, 0, $number);
1791
        }
1792
1793 3
        return static::create(
1794 3
            $array,
1795 3
            $this->iteratorClass,
1796 3
            false
1797
        );
1798
    }
1799
1800
    /**
1801
     * Get and rmove the first value(s) from the current array.
1802
     * And will return an empty array if there was no first entry.
1803
     *
1804
     * @param int|null $number <p>How many values you will take?</p>
1805
     *
1806
     * @return static
1807
     *                <p>(Mutable)</p>
1808
     */
1809 34
    public function firstsMutable(int $number = null): self
1810
    {
1811 34
        $this->generatorToArray();
1812
1813 34
        if ($number === null) {
1814 19
            $this->array = (array) \array_shift($this->array);
1815
        } else {
1816 15
            $number = (int) $number;
1817 15
            $this->array = \array_splice($this->array, 0, $number);
1818
        }
1819
1820 34
        return $this;
1821
    }
1822
1823
    /**
1824
     * Exchanges all keys with their associated values in an array.
1825
     *
1826
     * @return static
1827
     *                <p>(Immutable)</p>
1828
     */
1829 1
    public function flip(): self
1830
    {
1831 1
        return static::create(
1832 1
            \array_flip($this->getArray()),
1833 1
            $this->iteratorClass,
1834 1
            false
1835
        );
1836
    }
1837
1838
    /**
1839
     * Get a value from an array (optional using dot-notation).
1840
     *
1841
     * @param mixed $key      <p>The key to look for.</p>
1842
     * @param mixed $fallback <p>Value to fallback to.</p>
1843
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1844
     *                        class.</p>
1845
     *
1846
     * @return mixed|static
1847
     */
1848 167
    public function get($key, $fallback = null, array $array = null)
1849
    {
1850 167
        if ($array !== null) {
1851 4
            $usedArray = $array;
1852
        } else {
1853 164
            $this->generatorToArray();
1854
1855 164
            $usedArray = $this->array;
1856
        }
1857
1858 167
        if ($key === null) {
1859 1
            return static::create(
1860 1
                $usedArray,
1861 1
                $this->iteratorClass,
1862 1
                false
1863
            );
1864
        }
1865
1866
        // php cast "bool"-index into "int"-index
1867 167
        if ((bool) $key === $key) {
1868 3
            $key = (int) $key;
1869
        }
1870
1871 167
        if (\array_key_exists($key, $usedArray) === true) {
1872 157
            if (\is_array($usedArray[$key]) === true) {
1873 11
                return static::create(
1874 11
                    $usedArray[$key],
1875 11
                    $this->iteratorClass,
1876 11
                    false
1877
                );
1878
            }
1879
1880 149
            return $usedArray[$key];
1881
        }
1882
1883
        // crawl through array, get key according to object or not
1884 24
        $usePath = false;
1885
        if (
1886 24
            $this->pathSeparator
1887
            &&
1888 24
            (string) $key === $key
1889
            &&
1890 24
            \strpos($key, $this->pathSeparator) !== false
1891
        ) {
1892 7
            $segments = \explode($this->pathSeparator, (string) $key);
1893 7
            if ($segments !== false) {
1894 7
                $usePath = true;
1895
1896 7
                foreach ($segments as $segment) {
1897
                    if (
1898
                        (
1899 7
                            \is_array($usedArray) === true
1900
                            ||
1901 7
                            $usedArray instanceof \ArrayAccess
1902
                        )
1903
                        &&
1904 7
                        isset($usedArray[$segment])
1905
                    ) {
1906 7
                        $usedArray = $usedArray[$segment];
1907
1908 7
                        continue;
1909
                    }
1910
1911
                    if (
1912 6
                        \is_object($usedArray) === true
1913
                        &&
1914 6
                        \property_exists($usedArray, $segment)
1915
                    ) {
1916 1
                        $usedArray = $usedArray->{$segment};
1917
1918 1
                        continue;
1919
                    }
1920
1921 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
1922
                }
1923
            }
1924
        }
1925
1926 24
        if (!$usePath && !isset($usedArray[$key])) {
1927 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
1928
        }
1929
1930 7
        if (\is_array($usedArray) === true) {
1931 1
            return static::create(
1932 1
                $usedArray,
1933 1
                $this->iteratorClass,
1934 1
                false
1935
            );
1936
        }
1937
1938 7
        return $usedArray;
1939
    }
1940
1941
    /**
1942
     * alias: for "Arrayy->getArray()"
1943
     *
1944
     * @return array
1945
     *
1946
     * @see Arrayy::getArray()
1947
     */
1948 1
    public function getAll(): array
1949
    {
1950 1
        return $this->getArray();
1951
    }
1952
1953
    /**
1954
     * Get the current array from the "Arrayy"-object.
1955
     *
1956
     * @return array
1957
     */
1958 839
    public function getArray(): array
1959
    {
1960
        // init
1961 839
        $array = [];
1962
1963 839
        foreach ($this->getGenerator() as $key => $value) {
1964 730
            $array[$key] = $value;
1965
        }
1966
1967 839
        return $array;
1968
    }
1969
1970
    /**
1971
     * Returns the values from a single column of the input array, identified by
1972
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1973
     *
1974
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1975
     * array by the values from the $indexKey column in the input array.
1976
     *
1977
     * @param mixed $columnKey
1978
     * @param mixed $indexKey
1979
     *
1980
     * @return static
1981
     *                <p>(Immutable)</p>
1982
     */
1983 1
    public function getColumn($columnKey = null, $indexKey = null): self
1984
    {
1985 1
        return static::create(
1986 1
            \array_column($this->getArray(), $columnKey, $indexKey),
1987 1
            $this->iteratorClass,
1988 1
            false
1989
        );
1990
    }
1991
1992
    /**
1993
     * Get the current array from the "Arrayy"-object as generator.
1994
     *
1995
     * @return \Generator
1996
     */
1997 917
    public function getGenerator(): \Generator
1998
    {
1999 917
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2000 39
            yield from $this->generator;
2001
        }
2002
2003 917
        yield from $this->array;
2004 873
    }
2005
2006
    /**
2007
     * alias: for "Arrayy->keys()"
2008
     *
2009
     * @return static
2010
     *                <p>(Immutable)</p>
2011
     *
2012
     * @see Arrayy::keys()
2013
     */
2014 2
    public function getKeys(): self
2015
    {
2016 2
        return $this->keys();
2017
    }
2018
2019
    /**
2020
     * Get the current array from the "Arrayy"-object as object.
2021
     *
2022
     * @return \stdClass
2023
     */
2024 4
    public function getObject(): \stdClass
2025
    {
2026 4
        return self::arrayToObject($this->getArray());
2027
    }
2028
2029
    /**
2030
     * alias: for "Arrayy->randomImmutable()"
2031
     *
2032
     * @return static
2033
     *                <p>(Immutable)</p>
2034
     *
2035
     * @see Arrayy::randomImmutable()
2036
     */
2037 4
    public function getRandom(): self
2038
    {
2039 4
        return $this->randomImmutable();
2040
    }
2041
2042
    /**
2043
     * alias: for "Arrayy->randomKey()"
2044
     *
2045
     * @return mixed
2046
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2047
     *
2048
     * @see Arrayy::randomKey()
2049
     */
2050 3
    public function getRandomKey()
2051
    {
2052 3
        return $this->randomKey();
2053
    }
2054
2055
    /**
2056
     * alias: for "Arrayy->randomKeys()"
2057
     *
2058
     * @param int $number
2059
     *
2060
     * @return static
2061
     *                <p>(Immutable)</p>
2062
     *
2063
     * @see Arrayy::randomKeys()
2064
     */
2065 8
    public function getRandomKeys(int $number): self
2066
    {
2067 8
        return $this->randomKeys($number);
2068
    }
2069
2070
    /**
2071
     * alias: for "Arrayy->randomValue()"
2072
     *
2073
     * @return mixed
2074
     *               <p>Get a random value or null if there wasn't a value.</p>
2075
     *
2076
     * @see Arrayy::randomValue()
2077
     */
2078 3
    public function getRandomValue()
2079
    {
2080 3
        return $this->randomValue();
2081
    }
2082
2083
    /**
2084
     * alias: for "Arrayy->randomValues()"
2085
     *
2086
     * @param int $number
2087
     *
2088
     * @return static
2089
     *                <p>(Immutable)</p>
2090
     *
2091
     * @see Arrayy::randomValues()
2092
     */
2093 6
    public function getRandomValues(int $number): self
2094
    {
2095 6
        return $this->randomValues($number);
2096
    }
2097
2098
    /**
2099
     * Group values from a array according to the results of a closure.
2100
     *
2101
     * @param callable|string $grouper  <p>A callable function name.</p>
2102
     * @param bool            $saveKeys
2103
     *
2104
     * @return static
2105
     *                <p>(Immutable)</p>
2106
     */
2107 4
    public function group($grouper, bool $saveKeys = false): self
2108
    {
2109
        // init
2110 4
        $result = [];
2111
2112
        // Iterate over values, group by property/results from closure.
2113 4
        foreach ($this->getGenerator() as $key => $value) {
2114 4
            if (\is_callable($grouper) === true) {
2115 3
                $groupKey = $grouper($value, $key);
2116
            } else {
2117 1
                $groupKey = $this->get($grouper);
2118
            }
2119
2120 4
            $newValue = $this->get($groupKey, null, $result);
2121
2122 4
            if ($groupKey instanceof self) {
2123
                $groupKey = $groupKey->getArray();
2124
            }
2125
2126 4
            if ($newValue instanceof self) {
2127 4
                $newValue = $newValue->getArray();
2128
            }
2129
2130
            // Add to results.
2131 4
            if ($groupKey !== null) {
2132 3
                if ($saveKeys) {
2133 2
                    $result[$groupKey] = $newValue;
2134 2
                    $result[$groupKey][$key] = $value;
2135
                } else {
2136 1
                    $result[$groupKey] = $newValue;
2137 1
                    $result[$groupKey][] = $value;
2138
                }
2139
            }
2140
        }
2141
2142 4
        return static::create(
2143 4
            $result,
2144 4
            $this->iteratorClass,
2145 4
            false
2146
        );
2147
    }
2148
2149
    /**
2150
     * Check if an array has a given value.
2151
     *
2152
     * INFO: if you need to search recursive please use ```contains()```
2153
     *
2154
     * @param mixed $value
2155
     *
2156
     * @return bool
2157
     */
2158 1
    public function hasValue($value): bool
2159
    {
2160 1
        return $this->contains($value);
2161
    }
2162
2163
    /**
2164
     * Check if an array has a given key.
2165
     *
2166
     * @param mixed $key
2167
     *
2168
     * @return bool
2169
     */
2170 23
    public function has($key): bool
2171
    {
2172 23
        static $UN_FOUND = null;
2173
2174 23
        if ($UN_FOUND === null) {
2175
            // Generate unique string to use as marker.
2176 1
            $UN_FOUND = \uniqid('arrayy', true);
2177
        }
2178
2179 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2180
    }
2181
2182
    /**
2183
     * Implodes the values of this array.
2184
     *
2185
     * @param string $glue
2186
     *
2187
     * @return string
2188
     */
2189 29
    public function implode(string $glue = ''): string
2190
    {
2191 29
        return $this->implode_recursive($glue, $this->getArray(), false);
2192
    }
2193
2194
    /**
2195
     * Implodes the keys of this array.
2196
     *
2197
     * @param string $glue
2198
     *
2199
     * @return string
2200
     */
2201 8
    public function implodeKeys(string $glue = ''): string
2202
    {
2203 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2204
    }
2205
2206
    /**
2207
     * Given a list and an iterate-function that returns
2208
     * a key for each element in the list (or a property name),
2209
     * returns an object with an index of each item.
2210
     *
2211
     * @param mixed $key
2212
     *
2213
     * @return static
2214
     *                <p>(Immutable)</p>
2215
     */
2216 4
    public function indexBy($key): self
2217
    {
2218
        // init
2219 4
        $results = [];
2220
2221 4
        foreach ($this->getGenerator() as $a) {
2222 4
            if (\array_key_exists($key, $a) === true) {
2223 3
                $results[$a[$key]] = $a;
2224
            }
2225
        }
2226
2227 4
        return static::create(
2228 4
            $results,
2229 4
            $this->iteratorClass,
2230 4
            false
2231
        );
2232
    }
2233
2234
    /**
2235
     * alias: for "Arrayy->searchIndex()"
2236
     *
2237
     * @param mixed $value <p>The value to search for.</p>
2238
     *
2239
     * @return mixed
2240
     *
2241
     * @see Arrayy::searchIndex()
2242
     */
2243 4
    public function indexOf($value)
2244
    {
2245 4
        return $this->searchIndex($value);
2246
    }
2247
2248
    /**
2249
     * Get everything but the last..$to items.
2250
     *
2251
     * @param int $to
2252
     *
2253
     * @return static
2254
     *                <p>(Immutable)</p>
2255
     */
2256 12
    public function initial(int $to = 1): self
2257
    {
2258 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2259
    }
2260
2261
    /**
2262
     * Return an array with all elements found in input array.
2263
     *
2264
     * @param array ...$array
2265
     *
2266
     * @return static
2267
     *                <p>(Immutable)</p>
2268
     */
2269 1
    public function intersectionMulti(...$array): self
2270
    {
2271 1
        return static::create(
2272 1
            \array_values(\array_intersect($this->getArray(), ...$array)),
2273 1
            $this->iteratorClass,
2274 1
            false
2275
        );
2276
    }
2277
2278
    /**
2279
     * Return an array with all elements found in input array.
2280
     *
2281
     * @param array $search
2282
     * @param bool  $keepKeys
2283
     *
2284
     * @return static
2285
     *                <p>(Immutable)</p>
2286
     */
2287 4
    public function intersection(array $search, bool $keepKeys = false): self
2288
    {
2289 4
        if ($keepKeys) {
2290 1
            return static::create(
2291 1
                \array_uintersect(
2292 1
                    $this->array,
2293 1
                    $search,
2294
                    static function ($a, $b) {
2295 1
                        return $a === $b ? 0 : -1;
2296 1
                    }
2297
                ),
2298 1
                $this->iteratorClass,
2299 1
                false
2300
            );
2301
        }
2302
2303 3
        return static::create(
2304 3
            \array_values(\array_intersect($this->getArray(), $search)),
2305 3
            $this->iteratorClass,
2306 3
            false
2307
        );
2308
    }
2309
2310
    /**
2311
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2312
     *
2313
     * @param array $search
2314
     *
2315
     * @return bool
2316
     */
2317 1
    public function intersects(array $search): bool
2318
    {
2319 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2320
    }
2321
2322
    /**
2323
     * Invoke a function on all of an array's values.
2324
     *
2325
     * @param callable $callable
2326
     * @param mixed    $arguments
2327
     *
2328
     * @return static
2329
     *                <p>(Immutable)</p>
2330
     */
2331 1
    public function invoke($callable, $arguments = []): self
2332
    {
2333
        // If one argument given for each iteration, create an array for it.
2334 1
        if (\is_array($arguments) === false) {
2335 1
            $arguments = \array_fill(
2336 1
                0,
2337 1
                \count($this->getArray(), \COUNT_NORMAL),
2338 1
                $arguments
2339
            );
2340
        }
2341
2342
        // If the callable has arguments, pass them.
2343 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...
2344 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2345
        } else {
2346 1
            $array = $this->map($callable);
2347
        }
2348
2349 1
        return static::create(
2350 1
            $array,
2351 1
            $this->iteratorClass,
2352 1
            false
2353
        );
2354
    }
2355
2356
    /**
2357
     * Check whether array is associative or not.
2358
     *
2359
     * @param bool $recursive
2360
     *
2361
     * @return bool
2362
     *              <p>Returns true if associative, false otherwise.</p>
2363
     */
2364 15 View Code Duplication
    public function isAssoc(bool $recursive = false): 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...
2365
    {
2366 15
        if ($this->isEmpty()) {
2367 3
            return false;
2368
        }
2369
2370 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2371 13
            if ((string) $key !== $key) {
2372 11
                return false;
2373
            }
2374
        }
2375
2376 3
        return true;
2377
    }
2378
2379
    /**
2380
     * Check if a given key or keys are empty.
2381
     *
2382
     * @param int|int[]|string|string[]|null $keys
2383
     *
2384
     * @return bool
2385
     *              <p>Returns true if empty, false otherwise.</p>
2386
     */
2387 38
    public function isEmpty($keys = null): bool
2388
    {
2389 38
        if ($this->generator) {
2390
            return $this->getArray() === [];
2391
        }
2392
2393 38
        if ($keys === null) {
2394 38
            return $this->array === [];
2395
        }
2396
2397
        foreach ((array) $keys as $key) {
2398
            if (!empty($this->get($key))) {
2399
                return false;
2400
            }
2401
        }
2402
2403
        return true;
2404
    }
2405
2406
    /**
2407
     * Check if the current array is equal to the given "$array" or not.
2408
     *
2409
     * @param array $array
2410
     *
2411
     * @return bool
2412
     */
2413 1
    public function isEqual(array $array): bool
2414
    {
2415 1
        return $this->getArray() === $array;
2416
    }
2417
2418
    /**
2419
     * Check if the current array is a multi-array.
2420
     *
2421
     * @return bool
2422
     */
2423 22
    public function isMultiArray(): bool
2424
    {
2425
        return !(
2426 22
            \count($this->getArray(), \COUNT_NORMAL)
2427
            ===
2428 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2429
        );
2430
    }
2431
2432
    /**
2433
     * Check whether array is numeric or not.
2434
     *
2435
     * @return bool
2436
     *              <p>Returns true if numeric, false otherwise.</p>
2437
     */
2438 5 View Code Duplication
    public function isNumeric(): 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...
2439
    {
2440 5
        if ($this->isEmpty()) {
2441 2
            return false;
2442
        }
2443
2444 4
        foreach ($this->keys()->getGenerator() as $key) {
2445 4
            if ((int) $key !== $key) {
2446 2
                return false;
2447
            }
2448
        }
2449
2450 2
        return true;
2451
    }
2452
2453
    /**
2454
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2455
     *
2456
     * @param bool $recursive
2457
     *
2458
     * @return bool
2459
     */
2460 1
    public function isSequential(bool $recursive = false): bool
2461
    {
2462
2463
        // recursive
2464
2465 1
        if ($recursive === true) {
2466
            return $this->array_keys_recursive($this->getArray())
2467
                   ===
2468
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2469
        }
2470
2471
        // non recursive
2472
2473 1
        return \array_keys($this->getArray())
2474
               ===
2475 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2476
    }
2477
2478
    /**
2479
     * @return array
2480
     */
2481
    public function jsonSerialize(): array
2482
    {
2483
        return $this->getArray();
2484
    }
2485
2486
    /**
2487
     * Checks if the given key exists in the provided array.
2488
     *
2489
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2490
     *       then you need to use "Arrayy->offsetExists()".
2491
     *
2492
     * @param int|string $key the key to look for
2493
     *
2494
     * @return bool
2495
     */
2496 122
    public function keyExists($key): bool
2497
    {
2498 122
        return \array_key_exists($key, $this->array);
2499
    }
2500
2501
    /**
2502
     * Get all keys from the current array.
2503
     *
2504
     * @param bool       $recursive     [optional] <p>
2505
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
2506
     *                                  </p>
2507
     * @param mixed|null $search_values [optional] <p>
2508
     *                                  If specified, then only keys containing these values are returned.
2509
     *                                  </p>
2510
     * @param bool       $strict        [optional] <p>
2511
     *                                  Determines if strict comparison (===) should be used during the search.
2512
     *                                  </p>
2513
     *
2514
     * @return static
2515
     *                <p>(Immutable) An array of all the keys in input.</p>
2516
     */
2517 29
    public function keys(
2518
        bool $recursive = false,
2519
        $search_values = null,
2520
        bool $strict = true
2521
    ): self {
2522
2523
        // recursive
2524
2525 29
        if ($recursive === true) {
2526 4
            $array = $this->array_keys_recursive(
2527 4
                null,
2528 4
                $search_values,
2529 4
                $strict
2530
            );
2531
2532 4
            return static::create(
2533 4
                $array,
2534 4
                $this->iteratorClass,
2535 4
                false
2536
            );
2537
        }
2538
2539
        // non recursive
2540
2541 28
        if ($search_values === null) {
2542
            $arrayFunction = function () {
2543 28
                foreach ($this->getGenerator() as $key => $value) {
2544 26
                    yield $key;
2545
                }
2546 28
            };
2547
        } else {
2548
            $arrayFunction = function () use ($search_values, $strict) {
2549 1
                $is_array_tmp = \is_array($search_values);
2550
2551 1
                foreach ($this->getGenerator() as $key => $value) {
2552 View Code Duplication
                    if (
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...
2553
                        (
2554 1
                            $is_array_tmp === false
2555
                            &&
2556 1
                            $strict === true
2557
                            &&
2558 1
                            $search_values === $value
2559
                        )
2560
                        ||
2561
                        (
2562 1
                            $is_array_tmp === false
2563
                            &&
2564 1
                            $strict === false
2565
                            &&
2566 1
                            $search_values == $value
2567
                        )
2568
                        ||
2569
                        (
2570 1
                            $is_array_tmp === true
2571
                            &&
2572 1
                            \in_array($value, $search_values, $strict)
2573
                        )
2574
                    ) {
2575 1
                        yield $key;
2576
                    }
2577
                }
2578 1
            };
2579
        }
2580
2581 28
        return static::create(
2582 28
            $arrayFunction,
2583 28
            $this->iteratorClass,
2584 28
            false
2585
        );
2586
    }
2587
2588
    /**
2589
     * Sort an array by key in reverse order.
2590
     *
2591
     * @param int $sort_flags [optional] <p>
2592
     *                        You may modify the behavior of the sort using the optional
2593
     *                        parameter sort_flags, for details
2594
     *                        see sort.
2595
     *                        </p>
2596
     *
2597
     * @return static
2598
     *                <p>(Mutable) Return this Arrayy object.</p>
2599
     */
2600 4
    public function krsort(int $sort_flags = 0): self
2601
    {
2602 4
        $this->generatorToArray();
2603
2604 4
        \krsort($this->array, $sort_flags);
2605
2606 4
        return $this;
2607
    }
2608
2609
    /**
2610
     * Get the last value from the current array.
2611
     *
2612
     * @return mixed
2613
     *               <p>Return null if there wasn't a element.</p>
2614
     */
2615 17
    public function last()
2616
    {
2617 17
        $key_last = $this->lastKey();
2618 17
        if ($key_last === null) {
2619 2
            return null;
2620
        }
2621
2622 15
        return $this->get($key_last);
2623
    }
2624
2625
    /**
2626
     * Get the last key from the current array.
2627
     *
2628
     * @return mixed
2629
     *               <p>Return null if there wasn't a element.</p>
2630
     */
2631 21
    public function lastKey()
2632
    {
2633 21
        $this->generatorToArray();
2634
2635 21
        return \array_key_last($this->array);
2636
    }
2637
2638
    /**
2639
     * Get the last value(s) from the current array.
2640
     *
2641
     * @param int|null $number
2642
     *
2643
     * @return static
2644
     *                <p>(Immutable)</p>
2645
     */
2646 13
    public function lastsImmutable(int $number = null): self
2647
    {
2648 13
        if ($this->isEmpty()) {
2649 1
            return static::create(
2650 1
                [],
2651 1
                $this->iteratorClass,
2652 1
                false
2653
            );
2654
        }
2655
2656 12
        if ($number === null) {
2657 8
            $poppedValue = $this->last();
2658
2659 8
            if ($poppedValue === null) {
2660 1
                $poppedValue = [$poppedValue];
2661
            } else {
2662 7
                $poppedValue = (array) $poppedValue;
2663
            }
2664
2665 8
            $arrayy = static::create(
2666 8
                $poppedValue,
2667 8
                $this->iteratorClass,
2668 8
                false
2669
            );
2670
        } else {
2671 4
            $number = (int) $number;
2672 4
            $arrayy = $this->rest(-$number);
2673
        }
2674
2675 12
        return $arrayy;
2676
    }
2677
2678
    /**
2679
     * Get the last value(s) from the current array.
2680
     *
2681
     * @param int|null $number
2682
     *
2683
     * @return static
2684
     *                <p>(Mutable)</p>
2685
     */
2686 13
    public function lastsMutable(int $number = null): self
2687
    {
2688 13
        if ($this->isEmpty()) {
2689 1
            return $this;
2690
        }
2691
2692 12
        if ($number === null) {
2693 8
            $poppedValue = $this->last();
2694
2695 8
            if ($poppedValue === null) {
2696 1
                $poppedValue = [$poppedValue];
2697
            } else {
2698 7
                $poppedValue = (array) $poppedValue;
2699
            }
2700
2701 8
            $this->array = static::create(
2702 8
                $poppedValue,
2703 8
                $this->iteratorClass,
2704 8
                false
2705 8
            )->getArray();
2706
        } else {
2707 4
            $number = (int) $number;
2708 4
            $this->array = $this->rest(-$number)->getArray();
2709
        }
2710
2711 12
        $this->generator = null;
2712
2713 12
        return $this;
2714
    }
2715
2716
    /**
2717
     * Count the values from the current array.
2718
     *
2719
     * alias: for "Arrayy->count()"
2720
     *
2721
     * @param int $mode
2722
     *
2723
     * @return int
2724
     *
2725
     * @see Arrayy::count()
2726
     */
2727 20
    public function length(int $mode = \COUNT_NORMAL): int
2728
    {
2729 20
        return $this->count($mode);
2730
    }
2731
2732
    /**
2733
     * Apply the given function to the every element of the array,
2734
     * collecting the results.
2735
     *
2736
     * @param callable $callable
2737
     * @param bool     $useKeyAsSecondParameter
2738
     * @param mixed    ...$arguments
2739
     *
2740
     * @return static
2741
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2742
     */
2743 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2744
    {
2745 5
        $useArguments = \func_num_args() > 2;
2746
2747 5
        return static::create(
2748
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2749 5
                foreach ($this->getGenerator() as $key => $value) {
2750 4
                    if ($useArguments) {
2751 3
                        if ($useKeyAsSecondParameter) {
2752
                            yield $key => $callable($value, $key, ...$arguments);
2753
                        } else {
2754 3
                            yield $key => $callable($value, ...$arguments);
2755
                        }
2756
                    } else {
2757
                        /** @noinspection NestedPositiveIfStatementsInspection */
2758 4
                        if ($useKeyAsSecondParameter) {
2759
                            yield $key => $callable($value, $key);
2760
                        } else {
2761 4
                            yield $key => $callable($value);
2762
                        }
2763
                    }
2764
                }
2765 5
            },
2766 5
            $this->iteratorClass,
2767 5
            false
2768
        );
2769
    }
2770
2771
    /**
2772
     * Check if all items in current array match a truth test.
2773
     *
2774
     * @param \Closure $closure
2775
     *
2776
     * @return bool
2777
     */
2778 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...
2779
    {
2780 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2781 2
            return false;
2782
        }
2783
2784 13
        foreach ($this->getGenerator() as $key => $value) {
2785 13
            $value = $closure($value, $key);
2786
2787 13
            if ($value === false) {
2788 7
                return false;
2789
            }
2790
        }
2791
2792 7
        return true;
2793
    }
2794
2795
    /**
2796
     * Check if any item in the current array matches a truth test.
2797
     *
2798
     * @param \Closure $closure
2799
     *
2800
     * @return bool
2801
     */
2802 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...
2803
    {
2804 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2805 2
            return false;
2806
        }
2807
2808 12
        foreach ($this->getGenerator() as $key => $value) {
2809 12
            $value = $closure($value, $key);
2810
2811 12
            if ($value === true) {
2812 9
                return true;
2813
            }
2814
        }
2815
2816 4
        return false;
2817
    }
2818
2819
    /**
2820
     * Get the max value from an array.
2821
     *
2822
     * @return mixed
2823
     */
2824 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...
2825
    {
2826 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2827 1
            return false;
2828
        }
2829
2830 9
        return \max($this->getArray());
2831
    }
2832
2833
    /**
2834
     * Merge the new $array into the current array.
2835
     *
2836
     * - keep key,value from the current array, also if the index is in the new $array
2837
     *
2838
     * @param array $array
2839
     * @param bool  $recursive
2840
     *
2841
     * @return static
2842
     *                <p>(Immutable)</p>
2843
     */
2844 25 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false): self
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...
2845
    {
2846 25
        if ($recursive === true) {
2847 4
            $result = \array_replace_recursive($this->getArray(), $array);
2848
        } else {
2849 21
            $result = \array_replace($this->getArray(), $array);
2850
        }
2851
2852 25
        return static::create(
2853 25
            $result,
2854 25
            $this->iteratorClass,
2855 25
            false
2856
        );
2857
    }
2858
2859
    /**
2860
     * Merge the new $array into the current array.
2861
     *
2862
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2863
     * - create new indexes
2864
     *
2865
     * @param array $array
2866
     * @param bool  $recursive
2867
     *
2868
     * @return static
2869
     *                <p>(Immutable)</p>
2870
     */
2871 16 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false): self
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...
2872
    {
2873 16
        if ($recursive === true) {
2874 4
            $result = \array_merge_recursive($this->getArray(), $array);
2875
        } else {
2876 12
            $result = \array_merge($this->getArray(), $array);
2877
        }
2878
2879 16
        return static::create(
2880 16
            $result,
2881 16
            $this->iteratorClass,
2882 16
            false
2883
        );
2884
    }
2885
2886
    /**
2887
     * Merge the the current array into the $array.
2888
     *
2889
     * - use key,value from the new $array, also if the index is in the current array
2890
     *
2891
     * @param array $array
2892
     * @param bool  $recursive
2893
     *
2894
     * @return static
2895
     *                <p>(Immutable)</p>
2896
     */
2897 16 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false): self
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...
2898
    {
2899 16
        if ($recursive === true) {
2900 4
            $result = \array_replace_recursive($array, $this->getArray());
2901
        } else {
2902 12
            $result = \array_replace($array, $this->getArray());
2903
        }
2904
2905 16
        return static::create(
2906 16
            $result,
2907 16
            $this->iteratorClass,
2908 16
            false
2909
        );
2910
    }
2911
2912
    /**
2913
     * Merge the current array into the new $array.
2914
     *
2915
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2916
     * - create new indexes
2917
     *
2918
     * @param array $array
2919
     * @param bool  $recursive
2920
     *
2921
     * @return static
2922
     *                <p>(Immutable)</p>
2923
     */
2924 17 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false): self
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...
2925
    {
2926 17
        if ($recursive === true) {
2927 4
            $result = \array_merge_recursive($array, $this->getArray());
2928
        } else {
2929 13
            $result = \array_merge($array, $this->getArray());
2930
        }
2931
2932 17
        return static::create(
2933 17
            $result,
2934 17
            $this->iteratorClass,
2935 17
            false
2936
        );
2937
    }
2938
2939
    /**
2940
     * @return ArrayyMeta|static
2941
     */
2942 15
    public static function meta()
2943
    {
2944 15
        return (new ArrayyMeta())->getMetaObject(static::class);
2945
    }
2946
2947
    /**
2948
     * Get the min value from an array.
2949
     *
2950
     * @return mixed
2951
     */
2952 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...
2953
    {
2954 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2955 1
            return false;
2956
        }
2957
2958 9
        return \min($this->getArray());
2959
    }
2960
2961
    /**
2962
     * Get the most used value from the array.
2963
     *
2964
     * @return mixed
2965
     *               <p>Return null if there wasn't a element.</p>
2966
     */
2967 3
    public function mostUsedValue()
2968
    {
2969 3
        return $this->countValues()->arsort()->firstKey();
2970
    }
2971
2972
    /**
2973
     * Get the most used value from the array.
2974
     *
2975
     * @param int|null $number <p>How many values you will take?</p>
2976
     *
2977
     * @return static
2978
     *                <p>(Immutable)</p>
2979
     */
2980 3
    public function mostUsedValues(int $number = null): self
2981
    {
2982 3
        return $this->countValues()->arsort()->firstsKeys($number);
2983
    }
2984
2985
    /**
2986
     * Move an array element to a new index.
2987
     *
2988
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2989
     *
2990
     * @param int|string $from
2991
     * @param int        $to
2992
     *
2993
     * @return static
2994
     *                <p>(Immutable)</p>
2995
     */
2996 1
    public function moveElement($from, $to): self
2997
    {
2998 1
        $array = $this->getArray();
2999
3000 1
        if ((int) $from === $from) {
3001 1
            $tmp = \array_splice($array, $from, 1);
3002 1
            \array_splice($array, (int) $to, 0, $tmp);
3003 1
            $output = $array;
3004 1
        } elseif ((string) $from === $from) {
3005 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3006 1
            $itemToMove = $array[$from];
3007 1
            if ($indexToMove !== false) {
3008 1
                \array_splice($array, $indexToMove, 1);
3009
            }
3010 1
            $i = 0;
3011 1
            $output = [];
3012 1
            foreach ($array as $key => $item) {
3013 1
                if ($i === $to) {
3014 1
                    $output[$from] = $itemToMove;
3015
                }
3016 1
                $output[$key] = $item;
3017 1
                ++$i;
3018
            }
3019
        } else {
3020
            $output = [];
3021
        }
3022
3023 1
        return static::create(
3024 1
            $output,
3025 1
            $this->iteratorClass,
3026 1
            false
3027
        );
3028
    }
3029
3030
    /**
3031
     * Move an array element to the first place.
3032
     *
3033
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3034
     *       loss the keys of an indexed array.
3035
     *
3036
     * @param int|string $key
3037
     *
3038
     * @return static
3039
     *                <p>(Immutable)</p>
3040
     */
3041 1 View Code Duplication
    public function moveElementToFirstPlace($key): self
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...
3042
    {
3043 1
        $array = $this->getArray();
3044
3045 1
        if ($this->offsetExists($key)) {
3046 1
            $tmpValue = $this->get($key);
3047 1
            unset($array[$key]);
3048 1
            $array = [$key => $tmpValue] + $array;
3049
        }
3050
3051 1
        return static::create(
3052 1
            $array,
3053 1
            $this->iteratorClass,
3054 1
            false
3055
        );
3056
    }
3057
3058
    /**
3059
     * Move an array element to the last place.
3060
     *
3061
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3062
     *       loss the keys of an indexed array.
3063
     *
3064
     * @param int|string $key
3065
     *
3066
     * @return static
3067
     *                <p>(Immutable)</p>
3068
     */
3069 1 View Code Duplication
    public function moveElementToLastPlace($key): self
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...
3070
    {
3071 1
        $array = $this->getArray();
3072
3073 1
        if ($this->offsetExists($key)) {
3074 1
            $tmpValue = $this->get($key);
3075 1
            unset($array[$key]);
3076 1
            $array += [$key => $tmpValue];
3077
        }
3078
3079 1
        return static::create(
3080 1
            $array,
3081 1
            $this->iteratorClass,
3082 1
            false
3083
        );
3084
    }
3085
3086
    /**
3087
     * Get a subset of the items from the given array.
3088
     *
3089
     * @param mixed[] $keys
3090
     *
3091
     * @return static
3092
     *                <p>(Immutable)</p>
3093
     */
3094
    public function only(array $keys): self
3095
    {
3096
        $array = $this->getArray();
3097
3098
        return static::create(
3099
            \array_intersect_key($array, \array_flip($keys)),
3100
            $this->iteratorClass,
3101
            false
3102
        );
3103
    }
3104
3105
    /**
3106
     * Pad array to the specified size with a given value.
3107
     *
3108
     * @param int   $size  <p>Size of the result array.</p>
3109
     * @param mixed $value <p>Empty value by default.</p>
3110
     *
3111
     * @return static
3112
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3113
     */
3114 5
    public function pad(int $size, $value): self
3115
    {
3116 5
        return static::create(
3117 5
            \array_pad($this->getArray(), $size, $value),
3118 5
            $this->iteratorClass,
3119 5
            false
3120
        );
3121
    }
3122
3123
    /**
3124
     * Pop a specified value off the end of the current array.
3125
     *
3126
     * @return mixed
3127
     *               <p>(Mutable) The popped element from the current array.</p>
3128
     */
3129 5
    public function pop()
3130
    {
3131 5
        $this->generatorToArray();
3132
3133 5
        return \array_pop($this->array);
3134
    }
3135
3136
    /**
3137
     * Prepend a (key) + value to the current array.
3138
     *
3139
     * @param mixed $value
3140
     * @param mixed $key
3141
     *
3142
     * @return static
3143
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3144
     */
3145 11
    public function prepend($value, $key = null): self
3146
    {
3147 11
        $this->generatorToArray();
3148
3149 11
        if ($this->properties !== []) {
3150 3
            $this->checkType($key, $value);
3151
        }
3152
3153 9
        if ($key === null) {
3154 8
            \array_unshift($this->array, $value);
3155
        } else {
3156 2
            $this->array = [$key => $value] + $this->array;
3157
        }
3158
3159 9
        return $this;
3160
    }
3161
3162
    /**
3163
     * Add a suffix to each key.
3164
     *
3165
     * @param mixed $suffix
3166
     *
3167
     * @return static
3168
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3169
     */
3170 10 View Code Duplication
    public function prependToEachKey($suffix): self
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...
3171
    {
3172
        // init
3173 10
        $result = [];
3174
3175 10
        foreach ($this->getGenerator() as $key => $item) {
3176 9
            if ($item instanceof self) {
3177
                $result[$key] = $item->prependToEachKey($suffix);
3178 9
            } elseif (\is_array($item) === true) {
3179
                $result[$key] = self::create(
3180
                    $item,
3181
                    $this->iteratorClass,
3182
                    false
3183
                )->prependToEachKey($suffix)
3184
                    ->toArray();
3185
            } else {
3186 9
                $result[$key . $suffix] = $item;
3187
            }
3188
        }
3189
3190 10
        return self::create(
3191 10
            $result,
3192 10
            $this->iteratorClass,
3193 10
            false
3194
        );
3195
    }
3196
3197
    /**
3198
     * Add a suffix to each value.
3199
     *
3200
     * @param mixed $suffix
3201
     *
3202
     * @return static
3203
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3204
     */
3205 10 View Code Duplication
    public function prependToEachValue($suffix): self
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...
3206
    {
3207
        // init
3208 10
        $result = [];
3209
3210 10
        foreach ($this->getGenerator() as $key => $item) {
3211 9
            if ($item instanceof self) {
3212
                $result[$key] = $item->prependToEachValue($suffix);
3213 9
            } elseif (\is_array($item) === true) {
3214
                $result[$key] = self::create(
3215
                    $item,
3216
                    $this->iteratorClass,
3217
                    false
3218
                )->prependToEachValue($suffix)
3219
                    ->toArray();
3220 9
            } elseif (\is_object($item) === true) {
3221 1
                $result[$key] = $item;
3222
            } else {
3223 8
                $result[$key] = $item . $suffix;
3224
            }
3225
        }
3226
3227 10
        return self::create(
3228 10
            $result,
3229 10
            $this->iteratorClass,
3230 10
            false
3231
        );
3232
    }
3233
3234
    /**
3235
     * Return the value of a given key and
3236
     * delete the key.
3237
     *
3238
     * @param int|int[]|string|string[]|null $keyOrKeys
3239
     * @param mixed                          $fallback
3240
     *
3241
     * @return mixed
3242
     */
3243 1
    public function pull($keyOrKeys = null, $fallback = null)
3244
    {
3245 1
        if ($keyOrKeys === null) {
3246
            $array = $this->getArray();
3247
            $this->clear();
3248
3249
            return $array;
3250
        }
3251
3252 1
        if (\is_array($keyOrKeys) === true) {
3253 1
            $valueOrValues = [];
3254 1
            foreach ($keyOrKeys as $key) {
3255 1
                $valueOrValues[] = $this->get($key, $fallback);
3256 1
                $this->offsetUnset($key);
3257
            }
3258
        } else {
3259 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3260 1
            $this->offsetUnset($keyOrKeys);
3261
        }
3262
3263 1
        return $valueOrValues;
3264
    }
3265
3266
    /**
3267
     * Push one or more values onto the end of array at once.
3268
     *
3269
     * @param array ...$args
3270
     *
3271
     * @return static
3272
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3273
     *
3274
     * @noinspection ReturnTypeCanBeDeclaredInspection
3275
     */
3276 5
    public function push(...$args)
3277
    {
3278 5
        $this->generatorToArray();
3279
3280
        if (
3281 5
            $this->checkPropertyTypes
3282
            &&
3283 5
            $this->properties !== []
3284
        ) {
3285 1
            foreach ($args as $key => $value) {
3286 1
                $this->checkType($key, $value);
3287
            }
3288
        }
3289
3290 5
        \array_push(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_push() as the parameter $array expects a reference.
Loading history...
3291
3292 5
        return $this;
3293
    }
3294
3295
    /**
3296
     * Get a random value from the current array.
3297
     *
3298
     * @param int|null $number <p>How many values you will take?</p>
3299
     *
3300
     * @return static
3301
     *                <p>(Immutable)</p>
3302
     */
3303 19
    public function randomImmutable(int $number = null): self
3304
    {
3305 19
        $this->generatorToArray();
3306
3307 19 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...
3308 1
            return static::create(
3309 1
                [],
3310 1
                $this->iteratorClass,
3311 1
                false
3312
            );
3313
        }
3314
3315 18 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...
3316
            /** @noinspection NonSecureArrayRandUsageInspection */
3317 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3318
3319 13
            return static::create(
3320 13
                $arrayRandValue,
3321 13
                $this->iteratorClass,
3322 13
                false
3323
            );
3324
        }
3325
3326 6
        $arrayTmp = $this->array;
3327
        /** @noinspection NonSecureShuffleUsageInspection */
3328 6
        \shuffle($arrayTmp);
3329
3330 6
        return static::create(
3331 6
            $arrayTmp,
3332 6
            $this->iteratorClass,
3333 6
            false
3334 6
        )->firstsImmutable($number);
3335
    }
3336
3337
    /**
3338
     * Pick a random key/index from the keys of this array.
3339
     *
3340
     * @throws \RangeException If array is empty
3341
     *
3342
     * @return mixed
3343
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3344
     */
3345 4
    public function randomKey()
3346
    {
3347 4
        $result = $this->randomKeys(1);
3348
3349 4
        if (!isset($result[0])) {
3350
            $result[0] = null;
3351
        }
3352
3353 4
        return $result[0];
3354
    }
3355
3356
    /**
3357
     * Pick a given number of random keys/indexes out of this array.
3358
     *
3359
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3360
     *
3361
     * @throws \RangeException If array is empty
3362
     *
3363
     * @return static
3364
     *                <p>(Immutable)</p>
3365
     */
3366 13
    public function randomKeys(int $number): self
3367
    {
3368 13
        $this->generatorToArray();
3369
3370 13
        $count = \count($this->array, \COUNT_NORMAL);
3371
3372 13
        if ($number === 0 || $number > $count) {
3373 2
            throw new \RangeException(
3374 2
                \sprintf(
3375 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3376 2
                    $number,
3377 2
                    $count
3378
                )
3379
            );
3380
        }
3381
3382 11
        $result = (array) \array_rand($this->array, $number);
3383
3384 11
        return static::create(
3385 11
            $result,
3386 11
            $this->iteratorClass,
3387 11
            false
3388
        );
3389
    }
3390
3391
    /**
3392
     * Get a random value from the current array.
3393
     *
3394
     * @param int|null $number <p>How many values you will take?</p>
3395
     *
3396
     * @return static
3397
     *                <p>(Mutable)</p>
3398
     */
3399 17
    public function randomMutable(int $number = null): self
3400
    {
3401 17
        $this->generatorToArray();
3402
3403 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...
3404
            return static::create(
3405
                [],
3406
                $this->iteratorClass,
3407
                false
3408
            );
3409
        }
3410
3411 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...
3412
            /** @noinspection NonSecureArrayRandUsageInspection */
3413 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3414 7
            $this->array = $arrayRandValue;
3415
3416 7
            return $this;
3417
        }
3418
3419
        /** @noinspection NonSecureShuffleUsageInspection */
3420 11
        \shuffle($this->array);
3421
3422 11
        return $this->firstsMutable($number);
3423
    }
3424
3425
    /**
3426
     * Pick a random value from the values of this array.
3427
     *
3428
     * @return mixed
3429
     *               <p>Get a random value or null if there wasn't a value.</p>
3430
     */
3431 4
    public function randomValue()
3432
    {
3433 4
        $result = $this->randomImmutable();
3434
3435 4
        if (!isset($result[0])) {
3436
            $result[0] = null;
3437
        }
3438
3439 4
        return $result[0];
3440
    }
3441
3442
    /**
3443
     * Pick a given number of random values out of this array.
3444
     *
3445
     * @param int $number
3446
     *
3447
     * @return static
3448
     *                <p>(Mutable)</p>
3449
     */
3450 7
    public function randomValues(int $number): self
3451
    {
3452 7
        return $this->randomMutable($number);
3453
    }
3454
3455
    /**
3456
     * Get a random value from an array, with the ability to skew the results.
3457
     *
3458
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3459
     *
3460
     * @param array    $array
3461
     * @param int|null $number <p>How many values you will take?</p>
3462
     *
3463
     * @return static
3464
     *                <p>(Immutable)</p>
3465
     */
3466 9
    public function randomWeighted(array $array, int $number = null): self
3467
    {
3468
        // init
3469 9
        $options = [];
3470
3471 9
        foreach ($array as $option => $weight) {
3472 9
            if ($this->searchIndex($option) !== false) {
3473 2
                for ($i = 0; $i < $weight; ++$i) {
3474 1
                    $options[] = $option;
3475
                }
3476
            }
3477
        }
3478
3479 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3480
    }
3481
3482
    /**
3483
     * Reduce the current array via callable e.g. anonymous-function.
3484
     *
3485
     * @param callable $callable
3486
     * @param mixed    $init
3487
     *
3488
     * @return static
3489
     *                <p>(Immutable)</p>
3490
     */
3491 18
    public function reduce($callable, $init = []): self
3492
    {
3493 18
        if ($this->generator) {
3494 1
            $result = $init;
3495
3496 1
            foreach ($this->getGenerator() as $value) {
3497 1
                $result = $callable($result, $value);
3498
            }
3499
3500 1
            return static::create(
3501 1
                $result,
3502 1
                $this->iteratorClass,
3503 1
                false
3504
            );
3505
        }
3506
3507 18
        $result = \array_reduce($this->array, $callable, $init);
3508
3509 18
        if ($result === null) {
3510
            $this->array = [];
3511
        } else {
3512 18
            $this->array = (array) $result;
3513
        }
3514
3515 18
        return static::create(
3516 18
            $this->array,
3517 18
            $this->iteratorClass,
3518 18
            false
3519
        );
3520
    }
3521
3522
    /**
3523
     * @param bool $unique
3524
     *
3525
     * @return static
3526
     *                <p>(Immutable)</p>
3527
     */
3528 14
    public function reduce_dimension(bool $unique = true): self
3529
    {
3530
        // init
3531 14
        $result = [];
3532
3533 14
        foreach ($this->getGenerator() as $val) {
3534 12
            if (\is_array($val) === true) {
3535 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
3536
            } else {
3537 12
                $result[] = [$val];
3538
            }
3539
        }
3540
3541 14
        $result = $result === [] ? [] : \array_merge(...$result);
3542
3543 14
        $resultArrayy = new self($result);
3544
3545 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3546
    }
3547
3548
    /**
3549
     * Create a numerically re-indexed Arrayy object.
3550
     *
3551
     * @return static
3552
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3553
     */
3554 9
    public function reindex(): self
3555
    {
3556 9
        $this->generatorToArray();
3557
3558 9
        $this->array = \array_values($this->array);
3559
3560 9
        return $this;
3561
    }
3562
3563
    /**
3564
     * Return all items that fail the truth test.
3565
     *
3566
     * @param \Closure $closure
3567
     *
3568
     * @return static
3569
     *                <p>(Immutable)</p>
3570
     */
3571 1 View Code Duplication
    public function reject(\Closure $closure): self
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...
3572
    {
3573
        // init
3574 1
        $filtered = [];
3575
3576 1
        foreach ($this->getGenerator() as $key => $value) {
3577 1
            if (!$closure($value, $key)) {
3578 1
                $filtered[$key] = $value;
3579
            }
3580
        }
3581
3582 1
        return static::create(
3583 1
            $filtered,
3584 1
            $this->iteratorClass,
3585 1
            false
3586
        );
3587
    }
3588
3589
    /**
3590
     * Remove a value from the current array (optional using dot-notation).
3591
     *
3592
     * @param mixed $key
3593
     *
3594
     * @return static
3595
     *                <p>(Mutable)</p>
3596
     */
3597 18
    public function remove($key): self
3598
    {
3599
        // recursive call
3600 18
        if (\is_array($key) === true) {
3601
            foreach ($key as $k) {
3602
                $this->internalRemove($k);
3603
            }
3604
3605
            return static::create(
3606
                $this->getArray(),
3607
                $this->iteratorClass,
3608
                false
3609
            );
3610
        }
3611
3612 18
        $this->internalRemove($key);
3613
3614 18
        return static::create(
3615 18
            $this->getArray(),
3616 18
            $this->iteratorClass,
3617 18
            false
3618
        );
3619
    }
3620
3621
    /**
3622
     * Remove the first value from the current array.
3623
     *
3624
     * @return static
3625
     *                <p>(Immutable)</p>
3626
     */
3627 7 View Code Duplication
    public function removeFirst(): self
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...
3628
    {
3629 7
        $tmpArray = $this->getArray();
3630
3631 7
        \array_shift($tmpArray);
3632
3633 7
        return static::create(
3634 7
            $tmpArray,
3635 7
            $this->iteratorClass,
3636 7
            false
3637
        );
3638
    }
3639
3640
    /**
3641
     * Remove the last value from the current array.
3642
     *
3643
     * @return static
3644
     *                <p>(Immutable)</p>
3645
     */
3646 7 View Code Duplication
    public function removeLast(): self
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...
3647
    {
3648 7
        $tmpArray = $this->getArray();
3649
3650 7
        \array_pop($tmpArray);
3651
3652 7
        return static::create(
3653 7
            $tmpArray,
3654 7
            $this->iteratorClass,
3655 7
            false
3656
        );
3657
    }
3658
3659
    /**
3660
     * Removes a particular value from an array (numeric or associative).
3661
     *
3662
     * @param mixed $value
3663
     *
3664
     * @return static
3665
     *                <p>(Immutable)</p>
3666
     */
3667 7
    public function removeValue($value): self
3668
    {
3669 7
        $this->generatorToArray();
3670
3671
        // init
3672 7
        $isNumericArray = true;
3673
3674 7
        foreach ($this->getGenerator() as $key => $item) {
3675 6
            if ($item === $value) {
3676 6
                if ((int) $key !== $key) {
3677
                    $isNumericArray = false;
3678
                }
3679 6
                unset($this->array[$key]);
3680
            }
3681
        }
3682
3683 7
        if ($isNumericArray) {
3684 7
            $this->array = \array_values($this->array);
3685
        }
3686
3687 7
        return static::create(
3688 7
            $this->array,
3689 7
            $this->iteratorClass,
3690 7
            false
3691
        );
3692
    }
3693
3694
    /**
3695
     * Generate array of repeated arrays.
3696
     *
3697
     * @param int $times <p>How many times has to be repeated.</p>
3698
     *
3699
     * @return static
3700
     *                <p>(Immutable)</p>
3701
     */
3702 1
    public function repeat($times): self
3703
    {
3704 1
        if ($times === 0) {
3705 1
            return static::create([], $this->iteratorClass);
3706
        }
3707
3708 1
        return static::create(
3709 1
            \array_fill(0, (int) $times, $this->getArray()),
3710 1
            $this->iteratorClass,
3711 1
            false
3712
        );
3713
    }
3714
3715
    /**
3716
     * Replace a key with a new key/value pair.
3717
     *
3718
     * @param mixed $replace
3719
     * @param mixed $key
3720
     * @param mixed $value
3721
     *
3722
     * @return static
3723
     *                <p>(Immutable)</p>
3724
     */
3725 2
    public function replace($replace, $key, $value): self
3726
    {
3727 2
        $that = clone $this;
3728
3729 2
        return $that->remove($replace)
3730 2
            ->set($key, $value);
3731
    }
3732
3733
    /**
3734
     * Create an array using the current array as values and the other array as keys.
3735
     *
3736
     * @param array $keys <p>An array of keys.</p>
3737
     *
3738
     * @return static
3739
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3740
     */
3741 2
    public function replaceAllKeys(array $keys): self
3742
    {
3743 2
        return static::create(
3744 2
            \array_combine($keys, $this->getArray()),
3745 2
            $this->iteratorClass,
3746 2
            false
3747
        );
3748
    }
3749
3750
    /**
3751
     * Create an array using the current array as keys and the other array as values.
3752
     *
3753
     * @param array $array <p>An array o values.</p>
3754
     *
3755
     * @return static
3756
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3757
     */
3758 2
    public function replaceAllValues(array $array): self
3759
    {
3760 2
        return static::create(
3761 2
            \array_combine($this->array, $array),
3762 2
            $this->iteratorClass,
3763 2
            false
3764
        );
3765
    }
3766
3767
    /**
3768
     * Replace the keys in an array with another set.
3769
     *
3770
     * @param array $keys <p>An array of keys matching the array's size</p>
3771
     *
3772
     * @return static
3773
     *                <p>(Immutable)</p>
3774
     */
3775 1
    public function replaceKeys(array $keys): self
3776
    {
3777 1
        $values = \array_values($this->getArray());
3778 1
        $result = \array_combine($keys, $values);
3779
3780 1
        return static::create(
3781 1
            $result,
3782 1
            $this->iteratorClass,
3783 1
            false
3784
        );
3785
    }
3786
3787
    /**
3788
     * Replace the first matched value in an array.
3789
     *
3790
     * @param mixed $search      <p>The value to replace.</p>
3791
     * @param mixed $replacement <p>The value to replace.</p>
3792
     *
3793
     * @return static
3794
     *                <p>(Immutable)</p>
3795
     */
3796 3
    public function replaceOneValue($search, $replacement = ''): self
3797
    {
3798 3
        $array = $this->getArray();
3799 3
        $key = \array_search($search, $array, true);
3800
3801 3
        if ($key !== false) {
3802 3
            $array[$key] = $replacement;
3803
        }
3804
3805 3
        return static::create(
3806 3
            $array,
3807 3
            $this->iteratorClass,
3808 3
            false
3809
        );
3810
    }
3811
3812
    /**
3813
     * Replace values in the current array.
3814
     *
3815
     * @param mixed $search      <p>The value to replace.</p>
3816
     * @param mixed $replacement <p>What to replace it with.</p>
3817
     *
3818
     * @return static
3819
     *                <p>(Immutable)</p>
3820
     */
3821 1
    public function replaceValues($search, $replacement = ''): self
3822
    {
3823 1
        return $this->each(
3824
            static function ($value) use ($search, $replacement) {
3825 1
                return \str_replace($search, $replacement, $value);
3826 1
            }
3827
        );
3828
    }
3829
3830
    /**
3831
     * Get the last elements from index $from until the end of this array.
3832
     *
3833
     * @param int $from
3834
     *
3835
     * @return static
3836
     *                <p>(Immutable)</p>
3837
     */
3838 15 View Code Duplication
    public function rest(int $from = 1): self
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...
3839
    {
3840 15
        $tmpArray = $this->getArray();
3841
3842 15
        return static::create(
3843 15
            \array_splice($tmpArray, $from),
3844 15
            $this->iteratorClass,
3845 15
            false
3846
        );
3847
    }
3848
3849
    /**
3850
     * @param int      $offset
3851
     * @param int|null $length
3852
     * @param array    $replacement
3853
     *
3854
     * @return static
3855
     *                <p>(Immutable)</p>
3856
     */
3857 1
    public function splice(int $offset, int $length = null, $replacement = []): self
3858
    {
3859 1
        $tmpArray = $this->getArray();
3860
3861 1
        \array_splice($tmpArray, $offset, $length ?? $this->count(), $replacement);
3862
3863 1
        return static::create(
3864 1
            $tmpArray,
3865 1
            $this->iteratorClass,
3866 1
            false
3867
        );
3868
    }
3869
3870
    /**
3871
     * Return the array in the reverse order.
3872
     *
3873
     * @return static
3874
     *                <p>(Mutable) Return this Arrayy object.</p>
3875
     */
3876 9
    public function reverse(): self
3877
    {
3878 9
        $this->generatorToArray();
3879
3880 9
        $this->array = \array_reverse($this->array);
3881
3882 9
        return $this;
3883
    }
3884
3885
    /**
3886
     * Sort an array in reverse order.
3887
     *
3888
     * @param int $sort_flags [optional] <p>
3889
     *                        You may modify the behavior of the sort using the optional
3890
     *                        parameter sort_flags, for details
3891
     *                        see sort.
3892
     *                        </p>
3893
     *
3894
     * @return static
3895
     *                <p>(Mutable) Return this Arrayy object.</p>
3896
     */
3897 4
    public function rsort(int $sort_flags = 0): self
3898
    {
3899 4
        $this->generatorToArray();
3900
3901 4
        \rsort($this->array, $sort_flags);
3902
3903 4
        return $this;
3904
    }
3905
3906
    /**
3907
     * Search for the first index of the current array via $value.
3908
     *
3909
     * @param mixed $value
3910
     *
3911
     * @return false|float|int|string
3912
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3913
     */
3914 21
    public function searchIndex($value)
3915
    {
3916 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3917 20
            if ($value === $valueFromArray) {
3918 10
                return $keyFromArray;
3919
            }
3920
        }
3921
3922 11
        return false;
3923
    }
3924
3925
    /**
3926
     * Search for the value of the current array via $index.
3927
     *
3928
     * @param mixed $index
3929
     *
3930
     * @return static
3931
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3932
     */
3933 9
    public function searchValue($index): self
3934
    {
3935 9
        $this->generatorToArray();
3936
3937
        // init
3938 9
        $return = [];
3939
3940 9
        if ($this->array === []) {
3941
            return static::create(
3942
                [],
3943
                $this->iteratorClass,
3944
                false
3945
            );
3946
        }
3947
3948
        // php cast "bool"-index into "int"-index
3949 9
        if ((bool) $index === $index) {
3950 1
            $index = (int) $index;
3951
        }
3952
3953 9
        if ($this->offsetExists($index)) {
3954 7
            $return = [$this->array[$index]];
3955
        }
3956
3957 9
        return static::create(
3958 9
            $return,
3959 9
            $this->iteratorClass,
3960 9
            false
3961
        );
3962
    }
3963
3964
    /**
3965
     * Set a value for the current array (optional using dot-notation).
3966
     *
3967
     * @param string $key   <p>The key to set.</p>
3968
     * @param mixed  $value <p>Its value.</p>
3969
     *
3970
     * @return static
3971
     *                <p>(Mutable)</p>
3972
     */
3973 18
    public function set($key, $value): self
3974
    {
3975 18
        $this->generatorToArray();
3976
3977 18
        $this->internalSet($key, $value);
3978
3979 18
        return $this;
3980
    }
3981
3982
    /**
3983
     * Get a value from a array and set it if it was not.
3984
     *
3985
     * WARNING: this method only set the value, if the $key is not already set
3986
     *
3987
     * @param mixed $key      <p>The key</p>
3988
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3989
     *
3990
     * @return mixed
3991
     *               <p>(Mutable)</p>
3992
     */
3993 11
    public function setAndGet($key, $fallback = null)
3994
    {
3995 11
        $this->generatorToArray();
3996
3997
        // If the key doesn't exist, set it.
3998 11
        if (!$this->has($key)) {
3999 4
            $this->array = $this->set($key, $fallback)->getArray();
4000
        }
4001
4002 11
        return $this->get($key);
4003
    }
4004
4005
    /**
4006
     * Shifts a specified value off the beginning of array.
4007
     *
4008
     * @return mixed
4009
     *               <p>(Mutable) A shifted element from the current array.</p>
4010
     */
4011 5
    public function shift()
4012
    {
4013 5
        $this->generatorToArray();
4014
4015 5
        return \array_shift($this->array);
4016
    }
4017
4018
    /**
4019
     * Shuffle the current array.
4020
     *
4021
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
4022
     * @param array $array  [optional]
4023
     *
4024
     * @return static
4025
     *                <p>(Immutable)</p>
4026
     */
4027 2
    public function shuffle(bool $secure = false, array $array = null): self
4028
    {
4029 2
        if ($array === null) {
4030 2
            $array = $this->getArray();
4031
        }
4032
4033 2
        if ($secure !== true) {
4034
            /** @noinspection NonSecureShuffleUsageInspection */
4035 2
            \shuffle($array);
4036
        } else {
4037 1
            $size = \count($array, \COUNT_NORMAL);
4038 1
            $keys = \array_keys($array);
4039 1
            for ($i = $size - 1; $i > 0; --$i) {
4040
                try {
4041 1
                    $r = \random_int(0, $i);
4042
                } catch (\Exception $e) {
4043
                    /** @noinspection RandomApiMigrationInspection */
4044
                    $r = \mt_rand(0, $i);
4045
                }
4046 1
                if ($r !== $i) {
4047
                    $temp = $array[$keys[$r]];
4048
                    $array[$keys[$r]] = $array[$keys[$i]];
4049
                    $array[$keys[$i]] = $temp;
4050
                }
4051
            }
4052
4053
            // reset indices
4054 1
            $array = \array_values($array);
4055
        }
4056
4057 2
        foreach ($array as $key => $value) {
4058
            // check if recursive is needed
4059 2
            if (\is_array($value) === true) {
4060
                $array[$key] = $this->shuffle($secure, $value);
4061
            }
4062
        }
4063
4064 2
        return static::create(
4065 2
            $array,
4066 2
            $this->iteratorClass,
4067 2
            false
4068
        );
4069
    }
4070
4071
    /**
4072
     * Count the values from the current array.
4073
     *
4074
     * alias: for "Arrayy->count()"
4075
     *
4076
     * @param int $mode
4077
     *
4078
     * @return int
4079
     */
4080 20
    public function size(int $mode = \COUNT_NORMAL): int
4081
    {
4082 20
        return $this->count($mode);
4083
    }
4084
4085
    /**
4086
     * Checks whether array has exactly $size items.
4087
     *
4088
     * @param int $size
4089
     *
4090
     * @return bool
4091
     */
4092 1 View Code Duplication
    public function sizeIs(int $size): 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...
4093
    {
4094
        // init
4095 1
        $itemsTempCount = 0;
4096
4097 1
        foreach ($this->getGenerator() as $key => $value) {
4098 1
            ++$itemsTempCount;
4099 1
            if ($itemsTempCount > $size) {
4100 1
                return false;
4101
            }
4102
        }
4103
4104 1
        return $itemsTempCount === $size;
4105
    }
4106
4107
    /**
4108
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4109
     * smaller than $fromSize.
4110
     *
4111
     * @param int $fromSize
4112
     * @param int $toSize
4113
     *
4114
     * @return bool
4115
     */
4116 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4117
    {
4118 1
        if ($fromSize > $toSize) {
4119 1
            $tmp = $toSize;
4120 1
            $toSize = $fromSize;
4121 1
            $fromSize = $tmp;
4122
        }
4123
4124
        // init
4125 1
        $itemsTempCount = 0;
4126
4127 1
        foreach ($this->getGenerator() as $key => $value) {
4128 1
            ++$itemsTempCount;
4129 1
            if ($itemsTempCount > $toSize) {
4130 1
                return false;
4131
            }
4132
        }
4133
4134 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4135
    }
4136
4137
    /**
4138
     * Checks whether array has more than $size items.
4139
     *
4140
     * @param int $size
4141
     *
4142
     * @return bool
4143
     */
4144 1 View Code Duplication
    public function sizeIsGreaterThan(int $size): 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...
4145
    {
4146
        // init
4147 1
        $itemsTempCount = 0;
4148
4149 1
        foreach ($this->getGenerator() as $key => $value) {
4150 1
            ++$itemsTempCount;
4151 1
            if ($itemsTempCount > $size) {
4152 1
                return true;
4153
            }
4154
        }
4155
4156 1
        return $itemsTempCount > $size;
4157
    }
4158
4159
    /**
4160
     * Checks whether array has less than $size items.
4161
     *
4162
     * @param int $size
4163
     *
4164
     * @return bool
4165
     */
4166 1 View Code Duplication
    public function sizeIsLessThan(int $size): 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...
4167
    {
4168
        // init
4169 1
        $itemsTempCount = 0;
4170
4171 1
        foreach ($this->getGenerator() as $key => $value) {
4172 1
            ++$itemsTempCount;
4173 1
            if ($itemsTempCount > $size) {
4174 1
                return false;
4175
            }
4176
        }
4177
4178 1
        return $itemsTempCount < $size;
4179
    }
4180
4181
    /**
4182
     * Counts all elements in an array, or something in an object.
4183
     *
4184
     * <p>
4185
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4186
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4187
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4188
     * implemented and used in PHP.
4189
     * </p>
4190
     *
4191
     * @return int
4192
     *             <p>
4193
     *             The number of elements in var, which is
4194
     *             typically an array, since anything else will have one
4195
     *             element.
4196
     *             </p>
4197
     *             <p>
4198
     *             If var is not an array or an object with
4199
     *             implemented Countable interface,
4200
     *             1 will be returned.
4201
     *             There is one exception, if var is &null;,
4202
     *             0 will be returned.
4203
     *             </p>
4204
     *             <p>
4205
     *             Caution: count may return 0 for a variable that isn't set,
4206
     *             but it may also return 0 for a variable that has been initialized with an
4207
     *             empty array. Use isset to test if a variable is set.
4208
     *             </p>
4209
     */
4210 10
    public function sizeRecursive(): int
4211
    {
4212 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4213
    }
4214
4215
    /**
4216
     * Extract a slice of the array.
4217
     *
4218
     * @param int      $offset       <p>Slice begin index.</p>
4219
     * @param int|null $length       <p>Length of the slice.</p>
4220
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4221
     *
4222
     * @return static
4223
     *                <p>A slice of the original array with length $length.</p>
4224
     */
4225 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
4226
    {
4227 5
        return static::create(
4228 5
            \array_slice(
4229 5
                $this->getArray(),
4230 5
                $offset,
4231 5
                $length,
4232 5
                $preserveKeys
4233
            ),
4234 5
            $this->iteratorClass,
4235 5
            false
4236
        );
4237
    }
4238
4239
    /**
4240
     * Sort the current array and optional you can keep the keys.
4241
     *
4242
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4243
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4244
     *                              <strong>SORT_NATURAL</strong></p>
4245
     * @param bool       $keepKeys
4246
     *
4247
     * @return static
4248
     *                <p>(Mutable) Return this Arrayy object.</p>
4249
     */
4250 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4251
    {
4252 20
        $this->generatorToArray();
4253
4254 20
        return $this->sorting(
4255 20
            $this->array,
4256 20
            $direction,
4257 20
            $strategy,
4258 20
            $keepKeys
4259
        );
4260
    }
4261
4262
    /**
4263
     * Sort the current array by key.
4264
     *
4265
     * @see http://php.net/manual/en/function.ksort.php
4266
     * @see http://php.net/manual/en/function.krsort.php
4267
     *
4268
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4269
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4270
     *                              <strong>SORT_NATURAL</strong></p>
4271
     *
4272
     * @return static
4273
     *                <p>(Mutable) Return this Arrayy object.</p>
4274
     */
4275 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4276
    {
4277 18
        $this->generatorToArray();
4278
4279 18
        $this->sorterKeys($this->array, $direction, $strategy);
4280
4281 18
        return $this;
4282
    }
4283
4284
    /**
4285
     * Sort the current array by value.
4286
     *
4287
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4288
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4289
     *                              <strong>SORT_NATURAL</strong></p>
4290
     *
4291
     * @return static
4292
     *                <p>(Mutable)</p>
4293
     */
4294 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4295
    {
4296 1
        return $this->sort($direction, $strategy, true);
4297
    }
4298
4299
    /**
4300
     * Sort the current array by value.
4301
     *
4302
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4303
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4304
     *                              <strong>SORT_NATURAL</strong></p>
4305
     *
4306
     * @return static
4307
     *                <p>(Mutable)</p>
4308
     */
4309 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4310
    {
4311 1
        return $this->sort($direction, $strategy, false);
4312
    }
4313
4314
    /**
4315
     * Sort a array by value, by a closure or by a property.
4316
     *
4317
     * - If the sorter is null, the array is sorted naturally.
4318
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4319
     *
4320
     * @param callable|string|null $sorter
4321
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4322
     *                                        <strong>SORT_DESC</strong></p>
4323
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4324
     *                                        <strong>SORT_NATURAL</strong></p>
4325
     *
4326
     * @return static
4327
     *                <p>(Immutable)</p>
4328
     */
4329 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4330
    {
4331 1
        $array = $this->getArray();
4332 1
        $direction = $this->getDirection($direction);
4333
4334
        // Transform all values into their results.
4335 1
        if ($sorter) {
4336 1
            $arrayy = static::create(
4337 1
                $array,
4338 1
                $this->iteratorClass,
4339 1
                false
4340
            );
4341
4342 1
            $results = $arrayy->each(
4343
                function ($value) use ($sorter) {
4344 1
                    if (\is_callable($sorter) === true) {
4345 1
                        return $sorter($value);
4346
                    }
4347
4348 1
                    return $this->get($sorter);
4349 1
                }
4350
            );
4351
4352 1
            $results = $results->getArray();
4353
        } else {
4354 1
            $results = $array;
4355
        }
4356
4357
        // Sort by the results and replace by original values
4358 1
        \array_multisort($results, $direction, $strategy, $array);
4359
4360 1
        return static::create(
4361 1
            $array,
4362 1
            $this->iteratorClass,
4363 1
            false
4364
        );
4365
    }
4366
4367
    /**
4368
     * Split an array in the given amount of pieces.
4369
     *
4370
     * @param int  $numberOfPieces
4371
     * @param bool $keepKeys
4372
     *
4373
     * @return static
4374
     *                <p>(Immutable)</p>
4375
     */
4376 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4377
    {
4378 1
        $this->generatorToArray();
4379
4380 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4381
4382 1
        if ($arrayCount === 0) {
4383 1
            $result = [];
4384
        } else {
4385 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4386 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4387
        }
4388
4389 1
        return static::create(
4390 1
            $result,
4391 1
            $this->iteratorClass,
4392 1
            false
4393
        );
4394
    }
4395
4396
    /**
4397
     * Stripe all empty items.
4398
     *
4399
     * @return static
4400
     *                <p>(Immutable)</p>
4401
     */
4402 1
    public function stripEmpty(): self
4403
    {
4404 1
        return $this->filter(
4405
            static function ($item) {
4406 1
                if ($item === null) {
4407 1
                    return false;
4408
                }
4409
4410 1
                return (bool) \trim((string) $item);
4411 1
            }
4412
        );
4413
    }
4414
4415
    /**
4416
     * Swap two values between positions by key.
4417
     *
4418
     * @param int|string $swapA <p>a key in the array</p>
4419
     * @param int|string $swapB <p>a key in the array</p>
4420
     *
4421
     * @return static
4422
     *                <p>(Immutable)</p>
4423
     */
4424 1
    public function swap($swapA, $swapB): self
4425
    {
4426 1
        $array = $this->getArray();
4427
4428 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4429
4430 1
        return static::create(
4431 1
            $array,
4432 1
            $this->iteratorClass,
4433 1
            false
4434
        );
4435
    }
4436
4437
    /**
4438
     * alias: for "Arrayy->getArray()"
4439
     *
4440
     * @see Arrayy::getArray()
4441
     */
4442 235
    public function toArray()
4443
    {
4444 235
        return $this->getArray();
4445
    }
4446
4447
    /**
4448
     * Convert the current array to JSON.
4449
     *
4450
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4451
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4452
     *
4453
     * @return string
4454
     */
4455 7
    public function toJson(int $options = 0, int $depth = 512): string
4456
    {
4457 7
        $return = \json_encode($this->getArray(), $options, $depth);
4458 7
        if ($return === false) {
4459
            return '';
4460
        }
4461
4462 7
        return $return;
4463
    }
4464
4465
    /**
4466
     * Implodes array to a string with specified separator.
4467
     *
4468
     * @param string $separator [optional] <p>The element's separator.</p>
4469
     *
4470
     * @return string
4471
     *                <p>The string representation of array, separated by ",".</p>
4472
     */
4473 20
    public function toString(string $separator = ','): string
4474
    {
4475 20
        return $this->implode($separator);
4476
    }
4477
4478
    /**
4479
     * Return a duplicate free copy of the current array.
4480
     *
4481
     * @return static
4482
     *                <p>(Mutable)</p>
4483
     */
4484 13
    public function unique(): self
4485
    {
4486
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4487
4488 13
        $this->array = $this->reduce(
4489
            static function ($resultArray, $value) {
4490 12
                if (!\in_array($value, $resultArray, true)) {
4491 12
                    $resultArray[] = $value;
4492
                }
4493
4494 12
                return $resultArray;
4495 13
            },
4496 13
            []
4497 13
        )->getArray();
4498 13
        $this->generator = null;
4499
4500 13
        return $this;
4501
    }
4502
4503
    /**
4504
     * Return a duplicate free copy of the current array. (with the old keys)
4505
     *
4506
     * @return static
4507
     *                <p>(Mutable)</p>
4508
     */
4509 11
    public function uniqueKeepIndex(): self
4510
    {
4511
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4512
4513
        // init
4514 11
        $array = $this->getArray();
4515
4516 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...
4517 11
            \array_keys($array),
4518
            static function ($resultArray, $key) use ($array) {
4519 10
                if (!\in_array($array[$key], $resultArray, true)) {
4520 10
                    $resultArray[$key] = $array[$key];
4521
                }
4522
4523 10
                return $resultArray;
4524 11
            },
4525 11
            []
4526
        );
4527 11
        $this->generator = null;
4528
4529 11
        if ($this->array === null) {
4530
            $this->array = [];
4531
        } else {
4532 11
            $this->array = (array) $this->array;
4533
        }
4534
4535 11
        return $this;
4536
    }
4537
4538
    /**
4539
     * alias: for "Arrayy->unique()"
4540
     *
4541
     * @return static
4542
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4543
     *
4544
     * @see Arrayy::unique()
4545
     */
4546 10
    public function uniqueNewIndex(): self
4547
    {
4548 10
        return $this->unique();
4549
    }
4550
4551
    /**
4552
     * Prepends one or more values to the beginning of array at once.
4553
     *
4554
     * @param array ...$args
4555
     *
4556
     * @return static
4557
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4558
     */
4559 4
    public function unshift(...$args): self
4560
    {
4561 4
        $this->generatorToArray();
4562
4563 4
        \array_unshift(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_unshift() as the parameter $array expects a reference.
Loading history...
4564
4565 4
        return $this;
4566
    }
4567
4568
    /**
4569
     * Get all values from a array.
4570
     *
4571
     * @return static
4572
     *                <p>(Immutable)</p>
4573
     */
4574 2
    public function values(): self
4575
    {
4576 2
        return static::create(
4577
            function () {
4578
                /** @noinspection YieldFromCanBeUsedInspection */
4579 2
                foreach ($this->getGenerator() as $value) {
4580 2
                    yield $value;
4581
                }
4582 2
            },
4583 2
            $this->iteratorClass,
4584 2
            false
4585
        );
4586
    }
4587
4588
    /**
4589
     * Apply the given function to every element in the array, discarding the results.
4590
     *
4591
     * @param callable $callable
4592
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4593
     *
4594
     * @return static
4595
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4596
     */
4597 12
    public function walk($callable, bool $recursive = false): self
4598
    {
4599 12
        $this->generatorToArray();
4600
4601 12
        if ($recursive === true) {
4602 6
            \array_walk_recursive($this->array, $callable);
4603
        } else {
4604 6
            \array_walk($this->array, $callable);
4605
        }
4606
4607 12
        return $this;
4608
    }
4609
4610
    /**
4611
     * @param array $data
4612
     * @param bool  $checkPropertiesInConstructor
4613
     */
4614 1048
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
4615
    {
4616 1048
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
4617
                                        &&
4618 1048
                                        $checkPropertiesInConstructor === true;
4619
4620 1048
        if ($this->properties !== []) {
4621 72
            foreach ($data as $key => &$valueInner) {
4622 72
                $this->internalSet(
4623 72
                    $key,
4624 72
                    $valueInner,
4625 72
                    $checkPropertiesInConstructor
4626
                );
4627
            }
4628
        } else {
4629
            if (
4630 986
                $this->checkPropertyTypes === true
4631
                ||
4632 986
                $checkPropertiesInConstructor === true
4633
            ) {
4634 17
                $this->properties = $this->getPropertiesFromPhpDoc();
4635
            }
4636
4637
            if (
4638 986
                $this->checkPropertiesMismatchInConstructor === true
4639
                &&
4640 986
                \count($data) !== 0
4641
                &&
4642 986
                \count(\array_diff_key($this->properties, $data)) > 0
4643
            ) {
4644 1
                throw new \TypeError('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...his->properties), true).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4645
            }
4646
4647 985
            foreach ($data as $key => &$valueInner) {
4648 855
                $this->internalSet(
4649 855
                    $key,
4650 855
                    $valueInner,
4651 855
                    $checkPropertiesInConstructor
4652
                );
4653
            }
4654
        }
4655 1041
    }
4656
4657
    /**
4658
     * Convert an array into a object.
4659
     *
4660
     * @param array $array PHP array
4661
     *
4662
     * @return \stdClass
4663
     */
4664 4
    protected static function arrayToObject(array $array = []): \stdClass
4665
    {
4666
        // init
4667 4
        $object = new \stdClass();
4668
4669 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4670 1
            return $object;
4671
        }
4672
4673 3
        foreach ($array as $name => $value) {
4674 3
            if (\is_array($value) === true) {
4675 1
                $object->{$name} = self::arrayToObject($value);
4676
            } else {
4677 3
                $object->{$name} = $value;
4678
            }
4679
        }
4680
4681 3
        return $object;
4682
    }
4683
4684
    /**
4685
     * @param array|\Generator|null $input         <p>
4686
     *                                             An array containing keys to return.
4687
     *                                             </p>
4688
     * @param mixed|null            $search_values [optional] <p>
4689
     *                                             If specified, then only keys containing these values are returned.
4690
     *                                             </p>
4691
     * @param bool                  $strict        [optional] <p>
4692
     *                                             Determines if strict comparison (===) should be used during the
4693
     *                                             search.
4694
     *                                             </p>
4695
     *
4696
     * @return array
4697
     *               <p>an array of all the keys in input</p>
4698
     */
4699 11
    protected function array_keys_recursive(
4700
        $input = null,
4701
        $search_values = null,
4702
        bool $strict = true
4703
    ): array {
4704
        // init
4705 11
        $keys = [];
4706 11
        $keysTmp = [];
4707
4708 11
        if ($input === null) {
4709 4
            $input = $this->getGenerator();
4710
        }
4711
4712 11
        if ($search_values === null) {
4713 11
            foreach ($input as $key => $value) {
4714 11
                $keys[] = $key;
4715
4716
                // check if recursive is needed
4717 11
                if (\is_array($value) === true) {
4718 4
                    $keysTmp[] = $this->array_keys_recursive($value);
4719
                }
4720
            }
4721
        } else {
4722 1
            $is_array_tmp = \is_array($search_values);
4723
4724 1
            foreach ($input as $key => $value) {
4725 View Code Duplication
                if (
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...
4726
                    (
4727 1
                        $is_array_tmp === false
4728
                        &&
4729 1
                        $strict === true
4730
                        &&
4731 1
                        $search_values === $value
4732
                    )
4733
                    ||
4734
                    (
4735 1
                        $is_array_tmp === false
4736
                        &&
4737 1
                        $strict === false
4738
                        &&
4739 1
                        $search_values == $value
4740
                    )
4741
                    ||
4742
                    (
4743 1
                        $is_array_tmp === true
4744
                        &&
4745 1
                        \in_array($value, $search_values, $strict)
4746
                    )
4747
                ) {
4748 1
                    $keys[] = $key;
4749
                }
4750
4751
                // check if recursive is needed
4752 1
                if (\is_array($value) === true) {
4753 1
                    $keysTmp[] = $this->array_keys_recursive($value);
4754
                }
4755
            }
4756
        }
4757
4758 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
4759
    }
4760
4761
    /**
4762
     * @param mixed      $path
4763
     * @param callable   $callable
4764
     * @param array|null $currentOffset
4765
     */
4766 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4767
    {
4768 4
        $this->generatorToArray();
4769
4770 4
        if ($currentOffset === null) {
4771 4
            $currentOffset = &$this->array;
4772
        }
4773
4774 4
        $explodedPath = \explode($this->pathSeparator, $path);
4775 4
        if ($explodedPath === false) {
4776
            return;
4777
        }
4778
4779 4
        $nextPath = \array_shift($explodedPath);
4780
4781 4
        if (!isset($currentOffset[$nextPath])) {
4782
            return;
4783
        }
4784
4785 4
        if (!empty($explodedPath)) {
4786 1
            $this->callAtPath(
4787 1
                \implode($this->pathSeparator, $explodedPath),
4788 1
                $callable,
4789 1
                $currentOffset[$nextPath]
4790
            );
4791
        } else {
4792 4
            $callable($currentOffset[$nextPath]);
4793
        }
4794 4
    }
4795
4796
    /**
4797
     * create a fallback for array
4798
     *
4799
     * 1. use the current array, if it's a array
4800
     * 2. fallback to empty array, if there is nothing
4801
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4802
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4803
     * 5. call "__toArray()" on object, if the method exists
4804
     * 6. cast a string or object with "__toString()" into an array
4805
     * 7. throw a "InvalidArgumentException"-Exception
4806
     *
4807
     * @param mixed $data
4808
     *
4809
     * @throws \InvalidArgumentException
4810
     *
4811
     * @return array
4812
     */
4813 1050
    protected function fallbackForArray(&$data): array
4814
    {
4815 1050
        $data = $this->internalGetArray($data);
4816
4817 1050
        if ($data === null) {
4818 2
            throw new \InvalidArgumentException('Passed value should be a array');
4819
        }
4820
4821 1048
        return $data;
4822
    }
4823
4824
    /**
4825
     * Get correct PHP constant for direction.
4826
     *
4827
     * @param int|string $direction
4828
     *
4829
     * @return int
4830
     */
4831 39
    protected function getDirection($direction): int
4832
    {
4833 39
        if ((string) $direction === $direction) {
4834 10
            $direction = \strtolower($direction);
4835
4836 10
            if ($direction === 'desc') {
4837 2
                $direction = \SORT_DESC;
4838
            } else {
4839 8
                $direction = \SORT_ASC;
4840
            }
4841
        }
4842
4843
        if (
4844 39
            $direction !== \SORT_DESC
4845
            &&
4846 39
            $direction !== \SORT_ASC
4847
        ) {
4848
            $direction = \SORT_ASC;
4849
        }
4850
4851 39
        return $direction;
4852
    }
4853
4854
    /**
4855
     * @param mixed $glue
4856
     * @param mixed $pieces
4857
     * @param bool  $useKeys
4858
     *
4859
     * @return string
4860
     */
4861 37
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4862
    {
4863 37
        if ($pieces instanceof self) {
4864 1
            $pieces = $pieces->getArray();
4865
        }
4866
4867 37
        if (\is_array($pieces) === true) {
4868 37
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4869 37
            $pieces_count_not_zero = $pieces_count > 0;
4870
4871 37
            return \implode(
4872 37
                $glue,
4873 37
                \array_map(
4874 37
                    [$this, 'implode_recursive'],
4875 37
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4876 37
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4877
                )
4878
            );
4879
        }
4880
4881
        if (
4882 37
            \is_scalar($pieces) === true
4883
            ||
4884 37
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
4885
        ) {
4886 33
            return (string) $pieces;
4887
        }
4888
4889 8
        return '';
4890
    }
4891
4892
    /**
4893
     * @param mixed                 $needle   <p>
4894
     *                                        The searched value.
4895
     *                                        </p>
4896
     *                                        <p>
4897
     *                                        If needle is a string, the comparison is done
4898
     *                                        in a case-sensitive manner.
4899
     *                                        </p>
4900
     * @param array|\Generator|null $haystack <p>
4901
     *                                        The array.
4902
     *                                        </p>
4903
     * @param bool                  $strict   [optional] <p>
4904
     *                                        If the third parameter strict is set to true
4905
     *                                        then the in_array function will also check the
4906
     *                                        types of the
4907
     *                                        needle in the haystack.
4908
     *                                        </p>
4909
     *
4910
     * @return bool
4911
     *              <p>true if needle is found in the array, false otherwise</p>
4912
     */
4913 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4914
    {
4915 18
        if ($haystack === null) {
4916
            $haystack = $this->getGenerator();
4917
        }
4918
4919 18
        foreach ($haystack as $item) {
4920 14
            if (\is_array($item) === true) {
4921 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4922
            } else {
4923
                /** @noinspection NestedPositiveIfStatementsInspection */
4924 14
                if ($strict === true) {
4925 14
                    $returnTmp = $item === $needle;
4926
                } else {
4927
                    $returnTmp = $item == $needle;
4928
                }
4929
            }
4930
4931 14
            if ($returnTmp === true) {
4932 10
                return true;
4933
            }
4934
        }
4935
4936 8
        return false;
4937
    }
4938
4939
    /**
4940
     * @param mixed $data
4941
     *
4942
     * @return array|null
4943
     */
4944 1050
    protected function internalGetArray(&$data)
4945
    {
4946 1050
        if (\is_array($data) === true) {
4947 1047
            return $data;
4948
        }
4949
4950 51
        if (!$data) {
4951 6
            return [];
4952
        }
4953
4954 50
        if (\is_object($data) === true) {
4955 45
            if ($data instanceof \ArrayObject) {
4956 4
                return $data->getArrayCopy();
4957
            }
4958
4959 42
            if ($data instanceof \Generator) {
4960
                return static::createFromGeneratorImmutable($data)->getArray();
4961
            }
4962
4963 42
            if ($data instanceof \Traversable) {
4964
                return static::createFromObject($data)->getArray();
4965
            }
4966
4967 42
            if ($data 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...
4968
                return (array) $data->jsonSerialize();
4969
            }
4970
4971 42
            if (\method_exists($data, '__toArray')) {
4972
                return (array) $data->__toArray();
4973
            }
4974
4975 42
            if (\method_exists($data, '__toString')) {
4976
                return [(string) $data];
4977
            }
4978
        }
4979
4980 47
        if (\is_callable($data)) {
4981 40
            $this->generator = new ArrayyRewindableGenerator($data);
4982
4983 40
            return [];
4984
        }
4985
4986 9
        if (\is_scalar($data)) {
4987 7
            return [$data];
4988
        }
4989
4990 2
        return null;
4991
    }
4992
4993
    /**
4994
     * Internal mechanics of remove method.
4995
     *
4996
     * @param mixed $key
4997
     *
4998
     * @return bool
4999
     */
5000 18
    protected function internalRemove($key): bool
5001
    {
5002 18
        $this->generatorToArray();
5003
5004
        if (
5005 18
            $this->pathSeparator
5006
            &&
5007 18
            (string) $key === $key
5008
            &&
5009 18
            \strpos($key, $this->pathSeparator) !== false
5010
        ) {
5011
            $path = \explode($this->pathSeparator, (string) $key);
5012
5013
            if ($path !== false) {
5014
                // crawl though the keys
5015
                while (\count($path, \COUNT_NORMAL) > 1) {
5016
                    $key = \array_shift($path);
5017
5018
                    if (!$this->has($key)) {
5019
                        return false;
5020
                    }
5021
5022
                    $this->array = &$this->array[$key];
5023
                }
5024
5025
                $key = \array_shift($path);
5026
            }
5027
        }
5028
5029 18
        unset($this->array[$key]);
5030
5031 18
        return true;
5032
    }
5033
5034
    /**
5035
     * Internal mechanic of set method.
5036
     *
5037
     * @param int|string|null $key
5038
     * @param mixed           $value
5039
     * @param bool            $checkProperties
5040
     *
5041
     * @return bool
5042
     */
5043 924
    protected function internalSet($key, &$value, $checkProperties = true): bool
5044
    {
5045
        if (
5046 924
            $checkProperties === true
5047
            &&
5048 924
            $this->properties !== []
5049
        ) {
5050 81
            $this->checkType($key, $value);
5051
        }
5052
5053 922
        if ($key === null) {
5054
            return false;
5055
        }
5056
5057 922
        $this->generatorToArray();
5058
5059 922
        $array = &$this->array;
5060
5061
        if (
5062 922
            $this->pathSeparator
5063
            &&
5064 922
            (string) $key === $key
5065
            &&
5066 922
            \strpos($key, $this->pathSeparator) !== false
5067
        ) {
5068 3
            $path = \explode($this->pathSeparator, (string) $key);
5069
5070 3
            if ($path !== false) {
5071
                // crawl through the keys
5072 3
                while (\count($path, \COUNT_NORMAL) > 1) {
5073 3
                    $key = \array_shift($path);
5074
5075 3
                    $array = &$array[$key];
5076
                }
5077
5078 3
                $key = \array_shift($path);
5079
            }
5080
        }
5081
5082 922
        $array[$key] = $value;
5083
5084 922
        return true;
5085
    }
5086
5087
    /**
5088
     * Convert a object into an array.
5089
     *
5090
     * @param object $object
5091
     *
5092
     * @return mixed
5093
     */
5094 5
    protected static function objectToArray($object)
5095
    {
5096 5
        if (!\is_object($object)) {
5097 4
            return $object;
5098
        }
5099
5100 5
        if (\is_object($object)) {
5101 5
            $object = \get_object_vars($object);
5102
        }
5103
5104 5
        return \array_map(['static', 'objectToArray'], $object);
5105
    }
5106
5107
    /**
5108
     * sorting keys
5109
     *
5110
     * @param array      $elements
5111
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5112
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5113
     *                              <strong>SORT_NATURAL</strong></p>
5114
     *
5115
     * @return static
5116
     *                <p>(Mutable) Return this Arrayy object.</p>
5117
     */
5118 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5119
    {
5120 18
        $direction = $this->getDirection($direction);
5121
5122 18
        switch ($direction) {
5123 18
            case 'desc':
5124
            case \SORT_DESC:
5125 6
                \krsort($elements, $strategy);
5126
5127 6
                break;
5128 13
            case 'asc':
5129 13
            case \SORT_ASC:
5130
            default:
5131 13
                \ksort($elements, $strategy);
5132
        }
5133
5134 18
        return $this;
5135
    }
5136
5137
    /**
5138
     * @param array      $elements  <p>Warning: used as reference</p>
5139
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5140
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5141
     *                              <strong>SORT_NATURAL</strong></p>
5142
     * @param bool       $keepKeys
5143
     *
5144
     * @return static
5145
     *                <p>(Mutable) Return this Arrayy object.</p>
5146
     */
5147 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
5148
    {
5149 20
        $direction = $this->getDirection($direction);
5150
5151 20
        if (!$strategy) {
5152 20
            $strategy = \SORT_REGULAR;
5153
        }
5154
5155 20
        switch ($direction) {
5156 20
            case 'desc':
5157
            case \SORT_DESC:
5158 9
                if ($keepKeys) {
5159 5
                    \arsort($elements, $strategy);
5160
                } else {
5161 4
                    \rsort($elements, $strategy);
5162
                }
5163
5164 9
                break;
5165 11
            case 'asc':
5166 11
            case \SORT_ASC:
5167
            default:
5168 11
                if ($keepKeys) {
5169 4
                    \asort($elements, $strategy);
5170
                } else {
5171 7
                    \sort($elements, $strategy);
5172
                }
5173
        }
5174
5175 20
        return $this;
5176
    }
5177
5178
    /**
5179
     * @return TypeCheckPhpDoc[]
5180
     *
5181
     * @noinspection ReturnTypeCanBeDeclaredInspection
5182
     */
5183 17
    protected function getPropertiesFromPhpDoc()
5184
    {
5185 17
        static $PROPERTY_CACHE = [];
5186 17
        $cacheKey = 'Class::' . static::class;
5187
5188 17
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5189 15
            return $PROPERTY_CACHE[$cacheKey];
5190
        }
5191
5192
        // init
5193 3
        $properties = [];
5194
5195 3
        $reflector = new \ReflectionClass($this);
5196 3
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5197 3
        $docComment = $reflector->getDocComment();
5198 3
        if ($docComment) {
5199 2
            $docblock = $factory->create($docComment);
5200
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
5201 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5202 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
5203
            }
5204
        }
5205
5206 3
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5207
    }
5208
5209
    /**
5210
     * @return bool
5211
     *
5212
     * @noinspection ReturnTypeCanBeDeclaredInspection
5213
     */
5214 967
    protected function generatorToArray()
5215
    {
5216 967
        if ($this->generator) {
5217 2
            $this->array = $this->getArray();
5218 2
            $this->generator = null;
5219
5220 2
            return true;
5221
        }
5222
5223 967
        return false;
5224
    }
5225
5226
    /**
5227
     * @param int|string|null $key
5228
     * @param mixed           $value
5229
     */
5230 81
    private function checkType($key, $value)
5231
    {
5232
        if (
5233 81
            isset($this->properties[$key]) === false
5234
            &&
5235 81
            $this->checkPropertiesMismatch === true
5236
        ) {
5237
            throw new \TypeError('The key ' . $key . ' does not exists in "properties". Maybe because @property was not used for the class (' . \get_class($this) . ').');
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'The key ' . $key . ' do...get_class($this) . ').'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5238
        }
5239
5240 81
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
5241 70
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
5242 17
        } elseif (isset($this->properties[$key])) {
5243 17
            $this->properties[$key]->checkType($value);
5244
        }
5245 79
    }
5246
}
5247