Completed
Push — master ( 4690a7...753d16 )
by Lars
01:54
created

Arrayy::values()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 1
nop 0
dl 0
loc 13
ccs 7
cts 7
cp 1
crap 2
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
/** @noinspection ClassReImplementsParentInterfaceInspection */
8
9
/**
10
 * Methods to manage arrays.
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
16
{
17
    /**
18
     * @var array
19
     */
20
    protected $array = [];
21
22
    /**
23
     * @var ArrayyRewindableGenerator|null
24
     */
25
    protected $generator;
26
27
    /**
28
     * @var string
29
     */
30
    protected $iteratorClass = ArrayyIterator::class;
31
32
    /**
33
     * @var string
34
     */
35
    protected $pathSeparator = '.';
36
37
    /**
38
     * @var bool
39
     */
40
    protected $checkPropertyTypes = false;
41
42
    /**
43
     * @var bool
44
     */
45
    protected $checkForMissingPropertiesInConstructor = false;
46
47
    /**
48
     * @var bool
49
     */
50
    protected $checkPropertiesMismatchInConstructor = false;
51
52
    /**
53
     * @var Property[]
54
     */
55
    protected $properties = [];
56
57
    /**
58
     * Initializes
59
     *
60
     * @param mixed  $data                                   <p>
61
     *                                                       Should be an array or a generator, otherwise it will try
62
     *                                                       to convert it into an array.
63
     *                                                       </p>
64
     * @param string $iteratorClass                          optional <p>
65
     *                                                       You can overwrite the ArrayyIterator, but mostly you don't
66
     *                                                       need this option.
67
     *                                                       </p>
68
     * @param bool   $checkForMissingPropertiesInConstructor optional <p>
69
     *                                                       You need to extend the "Arrayy"-class and you need to set
70
     *                                                       the $checkPropertiesMismatchInConstructor class property
71
     *                                                       to
72
     *                                                       true, otherwise this option didn't not work anyway.
73
     *                                                       </p>
74
     */
75 991
    public function __construct(
76
        $data = [],
77
        string $iteratorClass = ArrayyIterator::class,
78
        bool $checkForMissingPropertiesInConstructor = true
79
    ) {
80 991
        $data = $this->fallbackForArray($data);
81
82
        // used only for serialize + unserialize, all other methods are overwritten
83 989
        parent::__construct([], 0, $iteratorClass);
84
85 989
        $checkForMissingPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
86
                                                  &&
87 989
                                                  $checkForMissingPropertiesInConstructor === true;
88
89
        if (
90 989
            $this->checkPropertyTypes === true
91
            ||
92 989
            $checkForMissingPropertiesInConstructor === true
93
        ) {
94 15
            $this->properties = $this->getPropertiesFromPhpDoc();
95
        }
96
97
        if (
98 989
            $this->checkPropertiesMismatchInConstructor === true
99
            &&
100 989
            \count($data) !== 0
101
            &&
102 989
            \count(\array_diff_key($this->properties, $data)) > 0
103
        ) {
104 1
            throw new \InvalidArgumentException('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
105
        }
106
107
        /** @noinspection AlterInForeachInspection */
108 988
        foreach ($data as $key => &$value) {
109 860
            $this->internalSet(
110 860
                $key,
111 860
                $value,
112 860
                $checkForMissingPropertiesInConstructor
113
            );
114
        }
115
116 984
        $this->setIteratorClass($iteratorClass);
117 984
    }
118
119
    /**
120
     * Call object as function.
121
     *
122
     * @param mixed $key
123
     *
124
     * @return mixed
125
     */
126 1
    public function __invoke($key = null)
127
    {
128 1
        if ($key !== null) {
129 1
            $this->generatorToArray();
130
131 1
            return $this->array[$key] ?? false;
132
        }
133
134
        return $this->getArray();
135
    }
136
137
    /**
138
     * Whether or not an element exists by key.
139
     *
140
     * @param mixed $key
141
     *
142
     * @return bool
143
     *              <p>True is the key/index exists, otherwise false.</p>
144
     */
145
    public function __isset($key): bool
146
    {
147
        return $this->offsetExists($key);
148
    }
149
150
    /**
151
     * Assigns a value to the specified element.
152
     *
153
     * @param mixed $key
154
     * @param mixed $value
155
     */
156 2
    public function __set($key, $value)
157
    {
158 2
        $this->internalSet($key, $value);
159 2
    }
160
161
    /**
162
     * magic to string
163
     *
164
     * @return string
165
     */
166 16
    public function __toString(): string
167
    {
168 16
        return $this->toString();
169
    }
170
171
    /**
172
     * Unset element by key.
173
     *
174
     * @param mixed $key
175
     */
176
    public function __unset($key)
177
    {
178
        $this->internalRemove($key);
179
    }
180
181
    /**
182
     * Get a value by key.
183
     *
184
     * @param mixed $key
185
     *
186
     * @return mixed
187
     *               <p>Get a Value from the current array.</p>
188
     */
189 4
    public function &__get($key)
190
    {
191 4
        $return = $this->get($key);
192
193 4
        if (\is_array($return)) {
194
            return static::create($return, $this->iteratorClass, false);
195
        }
196
197 4
        return $return;
198
    }
199
200
    /**
201
     * alias: for "Arrayy->append()"
202
     *
203
     * @param mixed $value
204
     *
205
     * @return static
206
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
207
     *
208
     * @see Arrayy::append()
209
     */
210 3
    public function add($value): self
211
    {
212 3
        return $this->append($value);
213
    }
214
215
    /**
216
     * Append a (key) + value to the current array.
217
     *
218
     * @param mixed $value
219
     * @param mixed $key
220
     *
221
     * @return static
222
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
223
     */
224 12
    public function append($value, $key = null): self
225
    {
226 12
        $this->generatorToArray();
227
228 12
        if ($key !== null) {
229
            if (
230
                isset($this->array[$key])
231
                &&
232
                \is_array($this->array[$key])
233
            ) {
234
                $this->array[$key][] = $value;
235
            } else {
236
                $this->array[$key] = $value;
237
            }
238
        } else {
239 12
            $this->array[] = $value;
240
        }
241
242 12
        return $this;
243
    }
244
245
    /**
246
     * Sort the entries by value.
247
     *
248
     * @param int $sort_flags [optional] <p>
249
     *                        You may modify the behavior of the sort using the optional
250
     *                        parameter sort_flags, for details
251
     *                        see sort.
252
     *                        </p>
253
     *
254
     * @return static
255
     *                <p>(Mutable) Return this Arrayy object.</p>
256
     */
257 4
    public function asort(int $sort_flags = 0): self
258
    {
259 4
        $this->generatorToArray();
260
261 4
        \asort($this->array, $sort_flags);
262
263 4
        return $this;
264
    }
265
266
    /**
267
     * Counts all elements in an array, or something in an object.
268
     *
269
     * <p>
270
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
271
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
272
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
273
     * implemented and used in PHP.
274
     * </p>
275
     *
276
     * @see http://php.net/manual/en/function.count.php
277
     *
278
     * @param int $mode [optional] If the optional mode parameter is set to
279
     *                  COUNT_RECURSIVE (or 1), count
280
     *                  will recursively count the array. This is particularly useful for
281
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
282
     *
283
     * @return int
284
     *             <p>
285
     *             The number of elements in var, which is
286
     *             typically an array, since anything else will have one
287
     *             element.
288
     *             </p>
289
     *             <p>
290
     *             If var is not an array or an object with
291
     *             implemented Countable interface,
292
     *             1 will be returned.
293
     *             There is one exception, if var is &null;,
294
     *             0 will be returned.
295
     *             </p>
296
     *             <p>
297
     *             Caution: count may return 0 for a variable that isn't set,
298
     *             but it may also return 0 for a variable that has been initialized with an
299
     *             empty array. Use isset to test if a variable is set.
300
     *             </p>
301
     */
302 49
    public function count(int $mode = \COUNT_NORMAL): int
303
    {
304 49
        return \count($this->getArray(), $mode);
305
    }
306
307
    /**
308
     * Exchange the array for another one.
309
     *
310
     * @param array|static $data
311
     *
312
     * @return array
313
     */
314 1
    public function exchangeArray($data): array
315
    {
316 1
        $this->array = $this->fallbackForArray($data);
317
318 1
        return $this->array;
319
    }
320
321
    /**
322
     * Creates a copy of the ArrayyObject.
323
     *
324
     * @return array
325
     */
326 1
    public function getArrayCopy(): array
327
    {
328 1
        $this->generatorToArray();
329
330 1
        return $this->array;
331
    }
332
333
    /**
334
     * Returns a new iterator, thus implementing the \Iterator interface.
335
     *
336
     * @return \Iterator
337
     *                   <p>An iterator for the values in the array.</p>
338
     */
339 24
    public function getIterator(): \Iterator
340
    {
341 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
342 1
            return $this->generator;
343
        }
344
345 23
        $iterator = $this->getIteratorClass();
346
347 23
        if ($iterator === ArrayyIterator::class) {
348 23
            return new $iterator($this->getArray(), 0, static::class);
349
        }
350
351
        return new $iterator($this->getArray());
352
    }
353
354
    /**
355
     * Gets the iterator classname for the ArrayObject.
356
     *
357
     * @return string
358
     */
359 23
    public function getIteratorClass(): string
360
    {
361 23
        return $this->iteratorClass;
362
    }
363
364
    /**
365
     * Sort the entries by key
366
     *
367
     * @param int $sort_flags [optional] <p>
368
     *                        You may modify the behavior of the sort using the optional
369
     *                        parameter sort_flags, for details
370
     *                        see sort.
371
     *                        </p>
372
     *
373
     * @return static
374
     *                <p>(Mutable) Return this Arrayy object.</p>
375
     */
376 4
    public function ksort(int $sort_flags = 0): self
377
    {
378 4
        $this->generatorToArray();
379
380 4
        \ksort($this->array, $sort_flags);
381
382 4
        return $this;
383
    }
384
385
    /**
386
     * Sort an array using a case insensitive "natural order" algorithm
387
     *
388
     * @return static
389
     *                <p>(Mutable) Return this Arrayy object.</p>
390
     */
391
    public function natcasesort(): self
392
    {
393
        $this->generatorToArray();
394
395
        \natcasesort($this->array);
396
397
        return $this;
398
    }
399
400
    /**
401
     * Sort entries using a "natural order" algorithm
402
     *
403
     * @return static
404
     *                <p>(Mutable) Return this Arrayy object.</p>
405
     */
406 1
    public function natsort(): self
407
    {
408 1
        $this->generatorToArray();
409
410 1
        \natsort($this->array);
411
412 1
        return $this;
413
    }
414
415
    /**
416
     * Whether or not an offset exists.
417
     *
418
     * @param bool|int|string $offset
419
     *
420
     * @return bool
421
     */
422 61
    public function offsetExists($offset): bool
423
    {
424 61
        $this->generatorToArray();
425
426 61
        if ($this->array === []) {
427 5
            return false;
428
        }
429
430
        // php cast "bool"-index into "int"-index
431 56
        if (\is_bool($offset)) {
432 1
            $offset = (int) $offset;
433
        }
434
435 56
        $tmpReturn = $this->keyExists($offset);
436
437
        if (
438 56
            $tmpReturn === true
439
            ||
440
            (
441 21
                $tmpReturn === false
442
                &&
443 56
                \strpos((string) $offset, $this->pathSeparator) === false
444
            )
445
        ) {
446 54
            return $tmpReturn;
447
        }
448
449 3
        $offsetExists = false;
450
451
        if (
452 3
            $this->pathSeparator
453
            &&
454 3
            \is_string($offset)
455
            &&
456 3
            \strpos($offset, $this->pathSeparator) !== false
457
        ) {
458 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
459 3
            if ($explodedPath !== false) {
460 3
                $lastOffset = \array_pop($explodedPath);
461 3
                if ($lastOffset !== null) {
462 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
463
464 3
                    $this->callAtPath(
465 3
                        $containerPath,
466
                        static function ($container) use ($lastOffset, &$offsetExists) {
467 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
468 3
                        }
469
                    );
470
                }
471
            }
472
        }
473
474 3
        return $offsetExists;
475
    }
476
477
    /**
478
     * Returns the value at specified offset.
479
     *
480
     * @param int|string $offset
481
     *
482
     * @return mixed
483
     *               <p>Will return null if the offset did not exists.</p>
484
     */
485 30
    public function offsetGet($offset)
486
    {
487 30
        return $this->offsetExists($offset) ? $this->get($offset) : null;
488
    }
489
490
    /**
491
     * Assigns a value to the specified offset.
492
     *
493
     * @param int|string|null $offset
494
     * @param mixed           $value
495
     */
496 20
    public function offsetSet($offset, $value)
497
    {
498 20
        $this->generatorToArray();
499
500 20
        if ($offset === null) {
501 4
            $this->array[] = $value;
502
        } else {
503 16
            $this->internalSet($offset, $value);
504
        }
505 20
    }
506
507
    /**
508
     * Unset an offset.
509
     *
510
     * @param int|string $offset
511
     */
512 12
    public function offsetUnset($offset)
513
    {
514 12
        $this->generatorToArray();
515
516 12
        if ($this->array === []) {
517 3
            return;
518
        }
519
520 10
        if ($this->keyExists($offset)) {
521 7
            unset($this->array[$offset]);
522
523 7
            return;
524
        }
525
526
        if (
527 5
            $this->pathSeparator
528
            &&
529 5
            \is_string($offset)
530
            &&
531 5
            \strpos($offset, $this->pathSeparator) !== false
532
        ) {
533 2
            $path = \explode($this->pathSeparator, (string) $offset);
534
535 2
            if ($path !== false) {
536 2
                $pathToUnset = \array_pop($path);
537
538 2
                $this->callAtPath(
539 2
                    \implode($this->pathSeparator, $path),
540
                    static function (&$offset) use ($pathToUnset) {
541 2
                        unset($offset[$pathToUnset]);
542 2
                    }
543
                );
544
            }
545
        }
546
547 5
        unset($this->array[$offset]);
548 5
    }
549
550
    /**
551
     * Serialize the current "Arrayy"-object.
552
     *
553
     * @return string
554
     */
555 2
    public function serialize(): string
556
    {
557 2
        $this->generatorToArray();
558
559 2
        return parent::serialize();
560
    }
561
562
    /**
563
     * Sets the iterator classname for the current "Arrayy"-object.
564
     *
565
     * @param string $class
566
     *
567
     * @throws \InvalidArgumentException
568
     */
569 984
    public function setIteratorClass($class)
570
    {
571 984
        if (\class_exists($class)) {
572 984
            $this->iteratorClass = $class;
573
574 984
            return;
575
        }
576
577
        if (\strpos($class, '\\') === 0) {
578
            $class = '\\' . $class;
579
            if (\class_exists($class)) {
580
                $this->iteratorClass = $class;
581
582
                return;
583
            }
584
        }
585
586
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
587
    }
588
589
    /**
590
     * Sort the entries with a user-defined comparison function and maintain key association.
591
     *
592
     * @param callable $function
593
     *
594
     * @throws \InvalidArgumentException
595
     *
596
     * @return static
597
     *                <p>(Mutable) Return this Arrayy object.</p>
598
     */
599 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...
600
    {
601
        if (!\is_callable($function)) {
602
            throw new \InvalidArgumentException(
603
                'Passed function must be callable'
604
            );
605
        }
606
607
        $this->generatorToArray();
608
609
        \uasort($this->array, $function);
610
611
        return $this;
612
    }
613
614
    /**
615
     * Sort the entries by keys using a user-defined comparison function.
616
     *
617
     * @param callable $function
618
     *
619
     * @throws \InvalidArgumentException
620
     *
621
     * @return static
622
     *                <p>(Mutable) Return this Arrayy object.</p>
623
     */
624 5
    public function uksort($function): self
625
    {
626 5
        return $this->customSortKeys($function);
627
    }
628
629
    /**
630
     * Unserialize an string and return the instance of the "Arrayy"-class.
631
     *
632
     * @param string $string
633
     *
634
     * @return static
635
     */
636 2
    public function unserialize($string): self
637
    {
638 2
        parent::unserialize($string);
639
640 2
        return $this;
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])
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)) {
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)) {
724
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
725 9
            } elseif (\is_object($item)) {
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 (!\is_int($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 4
    public function chunk($size, $preserveKeys = false): self
860
    {
861 4
        return static::create(
862 4
            \array_chunk($this->getArray(), $size, $preserveKeys),
863 4
            $this->iteratorClass,
864 4
            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 22
    public function contains($value, bool $recursive = false, bool $strict = true): bool
907
    {
908 22
        if ($recursive === true) {
909 18
            return $this->in_array_recursive($value, $this->getArray(), $strict);
910
        }
911
912 13
        foreach ($this->getGenerator() as $valueFromArray) {
913 10
            if ($strict) {
914 10
                if ($value === $valueFromArray) {
915 10
                    return true;
916
                }
917
            } else {
918
                /** @noinspection NestedPositiveIfStatementsInspection */
919
                if ($value == $valueFromArray) {
920
                    return true;
921
                }
922
            }
923
        }
924
925 6
        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)) {
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 new static(\array_count_values($this->getArray()));
1084
    }
1085
1086
    /**
1087
     * Creates an Arrayy object.
1088
     *
1089
     * @param mixed  $array
1090
     * @param string $iteratorClass
1091
     * @param bool   $checkForMissingPropertiesInConstructor
1092
     *
1093
     * @return static
1094
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1095
     */
1096 581
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1097
    {
1098 581
        return new static(
1099 581
            $array,
1100 581
            $iteratorClass,
1101 581
            $checkForMissingPropertiesInConstructor
1102
        );
1103
    }
1104
1105
    /**
1106
     * WARNING: Creates an Arrayy object by reference.
1107
     *
1108
     * @param array $array
1109
     *
1110
     * @return static
1111
     *                <p>(Mutable) Return this Arrayy object.</p>
1112
     */
1113 1
    public function createByReference(array &$array = []): self
1114
    {
1115 1
        $array = $this->fallbackForArray($array);
1116
1117 1
        $this->array = &$array;
1118
1119 1
        return $this;
1120
    }
1121
1122
    /**
1123
     * Create an new instance from a callable function which will return an Generator.
1124
     *
1125
     * @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...
1126
     *
1127
     * @return static
1128
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1129
     */
1130 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1131
    {
1132 5
        $arrayy = new static($generatorFunction);
1133
1134 5
        return $arrayy;
1135
    }
1136
1137
    /**
1138
     * Create an new instance filled with a copy of values from a "Generator"-object.
1139
     *
1140
     * @param \Generator $generator
1141
     *
1142
     * @return static
1143
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1144
     */
1145 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1146
    {
1147 4
        return new static(\iterator_to_array($generator, true));
1148
    }
1149
1150
    /**
1151
     * Create an new Arrayy object via JSON.
1152
     *
1153
     * @param string $json
1154
     *
1155
     * @return static
1156
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1157
     */
1158 5
    public static function createFromJson(string $json): self
1159
    {
1160 5
        return static::create(\json_decode($json, true));
1161
    }
1162
1163
    /**
1164
     * Create an new instance filled with values from an object that is iterable.
1165
     *
1166
     * @param \Traversable $object <p>iterable object</p>
1167
     *
1168
     * @return static
1169
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1170
     */
1171 4
    public static function createFromObject(\Traversable $object): self
1172
    {
1173
        // init
1174 4
        $array = new static();
1175
1176 4
        if ($object instanceof self) {
1177 4
            $objectArray = $object->getGenerator();
1178
        } else {
1179
            $objectArray = $object;
1180
        }
1181
1182 4
        foreach ($objectArray as $key => $value) {
1183 3
            $array[$key] = $value;
1184
        }
1185
1186 4
        return $array;
1187
    }
1188
1189
    /**
1190
     * Create an new instance filled with values from an object.
1191
     *
1192
     * @param object $object
1193
     *
1194
     * @return static
1195
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1196
     */
1197 5
    public static function createFromObjectVars($object): self
1198
    {
1199 5
        return new static(self::objectToArray($object));
1200
    }
1201
1202
    /**
1203
     * Create an new Arrayy object via string.
1204
     *
1205
     * @param string      $str       <p>The input string.</p>
1206
     * @param string|null $delimiter <p>The boundary string.</p>
1207
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1208
     *                               used.</p>
1209
     *
1210
     * @return static
1211
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1212
     */
1213 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1214
    {
1215 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...
1216 1
            \preg_match_all($regEx, $str, $array);
1217
1218 1
            if (!empty($array)) {
1219 1
                $array = $array[0];
1220
            }
1221
        } else {
1222
            /** @noinspection NestedPositiveIfStatementsInspection */
1223 9
            if ($delimiter !== null) {
1224 7
                $array = \explode($delimiter, $str);
1225
            } else {
1226 2
                $array = [$str];
1227
            }
1228
        }
1229
1230
        // trim all string in the array
1231 10
        \array_walk(
1232 10
            $array,
1233
            static function (&$val) {
1234 10
                if (\is_string($val)) {
1235 10
                    $val = \trim($val);
1236
                }
1237 10
            }
1238
        );
1239
1240 10
        return static::create($array);
1241
    }
1242
1243
    /**
1244
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1245
     *
1246
     * @param \Traversable $traversable
1247
     *
1248
     * @return static
1249
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1250
     */
1251 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1252
    {
1253 1
        return new static(\iterator_to_array($traversable, true));
1254
    }
1255
1256
    /**
1257
     * Create an new instance containing a range of elements.
1258
     *
1259
     * @param mixed $low  <p>First value of the sequence.</p>
1260
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1261
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1262
     *
1263
     * @return static
1264
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1265
     */
1266 2
    public static function createWithRange($low, $high, int $step = 1): self
1267
    {
1268 2
        return static::create(\range($low, $high, $step));
1269
    }
1270
1271
    /**
1272
     * Custom sort by index via "uksort".
1273
     *
1274
     * @see http://php.net/manual/en/function.uksort.php
1275
     *
1276
     * @param callable $function
1277
     *
1278
     * @throws \InvalidArgumentException
1279
     *
1280
     * @return static
1281
     *                <p>(Mutable) Return this Arrayy object.</p>
1282
     */
1283 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...
1284
    {
1285 5
        if (!\is_callable($function)) {
1286
            throw new \InvalidArgumentException(
1287
                'Passed function must be callable'
1288
            );
1289
        }
1290
1291 5
        $this->generatorToArray();
1292
1293 5
        \uksort($this->array, $function);
1294
1295 5
        return $this;
1296
    }
1297
1298
    /**
1299
     * Custom sort by value via "usort".
1300
     *
1301
     * @see http://php.net/manual/en/function.usort.php
1302
     *
1303
     * @param callable $function
1304
     *
1305
     * @throws \InvalidArgumentException
1306
     *
1307
     * @return static
1308
     *                <p>(Mutable) Return this Arrayy object.</p>
1309
     */
1310 5 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...
1311
    {
1312 5
        if (!\is_callable($function)) {
1313
            throw new \InvalidArgumentException(
1314
                'Passed function must be callable'
1315
            );
1316
        }
1317
1318 5
        $this->generatorToArray();
1319
1320 5
        \usort($this->array, $function);
1321
1322 5
        return $this;
1323
    }
1324
1325
    /**
1326
     * Delete the given key or keys.
1327
     *
1328
     * @param int|int[]|string|string[] $keyOrKeys
1329
     */
1330 4
    public function delete($keyOrKeys)
1331
    {
1332 4
        $keyOrKeys = (array) $keyOrKeys;
1333
1334 4
        foreach ($keyOrKeys as $key) {
1335 4
            $this->offsetUnset($key);
1336
        }
1337 4
    }
1338
1339
    /**
1340
     * Return values that are only in the current array.
1341
     *
1342
     * @param array $array
1343
     *
1344
     * @return static
1345
     *                <p>(Immutable)</p>
1346
     */
1347 12
    public function diff(array $array = []): self
1348
    {
1349 12
        return static::create(
1350 12
            \array_diff($this->getArray(), $array),
1351 12
            $this->iteratorClass,
1352 12
            false
1353
        );
1354
    }
1355
1356
    /**
1357
     * Return values that are only in the current array.
1358
     *
1359
     * @param array $array
1360
     *
1361
     * @return static
1362
     *                <p>(Immutable)</p>
1363
     */
1364 8
    public function diffKey(array $array = []): self
1365
    {
1366 8
        return static::create(
1367 8
            \array_diff_key($this->getArray(), $array),
1368 8
            $this->iteratorClass,
1369 8
            false
1370
        );
1371
    }
1372
1373
    /**
1374
     * Return values and Keys that are only in the current array.
1375
     *
1376
     * @param array $array
1377
     *
1378
     * @return static
1379
     *                <p>(Immutable)</p>
1380
     */
1381 8
    public function diffKeyAndValue(array $array = []): self
1382
    {
1383 8
        return static::create(
1384 8
            \array_diff_assoc($this->getArray(), $array),
1385 8
            $this->iteratorClass,
1386 8
            false
1387
        );
1388
    }
1389
1390
    /**
1391
     * Return values that are only in the current multi-dimensional array.
1392
     *
1393
     * @param array      $array
1394
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1395
     *
1396
     * @return static
1397
     *                <p>(Immutable)</p>
1398
     */
1399 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1400
    {
1401
        // init
1402 1
        $result = [];
1403
1404
        if (
1405 1
            $helperVariableForRecursion !== null
1406
            &&
1407 1
            \is_array($helperVariableForRecursion)
1408
        ) {
1409
            $arrayForTheLoop = $helperVariableForRecursion;
1410
        } else {
1411 1
            $arrayForTheLoop = $this->getGenerator();
1412
        }
1413
1414 1
        foreach ($arrayForTheLoop as $key => $value) {
1415 1
            if ($value instanceof self) {
1416
                $value = $value->getArray();
1417
            }
1418
1419 1
            if (\array_key_exists($key, $array)) {
1420 1
                if ($value !== $array[$key]) {
1421 1
                    $result[$key] = $value;
1422
                }
1423
            } else {
1424 1
                $result[$key] = $value;
1425
            }
1426
        }
1427
1428 1
        return static::create(
1429 1
            $result,
1430 1
            $this->iteratorClass,
1431 1
            false
1432
        );
1433
    }
1434
1435
    /**
1436
     * Return values that are only in the new $array.
1437
     *
1438
     * @param array $array
1439
     *
1440
     * @return static
1441
     *                <p>(Immutable)</p>
1442
     */
1443 8
    public function diffReverse(array $array = []): self
1444
    {
1445 8
        return static::create(
1446 8
            \array_diff($array, $this->getArray()),
1447 8
            $this->iteratorClass,
1448 8
            false
1449
        );
1450
    }
1451
1452
    /**
1453
     * Divide an array into two arrays. One with keys and the other with values.
1454
     *
1455
     * @return static
1456
     *                <p>(Immutable)</p>
1457
     */
1458 1
    public function divide(): self
1459
    {
1460 1
        return static::create(
1461
            [
1462 1
                $this->keys(),
1463 1
                $this->values(),
1464
            ],
1465 1
            $this->iteratorClass,
1466 1
            false
1467
        );
1468
    }
1469
1470
    /**
1471
     * Iterate over the current array and modify the array's value.
1472
     *
1473
     * @param \Closure $closure
1474
     *
1475
     * @return static
1476
     *                <p>(Immutable)</p>
1477
     */
1478 4 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...
1479
    {
1480
        // init
1481 4
        $array = [];
1482
1483 4
        foreach ($this->getGenerator() as $key => $value) {
1484 4
            $array[$key] = $closure($value, $key);
1485
        }
1486
1487 4
        return static::create(
1488 4
            $array,
1489 4
            $this->iteratorClass,
1490 4
            false
1491
        );
1492
    }
1493
1494
    /**
1495
     * Check if a value is in the current array using a closure.
1496
     *
1497
     * @param \Closure $closure
1498
     *
1499
     * @return bool
1500
     *              <p>Returns true if the given value is found, false otherwise.</p>
1501
     */
1502 4
    public function exists(\Closure $closure): bool
1503
    {
1504
        // init
1505 4
        $isExists = false;
1506
1507 4
        foreach ($this->getGenerator() as $key => $value) {
1508 3
            if ($closure($value, $key)) {
1509 1
                $isExists = true;
1510
1511 1
                break;
1512
            }
1513
        }
1514
1515 4
        return $isExists;
1516
    }
1517
1518
    /**
1519
     * Fill the array until "$num" with "$default" values.
1520
     *
1521
     * @param int   $num
1522
     * @param mixed $default
1523
     *
1524
     * @return static
1525
     *                <p>(Immutable)</p>
1526
     */
1527 8
    public function fillWithDefaults(int $num, $default = null): self
1528
    {
1529 8
        if ($num < 0) {
1530 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1531
        }
1532
1533 7
        $this->generatorToArray();
1534
1535 7
        $tmpArray = $this->array;
1536
1537 7
        $count = \count($tmpArray);
1538
1539 7
        while ($count < $num) {
1540 4
            $tmpArray[] = $default;
1541 4
            ++$count;
1542
        }
1543
1544 7
        return static::create(
1545 7
            $tmpArray,
1546 7
            $this->iteratorClass,
1547 7
            false
1548
        );
1549
    }
1550
1551
    /**
1552
     * Find all items in an array that pass the truth test.
1553
     *
1554
     * @param \Closure|null $closure [optional] <p>
1555
     *                               The callback function to use
1556
     *                               </p>
1557
     *                               <p>
1558
     *                               If no callback is supplied, all entries of
1559
     *                               input equal to false (see
1560
     *                               converting to
1561
     *                               boolean) will be removed.
1562
     *                               </p>
1563
     *                               * @param int $flag [optional] <p>
1564
     *                               Flag determining what arguments are sent to <i>callback</i>:
1565
     *                               </p><ul>
1566
     *                               <li>
1567
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1568
     *                               to <i>callback</i> instead of the value</span>
1569
     *                               </li>
1570
     *                               <li>
1571
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1572
     *                               arguments to <i>callback</i> instead of the value</span>
1573
     *                               </li>
1574
     *                               </ul>
1575
     *
1576
     * @return static
1577
     *                <p>(Immutable)</p>
1578
     */
1579 11
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1580
    {
1581 11
        if (!$closure) {
1582 1
            return $this->clean();
1583
        }
1584
1585 11
        return static::create(
1586 11
            \array_filter($this->getArray(), $closure, $flag),
1587 11
            $this->iteratorClass,
1588 11
            false
1589
        );
1590
    }
1591
1592
    /**
1593
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1594
     * property within that.
1595
     *
1596
     * @param string          $property
1597
     * @param string|string[] $value
1598
     * @param string          $comparisonOp
1599
     *                                      <p>
1600
     *                                      'eq' (equals),<br />
1601
     *                                      'gt' (greater),<br />
1602
     *                                      'gte' || 'ge' (greater or equals),<br />
1603
     *                                      'lt' (less),<br />
1604
     *                                      'lte' || 'le' (less or equals),<br />
1605
     *                                      'ne' (not equals),<br />
1606
     *                                      'contains',<br />
1607
     *                                      'notContains',<br />
1608
     *                                      'newer' (via strtotime),<br />
1609
     *                                      'older' (via strtotime),<br />
1610
     *                                      </p>
1611
     *
1612
     * @return static
1613
     *                <p>(Immutable)</p>
1614
     */
1615 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1616
    {
1617 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...
1618 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1619
        }
1620
1621
        $ops = [
1622
            'eq' => static function ($item, $prop, $value) {
1623 1
                return $item[$prop] === $value;
1624 1
            },
1625
            'gt' => static function ($item, $prop, $value) {
1626
                return $item[$prop] > $value;
1627 1
            },
1628
            'ge' => static function ($item, $prop, $value) {
1629
                return $item[$prop] >= $value;
1630 1
            },
1631
            'gte' => static function ($item, $prop, $value) {
1632
                return $item[$prop] >= $value;
1633 1
            },
1634
            'lt' => static function ($item, $prop, $value) {
1635 1
                return $item[$prop] < $value;
1636 1
            },
1637
            'le' => static function ($item, $prop, $value) {
1638
                return $item[$prop] <= $value;
1639 1
            },
1640
            'lte' => static function ($item, $prop, $value) {
1641
                return $item[$prop] <= $value;
1642 1
            },
1643
            'ne' => static function ($item, $prop, $value) {
1644
                return $item[$prop] !== $value;
1645 1
            },
1646
            'contains' => static function ($item, $prop, $value) {
1647 1
                return \in_array($item[$prop], (array) $value, true);
1648 1
            },
1649
            'notContains' => static function ($item, $prop, $value) {
1650
                return !\in_array($item[$prop], (array) $value, true);
1651 1
            },
1652
            'newer' => static function ($item, $prop, $value) {
1653
                return \strtotime($item[$prop]) > \strtotime($value);
1654 1
            },
1655
            'older' => static function ($item, $prop, $value) {
1656
                return \strtotime($item[$prop]) < \strtotime($value);
1657 1
            },
1658
        ];
1659
1660 1
        $result = \array_values(
1661 1
            \array_filter(
1662 1
                $this->getArray(),
1663
                static function ($item) use (
1664 1
                    $property,
1665 1
                    $value,
1666 1
                    $ops,
1667 1
                    $comparisonOp
1668
                ) {
1669 1
                    $item = (array) $item;
1670 1
                    $itemArrayy = new static($item);
1671 1
                    $item[$property] = $itemArrayy->get($property, []);
1672
1673 1
                    return $ops[$comparisonOp]($item, $property, $value);
1674 1
                }
1675
            )
1676
        );
1677
1678 1
        return static::create(
1679 1
            $result,
1680 1
            $this->iteratorClass,
1681 1
            false
1682
        );
1683
    }
1684
1685
    /**
1686
     * Find the first item in an array that passes the truth test,
1687
     *  otherwise return false
1688
     *
1689
     * @param \Closure $closure
1690
     *
1691
     * @return false|mixed
1692
     *                     <p>Return false if we did not find the value.</p>
1693
     */
1694 8
    public function find(\Closure $closure)
1695
    {
1696 8
        foreach ($this->getGenerator() as $key => $value) {
1697 6
            if ($closure($value, $key)) {
1698 5
                return $value;
1699
            }
1700
        }
1701
1702 3
        return false;
1703
    }
1704
1705
    /**
1706
     * find by ...
1707
     *
1708
     * @param string $property
1709
     * @param string $value
1710
     * @param string $comparisonOp
1711
     *
1712
     * @return static
1713
     *                <p>(Immutable)</p>
1714
     */
1715 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1716
    {
1717 1
        return $this->filterBy($property, $value, $comparisonOp);
1718
    }
1719
1720
    /**
1721
     * Get the first value from the current array.
1722
     *
1723
     * @return mixed
1724
     *               <p>Return null if there wasn't a element.</p>
1725
     */
1726 17
    public function first()
1727
    {
1728 17
        $key_first = $this->firstKey();
1729 17
        if ($key_first === null) {
1730 3
            return null;
1731
        }
1732
1733 14
        return $this->get($key_first);
1734
    }
1735
1736
    /**
1737
     * Move an array element to the first place.
1738
     *
1739
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
1740
     *       loss the keys of an indexed array.
1741
     *
1742
     * @param int|string $key
1743
     *
1744
     * @return static
1745
     *                <p>(Immutable)</p>
1746
     */
1747 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...
1748
    {
1749 1
        $array = $this->getArray();
1750
1751 1
        if ($this->offsetExists($key)) {
1752 1
            $tmpValue = $this->get($key);
1753 1
            unset($array[$key]);
1754 1
            $array = [$key => $tmpValue] + $array;
1755
        }
1756
1757 1
        return static::create(
1758 1
            $array,
1759 1
            $this->iteratorClass,
1760 1
            false
1761
        );
1762
    }
1763
1764
    /**
1765
     * Move an array element to the last place.
1766
     *
1767
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
1768
     *       loss the keys of an indexed array.
1769
     *
1770
     * @param int|string $key
1771
     *
1772
     * @return static
1773
     *                <p>(Immutable)</p>
1774
     */
1775 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...
1776
    {
1777 1
        $array = $this->getArray();
1778
1779 1
        if ($this->offsetExists($key)) {
1780 1
            $tmpValue = $this->get($key);
1781 1
            unset($array[$key]);
1782 1
            $array += [$key => $tmpValue];
1783
        }
1784
1785 1
        return static::create(
1786 1
            $array,
1787 1
            $this->iteratorClass,
1788 1
            false
1789
        );
1790
    }
1791
1792
    /**
1793
     * Get the first key from the current array.
1794
     *
1795
     * @return mixed
1796
     *               <p>Return null if there wasn't a element.</p>
1797
     */
1798 24
    public function firstKey()
1799
    {
1800 24
        $this->generatorToArray();
1801
1802 24
        return \array_key_first($this->array);
1803
    }
1804
1805
    /**
1806
     * Get the most used value from the array.
1807
     *
1808
     * @return mixed
1809
     *               <p>Return null if there wasn't a element.</p>
1810
     */
1811 3
    public function mostUsedValue()
1812
    {
1813 3
        return $this->countValues()->arsort()->firstKey();
1814
    }
1815
1816
    /**
1817
     * Get the most used value from the array.
1818
     *
1819
     * @param int|null $number <p>How many values you will take?</p>
1820
     *
1821
     * @return static
1822
     *                <p>(Immutable)</p>
1823
     */
1824 3
    public function mostUsedValues(int $number = null): self
1825
    {
1826 3
        return $this->countValues()->arsort()->firstsKeys($number);
1827
    }
1828
1829
    /**
1830
     * Get the first value(s) from the current array.
1831
     * And will return an empty array if there was no first entry.
1832
     *
1833
     * @param int|null $number <p>How many values you will take?</p>
1834
     *
1835
     * @return static
1836
     *                <p>(Immutable)</p>
1837
     */
1838 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...
1839
    {
1840 3
        $arrayTmp = $this->keys()->getArray();
1841
1842 3
        if ($number === null) {
1843
            $array = (array) \array_shift($arrayTmp);
1844
        } else {
1845 3
            $number = (int) $number;
1846 3
            $array = \array_splice($arrayTmp, 0, $number);
1847
        }
1848
1849 3
        return static::create(
1850 3
            $array,
1851 3
            $this->iteratorClass,
1852 3
            false
1853
        );
1854
    }
1855
1856
    /**
1857
     * Get the first value(s) from the current array.
1858
     * And will return an empty array if there was no first entry.
1859
     *
1860
     * @param int|null $number <p>How many values you will take?</p>
1861
     *
1862
     * @return static
1863
     *                <p>(Immutable)</p>
1864
     */
1865 36 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...
1866
    {
1867 36
        $arrayTmp = $this->getArray();
1868
1869 36
        if ($number === null) {
1870 14
            $array = (array) \array_shift($arrayTmp);
1871
        } else {
1872 22
            $number = (int) $number;
1873 22
            $array = \array_splice($arrayTmp, 0, $number);
1874
        }
1875
1876 36
        return static::create(
1877 36
            $array,
1878 36
            $this->iteratorClass,
1879 36
            false
1880
        );
1881
    }
1882
1883
    /**
1884
     * Get and rmove the first value(s) from the current array.
1885
     * And will return an empty array if there was no first entry.
1886
     *
1887
     * @param int|null $number <p>How many values you will take?</p>
1888
     *
1889
     * @return static
1890
     *                <p>(Mutable)</p>
1891
     */
1892 34
    public function firstsMutable(int $number = null): self
1893
    {
1894 34
        $this->generatorToArray();
1895
1896 34
        if ($number === null) {
1897 19
            $this->array = (array) \array_shift($this->array);
1898
        } else {
1899 15
            $number = (int) $number;
1900 15
            $this->array = \array_splice($this->array, 0, $number);
1901
        }
1902
1903 34
        return $this;
1904
    }
1905
1906
    /**
1907
     * Exchanges all keys with their associated values in an array.
1908
     *
1909
     * @return static
1910
     *                <p>(Immutable)</p>
1911
     */
1912 1
    public function flip(): self
1913
    {
1914 1
        return static::create(
1915 1
            \array_flip($this->getArray()),
1916 1
            $this->iteratorClass,
1917 1
            false
1918
        );
1919
    }
1920
1921
    /**
1922
     * Get a value from an array (optional using dot-notation).
1923
     *
1924
     * @param mixed $key      <p>The key to look for.</p>
1925
     * @param mixed $fallback <p>Value to fallback to.</p>
1926
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1927
     *                        class.</p>
1928
     *
1929
     * @return mixed|static
1930
     */
1931 104
    public function get($key, $fallback = null, array $array = null)
1932
    {
1933 104
        if ($array !== null) {
1934 5
            $usedArray = $array;
1935
        } else {
1936 99
            $this->generatorToArray();
1937
1938 99
            $usedArray = $this->array;
1939
        }
1940
1941 104
        if ($key === null) {
1942 1
            return static::create(
1943 1
                $usedArray,
1944 1
                $this->iteratorClass,
1945 1
                false
1946
            );
1947
        }
1948
1949
        // php cast "bool"-index into "int"-index
1950 104
        if ((bool) $key === $key) {
1951 3
            $key = (int) $key;
1952
        }
1953
1954 104
        if (\array_key_exists($key, $usedArray) === true) {
1955 93
            if (\is_array($usedArray[$key])) {
1956 11
                return static::create(
1957 11
                    $usedArray[$key],
1958 11
                    $this->iteratorClass,
1959 11
                    false
1960
                );
1961
            }
1962
1963 84
            return $usedArray[$key];
1964
        }
1965
1966
        // crawl through array, get key according to object or not
1967 24
        $usePath = false;
1968
        if (
1969 24
            $this->pathSeparator
1970
            &&
1971 24
            \is_string($key)
1972
            &&
1973 24
            \strpos($key, $this->pathSeparator) !== false
1974
        ) {
1975 7
            $segments = \explode($this->pathSeparator, (string) $key);
1976 7
            if ($segments !== false) {
1977 7
                $usePath = true;
1978
1979 7
                foreach ($segments as $segment) {
1980
                    if (
1981
                        (
1982 7
                            \is_array($usedArray)
1983
                            ||
1984 7
                            $usedArray instanceof \ArrayAccess
1985
                        )
1986
                        &&
1987 7
                        isset($usedArray[$segment])
1988
                    ) {
1989 7
                        $usedArray = $usedArray[$segment];
1990
1991 7
                        continue;
1992
                    }
1993
1994
                    if (
1995 6
                        \is_object($usedArray)
1996
                        &&
1997 6
                        \property_exists($usedArray, $segment)
1998
                    ) {
1999 1
                        $usedArray = $usedArray->{$segment};
2000
2001 1
                        continue;
2002
                    }
2003
2004 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2005
                }
2006
            }
2007
        }
2008
2009 24
        if (!$usePath && !isset($usedArray[$key])) {
2010 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2011
        }
2012
2013 7
        if (\is_array($usedArray)) {
2014 1
            return static::create(
2015 1
                $usedArray,
2016 1
                $this->iteratorClass,
2017 1
                false
2018
            );
2019
        }
2020
2021 7
        return $usedArray;
2022
    }
2023
2024
    /**
2025
     * alias: for "Arrayy->getArray()"
2026
     *
2027
     * @return array
2028
     *
2029
     * @see Arrayy::getArray()
2030
     */
2031 1
    public function getAll(): array
2032
    {
2033 1
        return $this->getArray();
2034
    }
2035
2036
    /**
2037
     * Get the current array from the "Arrayy"-object.
2038
     *
2039
     * @return array
2040
     */
2041 800
    public function getArray(): array
2042
    {
2043
        // init
2044 800
        $array = [];
2045
2046 800
        foreach ($this->getGenerator() as $key => $value) {
2047 692
            $array[$key] = $value;
2048
        }
2049
2050 800
        return $array;
2051
    }
2052
2053
    /**
2054
     * Returns the values from a single column of the input array, identified by
2055
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2056
     *
2057
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2058
     * array by the values from the $indexKey column in the input array.
2059
     *
2060
     * @param mixed $columnKey
2061
     * @param mixed $indexKey
2062
     *
2063
     * @return static
2064
     *                <p>(Immutable)</p>
2065
     */
2066 1
    public function getColumn($columnKey = null, $indexKey = null): self
2067
    {
2068 1
        return static::create(
2069 1
            \array_column($this->getArray(), $columnKey, $indexKey),
2070 1
            $this->iteratorClass,
2071 1
            false
2072
        );
2073
    }
2074
2075
    /**
2076
     * Get the current array from the "Arrayy"-object as generator.
2077
     *
2078
     * @return \Generator
2079
     */
2080 875
    public function getGenerator(): \Generator
2081
    {
2082 875
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2083 39
            yield from $this->generator;
2084
        }
2085
2086 875
        yield from $this->array;
2087 832
    }
2088
2089
    /**
2090
     * alias: for "Arrayy->keys()"
2091
     *
2092
     * @return static
2093
     *                <p>(Immutable)</p>
2094
     *
2095
     * @see Arrayy::keys()
2096
     */
2097 2
    public function getKeys(): self
2098
    {
2099 2
        return $this->keys();
2100
    }
2101
2102
    /**
2103
     * Get the current array from the "Arrayy"-object as object.
2104
     *
2105
     * @return \stdClass
2106
     */
2107 4
    public function getObject(): \stdClass
2108
    {
2109 4
        return self::arrayToObject($this->getArray());
2110
    }
2111
2112
    /**
2113
     * alias: for "Arrayy->randomImmutable()"
2114
     *
2115
     * @return static
2116
     *                <p>(Immutable)</p>
2117
     *
2118
     * @see Arrayy::randomImmutable()
2119
     */
2120 4
    public function getRandom(): self
2121
    {
2122 4
        return $this->randomImmutable();
2123
    }
2124
2125
    /**
2126
     * alias: for "Arrayy->randomKey()"
2127
     *
2128
     * @return mixed
2129
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2130
     *
2131
     * @see Arrayy::randomKey()
2132
     */
2133 3
    public function getRandomKey()
2134
    {
2135 3
        return $this->randomKey();
2136
    }
2137
2138
    /**
2139
     * alias: for "Arrayy->randomKeys()"
2140
     *
2141
     * @param int $number
2142
     *
2143
     * @return static
2144
     *                <p>(Immutable)</p>
2145
     *
2146
     * @see Arrayy::randomKeys()
2147
     */
2148 8
    public function getRandomKeys(int $number): self
2149
    {
2150 8
        return $this->randomKeys($number);
2151
    }
2152
2153
    /**
2154
     * alias: for "Arrayy->randomValue()"
2155
     *
2156
     * @return mixed
2157
     *               <p>Get a random value or null if there wasn't a value.</p>
2158
     *
2159
     * @see Arrayy::randomValue()
2160
     */
2161 3
    public function getRandomValue()
2162
    {
2163 3
        return $this->randomValue();
2164
    }
2165
2166
    /**
2167
     * alias: for "Arrayy->randomValues()"
2168
     *
2169
     * @param int $number
2170
     *
2171
     * @return static
2172
     *                <p>(Immutable)</p>
2173
     *
2174
     * @see Arrayy::randomValues()
2175
     */
2176 6
    public function getRandomValues(int $number): self
2177
    {
2178 6
        return $this->randomValues($number);
2179
    }
2180
2181
    /**
2182
     * Group values from a array according to the results of a closure.
2183
     *
2184
     * @param callable|string $grouper  <p>A callable function name.</p>
2185
     * @param bool            $saveKeys
2186
     *
2187
     * @return static
2188
     *                <p>(Immutable)</p>
2189
     */
2190 4
    public function group($grouper, bool $saveKeys = false): self
2191
    {
2192
        // init
2193 4
        $result = [];
2194
2195
        // Iterate over values, group by property/results from closure.
2196 4
        foreach ($this->getGenerator() as $key => $value) {
2197 4
            if (\is_callable($grouper)) {
2198 3
                $groupKey = $grouper($value, $key);
2199
            } else {
2200 1
                $groupKey = $this->get($grouper, null, $this->getArray());
2201
            }
2202
2203 4
            $newValue = $this->get($groupKey, null, $result);
2204
2205 4
            if ($groupKey instanceof self) {
2206
                $groupKey = $groupKey->getArray();
2207
            }
2208
2209 4
            if ($newValue instanceof self) {
2210 4
                $newValue = $newValue->getArray();
2211
            }
2212
2213
            // Add to results.
2214 4
            if ($groupKey !== null) {
2215 3
                if ($saveKeys) {
2216 2
                    $result[$groupKey] = $newValue;
2217 2
                    $result[$groupKey][$key] = $value;
2218
                } else {
2219 1
                    $result[$groupKey] = $newValue;
2220 1
                    $result[$groupKey][] = $value;
2221
                }
2222
            }
2223
        }
2224
2225 4
        return static::create(
2226 4
            $result,
2227 4
            $this->iteratorClass,
2228 4
            false
2229
        );
2230
    }
2231
2232
    /**
2233
     * Check if an array has a given key.
2234
     *
2235
     * @param mixed $key
2236
     *
2237
     * @return bool
2238
     */
2239 23
    public function has($key): bool
2240
    {
2241 23
        static $UN_FOUND = null;
2242
2243 23
        if ($UN_FOUND === null) {
2244
            // Generate unique string to use as marker.
2245 1
            $UN_FOUND = \uniqid('arrayy', true);
2246
        }
2247
2248 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2249
    }
2250
2251
    /**
2252
     * Implodes the values of this array.
2253
     *
2254
     * @param string $glue
2255
     *
2256
     * @return string
2257
     */
2258 28
    public function implode(string $glue = ''): string
2259
    {
2260 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2261
    }
2262
2263
    /**
2264
     * Implodes the keys of this array.
2265
     *
2266
     * @param string $glue
2267
     *
2268
     * @return string
2269
     */
2270 8
    public function implodeKeys(string $glue = ''): string
2271
    {
2272 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2273
    }
2274
2275
    /**
2276
     * Given a list and an iterate-function that returns
2277
     * a key for each element in the list (or a property name),
2278
     * returns an object with an index of each item.
2279
     *
2280
     * @param mixed $key
2281
     *
2282
     * @return static
2283
     *                <p>(Immutable)</p>
2284
     */
2285 4
    public function indexBy($key): self
2286
    {
2287
        // init
2288 4
        $results = [];
2289
2290 4
        foreach ($this->getGenerator() as $a) {
2291 4
            if (\array_key_exists($key, $a) === true) {
2292 3
                $results[$a[$key]] = $a;
2293
            }
2294
        }
2295
2296 4
        return static::create(
2297 4
            $results,
2298 4
            $this->iteratorClass,
2299 4
            false
2300
        );
2301
    }
2302
2303
    /**
2304
     * alias: for "Arrayy->searchIndex()"
2305
     *
2306
     * @param mixed $value <p>The value to search for.</p>
2307
     *
2308
     * @return mixed
2309
     *
2310
     * @see Arrayy::searchIndex()
2311
     */
2312 4
    public function indexOf($value)
2313
    {
2314 4
        return $this->searchIndex($value);
2315
    }
2316
2317
    /**
2318
     * Get everything but the last..$to items.
2319
     *
2320
     * @param int $to
2321
     *
2322
     * @return static
2323
     *                <p>(Immutable)</p>
2324
     */
2325 12
    public function initial(int $to = 1): self
2326
    {
2327 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2328
    }
2329
2330
    /**
2331
     * Return an array with all elements found in input array.
2332
     *
2333
     * @param array $search
2334
     * @param bool  $keepKeys
2335
     *
2336
     * @return static
2337
     *                <p>(Immutable)</p>
2338
     */
2339 3
    public function intersection(array $search, bool $keepKeys = false): self
2340
    {
2341 3
        if ($keepKeys) {
2342 1
            return static::create(
2343 1
                \array_uintersect(
2344 1
                    $this->array,
2345 1
                    $search,
2346
                    static function ($a, $b) {
2347 1
                        return $a === $b ? 0 : -1;
2348 1
                    }
2349
                ),
2350 1
                $this->iteratorClass,
2351 1
                false
2352
            );
2353
        }
2354
2355 2
        return static::create(
2356 2
            \array_values(\array_intersect($this->getArray(), $search)),
2357 2
            $this->iteratorClass,
2358 2
            false
2359
        );
2360
    }
2361
2362
    /**
2363
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2364
     *
2365
     * @param array $search
2366
     *
2367
     * @return bool
2368
     */
2369 1
    public function intersects(array $search): bool
2370
    {
2371 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2372
    }
2373
2374
    /**
2375
     * Invoke a function on all of an array's values.
2376
     *
2377
     * @param callable $callable
2378
     * @param mixed    $arguments
2379
     *
2380
     * @return static
2381
     *                <p>(Immutable)</p>
2382
     */
2383 1
    public function invoke($callable, $arguments = []): self
2384
    {
2385
        // If one argument given for each iteration, create an array for it.
2386 1
        if (!\is_array($arguments)) {
2387 1
            $arguments = \array_fill(
2388 1
                0,
2389 1
                \count($this->getArray(), \COUNT_NORMAL),
2390 1
                $arguments
2391
            );
2392
        }
2393
2394
        // If the callable has arguments, pass them.
2395 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...
2396 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2397
        } else {
2398 1
            $array = $this->map($callable);
2399
        }
2400
2401 1
        return static::create(
2402 1
            $array,
2403 1
            $this->iteratorClass,
2404 1
            false
2405
        );
2406
    }
2407
2408
    /**
2409
     * Check whether array is associative or not.
2410
     *
2411
     * @param bool $recursive
2412
     *
2413
     * @return bool
2414
     *              <p>Returns true if associative, false otherwise.</p>
2415
     */
2416 15
    public function isAssoc(bool $recursive = false): bool
2417
    {
2418 15
        if ($this->isEmpty()) {
2419 3
            return false;
2420
        }
2421
2422 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2423 13
            if (!\is_string($key)) {
2424 11
                return false;
2425
            }
2426
        }
2427
2428 3
        return true;
2429
    }
2430
2431
    /**
2432
     * Check if a given key or keys are empty.
2433
     *
2434
     * @param int|int[]|string|string[]|null $keys
2435
     *
2436
     * @return bool
2437
     *              <p>Returns true if empty, false otherwise.</p>
2438
     */
2439 38
    public function isEmpty($keys = null): bool
2440
    {
2441 38
        if ($this->generator) {
2442
            return $this->getArray() === [];
2443
        }
2444
2445 38
        if ($keys === null) {
2446 38
            return $this->array === [];
2447
        }
2448
2449
        foreach ((array) $keys as $key) {
2450
            if (!empty($this->get($key))) {
2451
                return false;
2452
            }
2453
        }
2454
2455
        return true;
2456
    }
2457
2458
    /**
2459
     * Check if the current array is equal to the given "$array" or not.
2460
     *
2461
     * @param array $array
2462
     *
2463
     * @return bool
2464
     */
2465 1
    public function isEqual(array $array): bool
2466
    {
2467 1
        return $this->getArray() === $array;
2468
    }
2469
2470
    /**
2471
     * Check if the current array is a multi-array.
2472
     *
2473
     * @return bool
2474
     */
2475 22
    public function isMultiArray(): bool
2476
    {
2477
        return !(
2478 22
            \count($this->getArray(), \COUNT_NORMAL)
2479
            ===
2480 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2481
        );
2482
    }
2483
2484
    /**
2485
     * Check whether array is numeric or not.
2486
     *
2487
     * @return bool
2488
     *              <p>Returns true if numeric, false otherwise.</p>
2489
     */
2490 5
    public function isNumeric(): bool
2491
    {
2492 5
        if ($this->isEmpty()) {
2493 2
            return false;
2494
        }
2495
2496 4
        foreach ($this->keys()->getGenerator() as $key) {
2497 4
            if (!\is_int($key)) {
2498 2
                return false;
2499
            }
2500
        }
2501
2502 2
        return true;
2503
    }
2504
2505
    /**
2506
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2507
     *
2508
     * @param bool $recursive
2509
     *
2510
     * @return bool
2511
     */
2512 1
    public function isSequential(bool $recursive = false): bool
2513
    {
2514
2515
        // recursive
2516
2517 1
        if ($recursive === true) {
2518
            return $this->array_keys_recursive($this->getArray())
2519
                   ===
2520
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2521
        }
2522
2523
        // non recursive
2524
2525 1
        return \array_keys($this->getArray())
2526
               ===
2527 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2528
    }
2529
2530
    /**
2531
     * @return array
2532
     */
2533
    public function jsonSerialize(): array
2534
    {
2535
        return $this->getArray();
2536
    }
2537
2538
    /**
2539
     * Checks if the given key exists in the provided array.
2540
     *
2541
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2542
     *       then you need to use "Arrayy->offsetExists()".
2543
     *
2544
     * @param int|string $key the key to look for
2545
     *
2546
     * @return bool
2547
     */
2548 58
    public function keyExists($key): bool
2549
    {
2550 58
        return \array_key_exists($key, $this->array);
2551
    }
2552
2553
    /**
2554
     * Get all keys from the current array.
2555
     *
2556
     * @param bool  $recursive    [optional] <p>
2557
     *                            Get all keys, also from all sub-arrays from an multi-dimensional array.
2558
     *                            </p>
2559
     * @param mixed $search_value [optional] <p>
2560
     *                            If specified, then only keys containing these values are returned.
2561
     *                            </p>
2562
     * @param bool  $strict       [optional] <p>
2563
     *                            Determines if strict comparison (===) should be used during the search.
2564
     *                            </p>
2565
     *
2566
     * @return static
2567
     *                <p>(Immutable) An array of all the keys in input.</p>
2568
     */
2569 29
    public function keys(bool $recursive = false, $search_value = null, bool $strict = true): self
2570
    {
2571
2572
        // recursive
2573
2574 29
        if ($recursive === true) {
2575 3
            if ($search_value === null) {
2576 3
                $array = $this->array_keys_recursive($this->getArray());
2577
            } else {
2578
                $array = $this->array_keys_recursive($this->getArray(), $search_value, $strict);
2579
            }
2580
2581 3
            return static::create(
2582 3
                $array,
2583 3
                $this->iteratorClass,
2584 3
                false
2585
            );
2586
        }
2587
2588
        // non recursive
2589
2590 28
        if ($search_value === null) {
2591
            $arrayFunction = function () {
2592 28
                foreach ($this->getGenerator() as $key => $value) {
2593 26
                    yield $key;
2594
                }
2595 28
            };
2596
        } else {
2597
            $arrayFunction = function () use ($search_value, $strict) {
2598
                foreach ($this->getGenerator() as $key => $value) {
2599
                    if ($strict) {
2600
                        if ($search_value === $value) {
2601
                            yield $key;
2602
                        }
2603
                    } else {
2604
                        /** @noinspection NestedPositiveIfStatementsInspection */
2605
                        if ($search_value == $value) {
2606
                            yield $key;
2607
                        }
2608
                    }
2609
                }
2610
            };
2611
        }
2612
2613 28
        return static::create(
2614 28
            $arrayFunction,
2615 28
            $this->iteratorClass,
2616 28
            false
2617
        );
2618
    }
2619
2620
    /**
2621
     * Sort an array by key in reverse order.
2622
     *
2623
     * @param int $sort_flags [optional] <p>
2624
     *                        You may modify the behavior of the sort using the optional
2625
     *                        parameter sort_flags, for details
2626
     *                        see sort.
2627
     *                        </p>
2628
     *
2629
     * @return static
2630
     *                <p>(Mutable) Return this Arrayy object.</p>
2631
     */
2632 4
    public function krsort(int $sort_flags = 0): self
2633
    {
2634 4
        $this->generatorToArray();
2635
2636 4
        \krsort($this->array, $sort_flags);
2637
2638 4
        return $this;
2639
    }
2640
2641
    /**
2642
     * Get the last value from the current array.
2643
     *
2644
     * @return mixed
2645
     *               <p>Return null if there wasn't a element.</p>
2646
     */
2647 16
    public function last()
2648
    {
2649 16
        $key_last = $this->lastKey();
2650 16
        if ($key_last === null) {
2651 2
            return null;
2652
        }
2653
2654 14
        return $this->get($key_last);
2655
    }
2656
2657
    /**
2658
     * Get the last key from the current array.
2659
     *
2660
     * @return mixed
2661
     *               <p>Return null if there wasn't a element.</p>
2662
     */
2663 20
    public function lastKey()
2664
    {
2665 20
        $this->generatorToArray();
2666
2667 20
        return \array_key_last($this->array);
2668
    }
2669
2670
    /**
2671
     * Get the last value(s) from the current array.
2672
     *
2673
     * @param int|null $number
2674
     *
2675
     * @return static
2676
     *                <p>(Immutable)</p>
2677
     */
2678 13
    public function lastsImmutable(int $number = null): self
2679
    {
2680 13
        if ($this->isEmpty()) {
2681 1
            return static::create(
2682 1
                [],
2683 1
                $this->iteratorClass,
2684 1
                false
2685
            );
2686
        }
2687
2688 12
        if ($number === null) {
2689 8
            $poppedValue = $this->last();
2690
2691 8
            if ($poppedValue === null) {
2692 1
                $poppedValue = [$poppedValue];
2693
            } else {
2694 7
                $poppedValue = (array) $poppedValue;
2695
            }
2696
2697 8
            $arrayy = static::create(
2698 8
                $poppedValue,
2699 8
                $this->iteratorClass,
2700 8
                false
2701
            );
2702
        } else {
2703 4
            $number = (int) $number;
2704 4
            $arrayy = $this->rest(-$number);
2705
        }
2706
2707 12
        return $arrayy;
2708
    }
2709
2710
    /**
2711
     * Get the last value(s) from the current array.
2712
     *
2713
     * @param int|null $number
2714
     *
2715
     * @return static
2716
     *                <p>(Mutable)</p>
2717
     */
2718 13
    public function lastsMutable(int $number = null): self
2719
    {
2720 13
        if ($this->isEmpty()) {
2721 1
            return $this;
2722
        }
2723
2724 12
        if ($number === null) {
2725 8
            $poppedValue = $this->last();
2726
2727 8
            if ($poppedValue === null) {
2728 1
                $poppedValue = [$poppedValue];
2729
            } else {
2730 7
                $poppedValue = (array) $poppedValue;
2731
            }
2732
2733 8
            $this->array = static::create(
2734 8
                $poppedValue,
2735 8
                $this->iteratorClass,
2736 8
                false
2737 8
            )->getArray();
2738
        } else {
2739 4
            $number = (int) $number;
2740 4
            $this->array = $this->rest(-$number)->getArray();
2741
        }
2742
2743 12
        $this->generator = null;
2744
2745 12
        return $this;
2746
    }
2747
2748
    /**
2749
     * Count the values from the current array.
2750
     *
2751
     * alias: for "Arrayy->count()"
2752
     *
2753
     * @param int $mode
2754
     *
2755
     * @return int
2756
     *
2757
     * @see Arrayy::count()
2758
     */
2759 20
    public function length(int $mode = \COUNT_NORMAL): int
2760
    {
2761 20
        return $this->count($mode);
2762
    }
2763
2764
    /**
2765
     * Apply the given function to the every element of the array,
2766
     * collecting the results.
2767
     *
2768
     * @param callable $callable
2769
     * @param bool     $useKeyAsSecondParameter
2770
     * @param mixed    ...$arguments
2771
     *
2772
     * @return static
2773
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2774
     */
2775 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2776
    {
2777 5
        $useArguments = \func_num_args() > 2;
2778
2779 5
        return static::create(
2780
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2781 5
                foreach ($this->getGenerator() as $key => $value) {
2782 4
                    if ($useArguments) {
2783 3
                        if ($useKeyAsSecondParameter) {
2784
                            yield $key => $callable($value, $key, ...$arguments);
2785
                        } else {
2786 3
                            yield $key => $callable($value, ...$arguments);
2787
                        }
2788
                    } else {
2789
                        /** @noinspection NestedPositiveIfStatementsInspection */
2790 4
                        if ($useKeyAsSecondParameter) {
2791
                            yield $key => $callable($value, $key);
2792
                        } else {
2793 4
                            yield $key => $callable($value);
2794
                        }
2795
                    }
2796
                }
2797 5
            },
2798 5
            $this->iteratorClass,
2799 5
            false
2800
        );
2801
    }
2802
2803
    /**
2804
     * Check if all items in current array match a truth test.
2805
     *
2806
     * @param \Closure $closure
2807
     *
2808
     * @return bool
2809
     */
2810 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...
2811
    {
2812 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2813 2
            return false;
2814
        }
2815
2816 13
        foreach ($this->getGenerator() as $key => $value) {
2817 13
            $value = $closure($value, $key);
2818
2819 13
            if ($value === false) {
2820 7
                return false;
2821
            }
2822
        }
2823
2824 7
        return true;
2825
    }
2826
2827
    /**
2828
     * Check if any item in the current array matches a truth test.
2829
     *
2830
     * @param \Closure $closure
2831
     *
2832
     * @return bool
2833
     */
2834 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...
2835
    {
2836 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2837 2
            return false;
2838
        }
2839
2840 12
        foreach ($this->getGenerator() as $key => $value) {
2841 12
            $value = $closure($value, $key);
2842
2843 12
            if ($value === true) {
2844 9
                return true;
2845
            }
2846
        }
2847
2848 4
        return false;
2849
    }
2850
2851
    /**
2852
     * Get the max value from an array.
2853
     *
2854
     * @return mixed
2855
     */
2856 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...
2857
    {
2858 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2859 1
            return false;
2860
        }
2861
2862 9
        return \max($this->getArray());
2863
    }
2864
2865
    /**
2866
     * Merge the new $array into the current array.
2867
     *
2868
     * - keep key,value from the current array, also if the index is in the new $array
2869
     *
2870
     * @param array $array
2871
     * @param bool  $recursive
2872
     *
2873
     * @return static
2874
     *                <p>(Immutable)</p>
2875
     */
2876 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...
2877
    {
2878 25
        if ($recursive === true) {
2879 4
            $result = \array_replace_recursive($this->getArray(), $array);
2880
        } else {
2881 21
            $result = \array_replace($this->getArray(), $array);
2882
        }
2883
2884 25
        return static::create(
2885 25
            $result,
2886 25
            $this->iteratorClass,
2887 25
            false
2888
        );
2889
    }
2890
2891
    /**
2892
     * Merge the new $array into the current array.
2893
     *
2894
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2895
     * - create new indexes
2896
     *
2897
     * @param array $array
2898
     * @param bool  $recursive
2899
     *
2900
     * @return static
2901
     *                <p>(Immutable)</p>
2902
     */
2903 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...
2904
    {
2905 16
        if ($recursive === true) {
2906 4
            $result = \array_merge_recursive($this->getArray(), $array);
2907
        } else {
2908 12
            $result = \array_merge($this->getArray(), $array);
2909
        }
2910
2911 16
        return static::create(
2912 16
            $result,
2913 16
            $this->iteratorClass,
2914 16
            false
2915
        );
2916
    }
2917
2918
    /**
2919
     * Merge the the current array into the $array.
2920
     *
2921
     * - use key,value from the new $array, also if the index is in the current array
2922
     *
2923
     * @param array $array
2924
     * @param bool  $recursive
2925
     *
2926
     * @return static
2927
     *                <p>(Immutable)</p>
2928
     */
2929 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...
2930
    {
2931 16
        if ($recursive === true) {
2932 4
            $result = \array_replace_recursive($array, $this->getArray());
2933
        } else {
2934 12
            $result = \array_replace($array, $this->getArray());
2935
        }
2936
2937 16
        return static::create(
2938 16
            $result,
2939 16
            $this->iteratorClass,
2940 16
            false
2941
        );
2942
    }
2943
2944
    /**
2945
     * Merge the current array into the new $array.
2946
     *
2947
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2948
     * - create new indexes
2949
     *
2950
     * @param array $array
2951
     * @param bool  $recursive
2952
     *
2953
     * @return static
2954
     *                <p>(Immutable)</p>
2955
     */
2956 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...
2957
    {
2958 17
        if ($recursive === true) {
2959 4
            $result = \array_merge_recursive($array, $this->getArray());
2960
        } else {
2961 13
            $result = \array_merge($array, $this->getArray());
2962
        }
2963
2964 17
        return static::create(
2965 17
            $result,
2966 17
            $this->iteratorClass,
2967 17
            false
2968
        );
2969
    }
2970
2971
    /**
2972
     * @return ArrayyMeta|static
2973
     */
2974 14
    public static function meta()
2975
    {
2976 14
        return (new ArrayyMeta())->getMetaObject(static::class);
2977
    }
2978
2979
    /**
2980
     * Get the min value from an array.
2981
     *
2982
     * @return mixed
2983
     */
2984 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...
2985
    {
2986 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2987 1
            return false;
2988
        }
2989
2990 9
        return \min($this->getArray());
2991
    }
2992
2993
    /**
2994
     * Move an array element to a new index.
2995
     *
2996
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2997
     *
2998
     * @param int|string $from
2999
     * @param int        $to
3000
     *
3001
     * @return static
3002
     *                <p>(Immutable)</p>
3003
     */
3004 1
    public function moveElement($from, $to): self
3005
    {
3006 1
        $array = $this->getArray();
3007
3008 1
        if (\is_int($from)) {
3009 1
            $tmp = \array_splice($array, $from, 1);
3010 1
            \array_splice($array, (int) $to, 0, $tmp);
3011 1
            $output = $array;
3012 1
        } elseif (\is_string($from)) {
3013 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3014 1
            $itemToMove = $array[$from];
3015 1
            if ($indexToMove !== false) {
3016 1
                \array_splice($array, $indexToMove, 1);
3017
            }
3018 1
            $i = 0;
3019 1
            $output = [];
3020 1
            foreach ($array as $key => $item) {
3021 1
                if ($i === $to) {
3022 1
                    $output[$from] = $itemToMove;
3023
                }
3024 1
                $output[$key] = $item;
3025 1
                ++$i;
3026
            }
3027
        } else {
3028
            $output = [];
3029
        }
3030
3031 1
        return static::create(
3032 1
            $output,
3033 1
            $this->iteratorClass,
3034 1
            false
3035
        );
3036
    }
3037
3038
    /**
3039
     * Get a subset of the items from the given array.
3040
     *
3041
     * @param mixed[] $keys
3042
     *
3043
     * @return static
3044
     *                <p>(Immutable)</p>
3045
     */
3046
    public function only(array $keys): self
3047
    {
3048
        $array = $this->getArray();
3049
3050
        return static::create(
3051
            \array_intersect_key($array, \array_flip($keys)),
3052
            $this->iteratorClass,
3053
            false
3054
        );
3055
    }
3056
3057
    /**
3058
     * Pad array to the specified size with a given value.
3059
     *
3060
     * @param int   $size  <p>Size of the result array.</p>
3061
     * @param mixed $value <p>Empty value by default.</p>
3062
     *
3063
     * @return static
3064
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3065
     */
3066 4
    public function pad(int $size, $value): self
3067
    {
3068 4
        return static::create(
3069 4
            \array_pad($this->getArray(), $size, $value),
3070 4
            $this->iteratorClass,
3071 4
            false
3072
        );
3073
    }
3074
3075
    /**
3076
     * Pop a specified value off the end of the current array.
3077
     *
3078
     * @return mixed
3079
     *               <p>(Mutable) The popped element from the current array.</p>
3080
     */
3081 4
    public function pop()
3082
    {
3083 4
        $this->generatorToArray();
3084
3085 4
        return \array_pop($this->array);
3086
    }
3087
3088
    /**
3089
     * Prepend a (key) + value to the current array.
3090
     *
3091
     * @param mixed $value
3092
     * @param mixed $key
3093
     *
3094
     * @return static
3095
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3096
     */
3097 9
    public function prepend($value, $key = null): self
3098
    {
3099 9
        $this->generatorToArray();
3100
3101 9
        if ($key === null) {
3102 8
            \array_unshift($this->array, $value);
3103
        } else {
3104
            /** @noinspection AdditionOperationOnArraysInspection */
3105 2
            $this->array = [$key => $value] + $this->array;
3106
        }
3107
3108 9
        return $this;
3109
    }
3110
3111
    /**
3112
     * Add a suffix to each key.
3113
     *
3114
     * @param mixed $suffix
3115
     *
3116
     * @return static
3117
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3118
     */
3119 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...
3120
    {
3121
        // init
3122 10
        $result = [];
3123
3124 10
        foreach ($this->getGenerator() as $key => $item) {
3125 9
            if ($item instanceof self) {
3126
                $result[$key] = $item->prependToEachKey($suffix);
3127 9
            } elseif (\is_array($item)) {
3128
                $result[$key] = self::create(
3129
                    $item,
3130
                    $this->iteratorClass,
3131
                    false
3132
                )->prependToEachKey($suffix)
3133
                    ->toArray();
3134
            } else {
3135 9
                $result[$key . $suffix] = $item;
3136
            }
3137
        }
3138
3139 10
        return self::create(
3140 10
            $result,
3141 10
            $this->iteratorClass,
3142 10
            false
3143
        );
3144
    }
3145
3146
    /**
3147
     * Add a suffix to each value.
3148
     *
3149
     * @param mixed $suffix
3150
     *
3151
     * @return static
3152
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3153
     */
3154 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...
3155
    {
3156
        // init
3157 10
        $result = [];
3158
3159 10
        foreach ($this->getGenerator() as $key => $item) {
3160 9
            if ($item instanceof self) {
3161
                $result[$key] = $item->prependToEachValue($suffix);
3162 9
            } elseif (\is_array($item)) {
3163
                $result[$key] = self::create(
3164
                    $item,
3165
                    $this->iteratorClass,
3166
                    false
3167
                )->prependToEachValue($suffix)
3168
                    ->toArray();
3169 9
            } elseif (\is_object($item)) {
3170 1
                $result[$key] = $item;
3171
            } else {
3172 8
                $result[$key] = $item . $suffix;
3173
            }
3174
        }
3175
3176 10
        return self::create(
3177 10
            $result,
3178 10
            $this->iteratorClass,
3179 10
            false
3180
        );
3181
    }
3182
3183
    /**
3184
     * Return the value of a given key and
3185
     * delete the key.
3186
     *
3187
     * @param int|int[]|string|string[]|null $keyOrKeys
3188
     * @param mixed                          $fallback
3189
     *
3190
     * @return mixed
3191
     */
3192 1
    public function pull($keyOrKeys = null, $fallback = null)
3193
    {
3194 1
        if ($keyOrKeys === null) {
3195
            $array = $this->getArray();
3196
            $this->clear();
3197
3198
            return $array;
3199
        }
3200
3201 1
        if (\is_array($keyOrKeys)) {
3202 1
            $valueOrValues = [];
3203 1
            foreach ($keyOrKeys as $key) {
3204 1
                $valueOrValues[] = $this->get($key, $fallback);
3205 1
                $this->offsetUnset($key);
3206
            }
3207
        } else {
3208 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3209 1
            $this->offsetUnset($keyOrKeys);
3210
        }
3211
3212 1
        return $valueOrValues;
3213
    }
3214
3215
    /**
3216
     * Push one or more values onto the end of array at once.
3217
     *
3218
     * @return static
3219
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3220
     */
3221 4 View Code Duplication
    public function push(/* variadic arguments allowed */): 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...
3222
    {
3223 4
        $this->generatorToArray();
3224
3225 4
        if (\func_num_args()) {
3226 4
            $args = \func_get_args();
3227 4
            \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...
3228
        }
3229
3230 4
        return $this;
3231
    }
3232
3233
    /**
3234
     * Get a random value from the current array.
3235
     *
3236
     * @param int|null $number <p>How many values you will take?</p>
3237
     *
3238
     * @return static
3239
     *                <p>(Immutable)</p>
3240
     */
3241 18
    public function randomImmutable(int $number = null): self
3242
    {
3243 18
        $this->generatorToArray();
3244
3245 18 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3246 1
            return static::create(
3247 1
                [],
3248 1
                $this->iteratorClass,
3249 1
                false
3250
            );
3251
        }
3252
3253 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...
3254
            /** @noinspection NonSecureArrayRandUsageInspection */
3255 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3256
3257 13
            return static::create(
3258 13
                $arrayRandValue,
3259 13
                $this->iteratorClass,
3260 13
                false
3261
            );
3262
        }
3263
3264 5
        $arrayTmp = $this->array;
3265
        /** @noinspection NonSecureShuffleUsageInspection */
3266 5
        \shuffle($arrayTmp);
3267
3268 5
        return static::create(
3269 5
            $arrayTmp,
3270 5
            $this->iteratorClass,
3271 5
            false
3272 5
        )->firstsImmutable($number);
3273
    }
3274
3275
    /**
3276
     * Pick a random key/index from the keys of this array.
3277
     *
3278
     * @throws \RangeException If array is empty
3279
     *
3280
     * @return mixed
3281
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3282
     */
3283 4
    public function randomKey()
3284
    {
3285 4
        $result = $this->randomKeys(1);
3286
3287 4
        if (!isset($result[0])) {
3288
            $result[0] = null;
3289
        }
3290
3291 4
        return $result[0];
3292
    }
3293
3294
    /**
3295
     * Pick a given number of random keys/indexes out of this array.
3296
     *
3297
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3298
     *
3299
     * @throws \RangeException If array is empty
3300
     *
3301
     * @return static
3302
     *                <p>(Immutable)</p>
3303
     */
3304 13
    public function randomKeys(int $number): self
3305
    {
3306 13
        $this->generatorToArray();
3307
3308 13
        $count = \count($this->array, \COUNT_NORMAL);
3309
3310 13
        if ($number === 0 || $number > $count) {
3311 2
            throw new \RangeException(
3312 2
                \sprintf(
3313 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3314 2
                    $number,
3315 2
                    $count
3316
                )
3317
            );
3318
        }
3319
3320 11
        $result = (array) \array_rand($this->array, $number);
3321
3322 11
        return static::create(
3323 11
            $result,
3324 11
            $this->iteratorClass,
3325 11
            false
3326
        );
3327
    }
3328
3329
    /**
3330
     * Get a random value from the current array.
3331
     *
3332
     * @param int|null $number <p>How many values you will take?</p>
3333
     *
3334
     * @return static
3335
     *                <p>(Mutable)</p>
3336
     */
3337 17
    public function randomMutable(int $number = null): self
3338
    {
3339 17
        $this->generatorToArray();
3340
3341 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...
3342
            return static::create(
3343
                [],
3344
                $this->iteratorClass,
3345
                false
3346
            );
3347
        }
3348
3349 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...
3350
            /** @noinspection NonSecureArrayRandUsageInspection */
3351 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3352 7
            $this->array = $arrayRandValue;
3353
3354 7
            return $this;
3355
        }
3356
3357
        /** @noinspection NonSecureShuffleUsageInspection */
3358 11
        \shuffle($this->array);
3359
3360 11
        return $this->firstsMutable($number);
3361
    }
3362
3363
    /**
3364
     * Pick a random value from the values of this array.
3365
     *
3366
     * @return mixed
3367
     *               <p>Get a random value or null if there wasn't a value.</p>
3368
     */
3369 4
    public function randomValue()
3370
    {
3371 4
        $result = $this->randomImmutable();
3372
3373 4
        if (!isset($result[0])) {
3374
            $result[0] = null;
3375
        }
3376
3377 4
        return $result[0];
3378
    }
3379
3380
    /**
3381
     * Pick a given number of random values out of this array.
3382
     *
3383
     * @param int $number
3384
     *
3385
     * @return static
3386
     *                <p>(Mutable)</p>
3387
     */
3388 7
    public function randomValues(int $number): self
3389
    {
3390 7
        return $this->randomMutable($number);
3391
    }
3392
3393
    /**
3394
     * Get a random value from an array, with the ability to skew the results.
3395
     *
3396
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3397
     *
3398
     * @param array    $array
3399
     * @param int|null $number <p>How many values you will take?</p>
3400
     *
3401
     * @return static
3402
     *                <p>(Immutable)</p>
3403
     */
3404 9
    public function randomWeighted(array $array, int $number = null): self
3405
    {
3406
        // init
3407 9
        $options = [];
3408
3409 9
        foreach ($array as $option => $weight) {
3410 9
            if ($this->searchIndex($option) !== false) {
3411 2
                for ($i = 0; $i < $weight; ++$i) {
3412 1
                    $options[] = $option;
3413
                }
3414
            }
3415
        }
3416
3417 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3418
    }
3419
3420
    /**
3421
     * Reduce the current array via callable e.g. anonymous-function.
3422
     *
3423
     * @param callable $callable
3424
     * @param array    $init
3425
     *
3426
     * @return static
3427
     *                <p>(Immutable)</p>
3428
     */
3429 16
    public function reduce($callable, array $init = []): self
3430
    {
3431 16
        if ($this->generator) {
3432 1
            $result = $init;
3433
3434 1
            foreach ($this->getGenerator() as $value) {
3435 1
                $result = $callable($result, $value);
3436
            }
3437
3438 1
            return static::create(
3439 1
                $result,
3440 1
                $this->iteratorClass,
3441 1
                false
3442
            );
3443
        }
3444
3445 16
        $result = \array_reduce($this->array, $callable, $init);
3446
3447 16
        if ($result === null) {
3448
            $this->array = [];
3449
        } else {
3450 16
            $this->array = (array) $result;
3451
        }
3452
3453 16
        return static::create(
3454 16
            $this->array,
3455 16
            $this->iteratorClass,
3456 16
            false
3457
        );
3458
    }
3459
3460
    /**
3461
     * @param bool $unique
3462
     *
3463
     * @return static
3464
     *                <p>(Immutable)</p>
3465
     */
3466 14
    public function reduce_dimension(bool $unique = true): self
3467
    {
3468
        // init
3469 14
        $result = [[]];
3470
3471 14
        foreach ($this->getGenerator() as $val) {
3472 12
            if (\is_array($val)) {
3473 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
3474
            } else {
3475 12
                $result[] = [$val];
3476
            }
3477
        }
3478 14
        $result = \array_merge(...$result);
3479
3480 14
        $resultArrayy = new self($result);
3481
3482 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3483
    }
3484
3485
    /**
3486
     * Create a numerically re-indexed Arrayy object.
3487
     *
3488
     * @return static
3489
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3490
     */
3491 9
    public function reindex(): self
3492
    {
3493 9
        $this->generatorToArray();
3494
3495 9
        $this->array = \array_values($this->array);
3496
3497 9
        return $this;
3498
    }
3499
3500
    /**
3501
     * Return all items that fail the truth test.
3502
     *
3503
     * @param \Closure $closure
3504
     *
3505
     * @return static
3506
     *                <p>(Immutable)</p>
3507
     */
3508 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...
3509
    {
3510
        // init
3511 1
        $filtered = [];
3512
3513 1
        foreach ($this->getGenerator() as $key => $value) {
3514 1
            if (!$closure($value, $key)) {
3515 1
                $filtered[$key] = $value;
3516
            }
3517
        }
3518
3519 1
        return static::create(
3520 1
            $filtered,
3521 1
            $this->iteratorClass,
3522 1
            false
3523
        );
3524
    }
3525
3526
    /**
3527
     * Remove a value from the current array (optional using dot-notation).
3528
     *
3529
     * @param mixed $key
3530
     *
3531
     * @return static
3532
     *                <p>(Mutable)</p>
3533
     */
3534 18
    public function remove($key): self
3535
    {
3536
        // recursive call
3537 18
        if (\is_array($key)) {
3538
            foreach ($key as $k) {
3539
                $this->internalRemove($k);
3540
            }
3541
3542
            return static::create(
3543
                $this->getArray(),
3544
                $this->iteratorClass,
3545
                false
3546
            );
3547
        }
3548
3549 18
        $this->internalRemove($key);
3550
3551 18
        return static::create(
3552 18
            $this->getArray(),
3553 18
            $this->iteratorClass,
3554 18
            false
3555
        );
3556
    }
3557
3558
    /**
3559
     * Remove the first value from the current array.
3560
     *
3561
     * @return static
3562
     *                <p>(Immutable)</p>
3563
     */
3564 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...
3565
    {
3566 7
        $tmpArray = $this->getArray();
3567
3568 7
        \array_shift($tmpArray);
3569
3570 7
        return static::create(
3571 7
            $tmpArray,
3572 7
            $this->iteratorClass,
3573 7
            false
3574
        );
3575
    }
3576
3577
    /**
3578
     * Remove the last value from the current array.
3579
     *
3580
     * @return static
3581
     *                <p>(Immutable)</p>
3582
     */
3583 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...
3584
    {
3585 7
        $tmpArray = $this->getArray();
3586
3587 7
        \array_pop($tmpArray);
3588
3589 7
        return static::create(
3590 7
            $tmpArray,
3591 7
            $this->iteratorClass,
3592 7
            false
3593
        );
3594
    }
3595
3596
    /**
3597
     * Removes a particular value from an array (numeric or associative).
3598
     *
3599
     * @param mixed $value
3600
     *
3601
     * @return static
3602
     *                <p>(Immutable)</p>
3603
     */
3604 7
    public function removeValue($value): self
3605
    {
3606 7
        $this->generatorToArray();
3607
3608
        // init
3609 7
        $isNumericArray = true;
3610
3611 7
        foreach ($this->getGenerator() as $key => $item) {
3612 6
            if ($item === $value) {
3613 6
                if (!\is_int($key)) {
3614
                    $isNumericArray = false;
3615
                }
3616 6
                unset($this->array[$key]);
3617
            }
3618
        }
3619
3620 7
        if ($isNumericArray) {
3621 7
            $this->array = \array_values($this->array);
3622
        }
3623
3624 7
        return static::create(
3625 7
            $this->array,
3626 7
            $this->iteratorClass,
3627 7
            false
3628
        );
3629
    }
3630
3631
    /**
3632
     * Generate array of repeated arrays.
3633
     *
3634
     * @param int $times <p>How many times has to be repeated.</p>
3635
     *
3636
     * @return static
3637
     *                <p>(Immutable)</p>
3638
     */
3639 1
    public function repeat($times): self
3640
    {
3641 1
        if ($times === 0) {
3642 1
            return new static();
3643
        }
3644
3645 1
        return static::create(
3646 1
            \array_fill(0, (int) $times, $this->getArray()),
3647 1
            $this->iteratorClass,
3648 1
            false
3649
        );
3650
    }
3651
3652
    /**
3653
     * Replace a key with a new key/value pair.
3654
     *
3655
     * @param mixed $replace
3656
     * @param mixed $key
3657
     * @param mixed $value
3658
     *
3659
     * @return static
3660
     *                <p>(Immutable)</p>
3661
     */
3662 2
    public function replace($replace, $key, $value): self
3663
    {
3664 2
        $that = clone $this;
3665
3666 2
        return $that->remove($replace)
3667 2
            ->set($key, $value);
3668
    }
3669
3670
    /**
3671
     * Create an array using the current array as values and the other array as keys.
3672
     *
3673
     * @param array $keys <p>An array of keys.</p>
3674
     *
3675
     * @return static
3676
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3677
     */
3678 2
    public function replaceAllKeys(array $keys): self
3679
    {
3680 2
        return static::create(
3681 2
            \array_combine($keys, $this->getArray()),
3682 2
            $this->iteratorClass,
3683 2
            false
3684
        );
3685
    }
3686
3687
    /**
3688
     * Create an array using the current array as keys and the other array as values.
3689
     *
3690
     * @param array $array <p>An array o values.</p>
3691
     *
3692
     * @return static
3693
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3694
     */
3695 2
    public function replaceAllValues(array $array): self
3696
    {
3697 2
        return static::create(
3698 2
            \array_combine($this->array, $array),
3699 2
            $this->iteratorClass,
3700 2
            false
3701
        );
3702
    }
3703
3704
    /**
3705
     * Replace the keys in an array with another set.
3706
     *
3707
     * @param array $keys <p>An array of keys matching the array's size</p>
3708
     *
3709
     * @return static
3710
     *                <p>(Immutable)</p>
3711
     */
3712 1
    public function replaceKeys(array $keys): self
3713
    {
3714 1
        $values = \array_values($this->getArray());
3715 1
        $result = \array_combine($keys, $values);
3716
3717 1
        return static::create(
3718 1
            $result,
3719 1
            $this->iteratorClass,
3720 1
            false
3721
        );
3722
    }
3723
3724
    /**
3725
     * Replace the first matched value in an array.
3726
     *
3727
     * @param mixed $search      <p>The value to replace.</p>
3728
     * @param mixed $replacement <p>The value to replace.</p>
3729
     *
3730
     * @return static
3731
     *                <p>(Immutable)</p>
3732
     */
3733 3
    public function replaceOneValue($search, $replacement = ''): self
3734
    {
3735 3
        $array = $this->getArray();
3736 3
        $key = \array_search($search, $array, true);
3737
3738 3
        if ($key !== false) {
3739 3
            $array[$key] = $replacement;
3740
        }
3741
3742 3
        return static::create(
3743 3
            $array,
3744 3
            $this->iteratorClass,
3745 3
            false
3746
        );
3747
    }
3748
3749
    /**
3750
     * Replace values in the current array.
3751
     *
3752
     * @param mixed $search      <p>The value to replace.</p>
3753
     * @param mixed $replacement <p>What to replace it with.</p>
3754
     *
3755
     * @return static
3756
     *                <p>(Immutable)</p>
3757
     */
3758 1
    public function replaceValues($search, $replacement = ''): self
3759
    {
3760 1
        $array = $this->each(
3761
            static function ($value) use ($search, $replacement) {
3762 1
                return \str_replace($search, $replacement, $value);
3763 1
            }
3764
        );
3765
3766 1
        return $array;
3767
    }
3768
3769
    /**
3770
     * Get the last elements from index $from until the end of this array.
3771
     *
3772
     * @param int $from
3773
     *
3774
     * @return static
3775
     *                <p>(Immutable)</p>
3776
     */
3777 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...
3778
    {
3779 15
        $tmpArray = $this->getArray();
3780
3781 15
        return static::create(
3782 15
            \array_splice($tmpArray, $from),
3783 15
            $this->iteratorClass,
3784 15
            false
3785
        );
3786
    }
3787
3788
    /**
3789
     * Return the array in the reverse order.
3790
     *
3791
     * @return static
3792
     *                <p>(Mutable) Return this Arrayy object.</p>
3793
     */
3794 8
    public function reverse(): self
3795
    {
3796 8
        $this->generatorToArray();
3797
3798 8
        $this->array = \array_reverse($this->array);
3799
3800 8
        return $this;
3801
    }
3802
3803
    /**
3804
     * Sort an array in reverse order.
3805
     *
3806
     * @param int $sort_flags [optional] <p>
3807
     *                        You may modify the behavior of the sort using the optional
3808
     *                        parameter sort_flags, for details
3809
     *                        see sort.
3810
     *                        </p>
3811
     *
3812
     * @return static
3813
     *                <p>(Mutable) Return this Arrayy object.</p>
3814
     */
3815 4
    public function rsort(int $sort_flags = 0): self
3816
    {
3817 4
        $this->generatorToArray();
3818
3819 4
        \rsort($this->array, $sort_flags);
3820
3821 4
        return $this;
3822
    }
3823
3824
    /**
3825
     * Search for the first index of the current array via $value.
3826
     *
3827
     * @param mixed $value
3828
     *
3829
     * @return false|float|int|string
3830
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3831
     */
3832 20
    public function searchIndex($value)
3833
    {
3834 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3835 19
            if ($value === $valueFromArray) {
3836 9
                return $keyFromArray;
3837
            }
3838
        }
3839
3840 11
        return false;
3841
    }
3842
3843
    /**
3844
     * Search for the value of the current array via $index.
3845
     *
3846
     * @param mixed $index
3847
     *
3848
     * @return static
3849
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3850
     */
3851 9
    public function searchValue($index): self
3852
    {
3853 9
        $this->generatorToArray();
3854
3855
        // init
3856 9
        $return = [];
3857
3858 9
        if ($this->array === []) {
3859
            return static::create(
3860
                [],
3861
                $this->iteratorClass,
3862
                false
3863
            );
3864
        }
3865
3866
        // php cast "bool"-index into "int"-index
3867 9
        if ((bool) $index === $index) {
3868 1
            $index = (int) $index;
3869
        }
3870
3871 9
        if ($this->offsetExists($index)) {
3872 7
            $return = [$this->array[$index]];
3873
        }
3874
3875 9
        return static::create(
3876 9
            $return,
3877 9
            $this->iteratorClass,
3878 9
            false
3879
        );
3880
    }
3881
3882
    /**
3883
     * Set a value for the current array (optional using dot-notation).
3884
     *
3885
     * @param string $key   <p>The key to set.</p>
3886
     * @param mixed  $value <p>Its value.</p>
3887
     *
3888
     * @return static
3889
     *                <p>(Mutable)</p>
3890
     */
3891 18
    public function set($key, $value): self
3892
    {
3893 18
        $this->generatorToArray();
3894
3895 18
        $this->internalSet($key, $value);
3896
3897 18
        return $this;
3898
    }
3899
3900
    /**
3901
     * Get a value from a array and set it if it was not.
3902
     *
3903
     * WARNING: this method only set the value, if the $key is not already set
3904
     *
3905
     * @param mixed $key      <p>The key</p>
3906
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3907
     *
3908
     * @return mixed
3909
     *               <p>(Mutable)</p>
3910
     */
3911 11
    public function setAndGet($key, $fallback = null)
3912
    {
3913 11
        $this->generatorToArray();
3914
3915
        // If the key doesn't exist, set it.
3916 11
        if (!$this->has($key)) {
3917 4
            $this->array = $this->set($key, $fallback)->getArray();
3918
        }
3919
3920 11
        return $this->get($key);
3921
    }
3922
3923
    /**
3924
     * Shifts a specified value off the beginning of array.
3925
     *
3926
     * @return mixed
3927
     *               <p>(Mutable) A shifted element from the current array.</p>
3928
     */
3929 4
    public function shift()
3930
    {
3931 4
        $this->generatorToArray();
3932
3933 4
        return \array_shift($this->array);
3934
    }
3935
3936
    /**
3937
     * Shuffle the current array.
3938
     *
3939
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3940
     * @param array $array  [optional]
3941
     *
3942
     * @return static
3943
     *                <p>(Immutable)</p>
3944
     */
3945 1
    public function shuffle(bool $secure = false, array $array = null): self
3946
    {
3947 1
        if ($array === null) {
3948 1
            $array = $this->getArray();
3949
        }
3950
3951 1
        if ($secure !== true) {
3952
            /** @noinspection NonSecureShuffleUsageInspection */
3953 1
            \shuffle($array);
3954
        } else {
3955 1
            $size = \count($array, \COUNT_NORMAL);
3956 1
            $keys = \array_keys($array);
3957 1
            for ($i = $size - 1; $i > 0; --$i) {
3958
                try {
3959 1
                    $r = \random_int(0, $i);
3960
                } catch (\Exception $e) {
3961
                    /** @noinspection RandomApiMigrationInspection */
3962
                    $r = \mt_rand(0, $i);
3963
                }
3964 1
                if ($r !== $i) {
3965 1
                    $temp = $array[$keys[$r]];
3966 1
                    $array[$keys[$r]] = $array[$keys[$i]];
3967 1
                    $array[$keys[$i]] = $temp;
3968
                }
3969
            }
3970
3971
            // reset indices
3972 1
            $array = \array_values($array);
3973
        }
3974
3975 1
        foreach ($array as $key => $value) {
3976
            // check if recursive is needed
3977 1
            if (\is_array($value) === true) {
3978
                $array[$key] = $this->shuffle($secure, $value);
3979
            }
3980
        }
3981
3982 1
        return static::create(
3983 1
            $array,
3984 1
            $this->iteratorClass,
3985 1
            false
3986
        );
3987
    }
3988
3989
    /**
3990
     * Count the values from the current array.
3991
     *
3992
     * alias: for "Arrayy->count()"
3993
     *
3994
     * @param int $mode
3995
     *
3996
     * @return int
3997
     */
3998 20
    public function size(int $mode = \COUNT_NORMAL): int
3999
    {
4000 20
        return $this->count($mode);
4001
    }
4002
4003
    /**
4004
     * Checks whether array has exactly $size items.
4005
     *
4006
     * @param int $size
4007
     *
4008
     * @return bool
4009
     */
4010 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...
4011
    {
4012
        // init
4013 1
        $itemsTempCount = 0;
4014
4015 1
        foreach ($this->getGenerator() as $key => $value) {
4016 1
            ++$itemsTempCount;
4017 1
            if ($itemsTempCount > $size) {
4018 1
                return false;
4019
            }
4020
        }
4021
4022 1
        return $itemsTempCount === $size;
4023
    }
4024
4025
    /**
4026
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4027
     * smaller than $fromSize.
4028
     *
4029
     * @param int $fromSize
4030
     * @param int $toSize
4031
     *
4032
     * @return bool
4033
     */
4034 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4035
    {
4036 1
        if ($fromSize > $toSize) {
4037 1
            $tmp = $toSize;
4038 1
            $toSize = $fromSize;
4039 1
            $fromSize = $tmp;
4040
        }
4041
4042
        // init
4043 1
        $itemsTempCount = 0;
4044
4045 1
        foreach ($this->getGenerator() as $key => $value) {
4046 1
            ++$itemsTempCount;
4047 1
            if ($itemsTempCount > $toSize) {
4048 1
                return false;
4049
            }
4050
        }
4051
4052 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4053
    }
4054
4055
    /**
4056
     * Checks whether array has more than $size items.
4057
     *
4058
     * @param int $size
4059
     *
4060
     * @return bool
4061
     */
4062 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...
4063
    {
4064
        // init
4065 1
        $itemsTempCount = 0;
4066
4067 1
        foreach ($this->getGenerator() as $key => $value) {
4068 1
            ++$itemsTempCount;
4069 1
            if ($itemsTempCount > $size) {
4070 1
                return true;
4071
            }
4072
        }
4073
4074 1
        return $itemsTempCount > $size;
4075
    }
4076
4077
    /**
4078
     * Checks whether array has less than $size items.
4079
     *
4080
     * @param int $size
4081
     *
4082
     * @return bool
4083
     */
4084 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...
4085
    {
4086
        // init
4087 1
        $itemsTempCount = 0;
4088
4089 1
        foreach ($this->getGenerator() as $key => $value) {
4090 1
            ++$itemsTempCount;
4091 1
            if ($itemsTempCount > $size) {
4092 1
                return false;
4093
            }
4094
        }
4095
4096 1
        return $itemsTempCount < $size;
4097
    }
4098
4099
    /**
4100
     * Counts all elements in an array, or something in an object.
4101
     *
4102
     * <p>
4103
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4104
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4105
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4106
     * implemented and used in PHP.
4107
     * </p>
4108
     *
4109
     * @return int
4110
     *             <p>
4111
     *             The number of elements in var, which is
4112
     *             typically an array, since anything else will have one
4113
     *             element.
4114
     *             </p>
4115
     *             <p>
4116
     *             If var is not an array or an object with
4117
     *             implemented Countable interface,
4118
     *             1 will be returned.
4119
     *             There is one exception, if var is &null;,
4120
     *             0 will be returned.
4121
     *             </p>
4122
     *             <p>
4123
     *             Caution: count may return 0 for a variable that isn't set,
4124
     *             but it may also return 0 for a variable that has been initialized with an
4125
     *             empty array. Use isset to test if a variable is set.
4126
     *             </p>
4127
     */
4128 10
    public function sizeRecursive(): int
4129
    {
4130 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4131
    }
4132
4133
    /**
4134
     * Extract a slice of the array.
4135
     *
4136
     * @param int      $offset       <p>Slice begin index.</p>
4137
     * @param int|null $length       <p>Length of the slice.</p>
4138
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4139
     *
4140
     * @return static
4141
     *                <p>A slice of the original array with length $length.</p>
4142
     */
4143 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
4144
    {
4145 4
        return static::create(
4146 4
            \array_slice(
4147 4
                $this->getArray(),
4148 4
                $offset,
4149 4
                $length,
4150 4
                $preserveKeys
4151
            ),
4152 4
            $this->iteratorClass,
4153 4
            false
4154
        );
4155
    }
4156
4157
    /**
4158
     * Sort the current array and optional you can keep the keys.
4159
     *
4160
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4161
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4162
     *                              <strong>SORT_NATURAL</strong></p>
4163
     * @param bool       $keepKeys
4164
     *
4165
     * @return static
4166
     *                <p>(Mutable) Return this Arrayy object.</p>
4167
     */
4168 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4169
    {
4170 20
        $this->generatorToArray();
4171
4172 20
        return $this->sorting(
4173 20
            $this->array,
4174 20
            $direction,
4175 20
            $strategy,
4176 20
            $keepKeys
4177
        );
4178
    }
4179
4180
    /**
4181
     * Sort the current array by key.
4182
     *
4183
     * @see http://php.net/manual/en/function.ksort.php
4184
     * @see http://php.net/manual/en/function.krsort.php
4185
     *
4186
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4187
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4188
     *                              <strong>SORT_NATURAL</strong></p>
4189
     *
4190
     * @return static
4191
     *                <p>(Mutable) Return this Arrayy object.</p>
4192
     */
4193 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4194
    {
4195 18
        $this->generatorToArray();
4196
4197 18
        $this->sorterKeys($this->array, $direction, $strategy);
4198
4199 18
        return $this;
4200
    }
4201
4202
    /**
4203
     * Sort the current array by value.
4204
     *
4205
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4206
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4207
     *                              <strong>SORT_NATURAL</strong></p>
4208
     *
4209
     * @return static
4210
     *                <p>(Mutable)</p>
4211
     */
4212 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4213
    {
4214 1
        return $this->sort($direction, $strategy, true);
4215
    }
4216
4217
    /**
4218
     * Sort the current array by value.
4219
     *
4220
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4221
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4222
     *                              <strong>SORT_NATURAL</strong></p>
4223
     *
4224
     * @return static
4225
     *                <p>(Mutable)</p>
4226
     */
4227 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4228
    {
4229 1
        return $this->sort($direction, $strategy, false);
4230
    }
4231
4232
    /**
4233
     * Sort a array by value, by a closure or by a property.
4234
     *
4235
     * - If the sorter is null, the array is sorted naturally.
4236
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4237
     *
4238
     * @param callable|string|null $sorter
4239
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4240
     *                                        <strong>SORT_DESC</strong></p>
4241
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4242
     *                                        <strong>SORT_NATURAL</strong></p>
4243
     *
4244
     * @return static
4245
     *                <p>(Immutable)</p>
4246
     */
4247 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4248
    {
4249 1
        $array = $this->getArray();
4250 1
        $direction = $this->getDirection($direction);
4251
4252
        // Transform all values into their results.
4253 1
        if ($sorter) {
4254 1
            $arrayy = static::create(
4255 1
                $array,
4256 1
                $this->iteratorClass,
4257 1
                false
4258
            );
4259
4260 1
            $results = $arrayy->each(
4261
                function ($value) use ($sorter) {
4262 1
                    if (\is_callable($sorter)) {
4263 1
                        return $sorter($value);
4264
                    }
4265
4266 1
                    return $this->get($sorter, null, $this->getArray());
4267 1
                }
4268
            );
4269
4270 1
            $results = $results->getArray();
4271
        } else {
4272 1
            $results = $array;
4273
        }
4274
4275
        // Sort by the results and replace by original values
4276 1
        \array_multisort($results, $direction, $strategy, $array);
4277
4278 1
        return static::create(
4279 1
            $array,
4280 1
            $this->iteratorClass,
4281 1
            false
4282
        );
4283
    }
4284
4285
    /**
4286
     * Split an array in the given amount of pieces.
4287
     *
4288
     * @param int  $numberOfPieces
4289
     * @param bool $keepKeys
4290
     *
4291
     * @return static
4292
     *                <p>(Immutable)</p>
4293
     */
4294 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4295
    {
4296 1
        $this->generatorToArray();
4297
4298 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4299
4300 1
        if ($arrayCount === 0) {
4301 1
            $result = [];
4302
        } else {
4303 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4304 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4305
        }
4306
4307 1
        return static::create(
4308 1
            $result,
4309 1
            $this->iteratorClass,
4310 1
            false
4311
        );
4312
    }
4313
4314
    /**
4315
     * Stripe all empty items.
4316
     *
4317
     * @return static
4318
     *                <p>(Immutable)</p>
4319
     */
4320 1
    public function stripEmpty(): self
4321
    {
4322 1
        return $this->filter(
4323
            static function ($item) {
4324 1
                if ($item === null) {
4325 1
                    return false;
4326
                }
4327
4328 1
                return (bool) \trim((string) $item);
4329 1
            }
4330
        );
4331
    }
4332
4333
    /**
4334
     * Swap two values between positions by key.
4335
     *
4336
     * @param int|string $swapA <p>a key in the array</p>
4337
     * @param int|string $swapB <p>a key in the array</p>
4338
     *
4339
     * @return static
4340
     *                <p>(Immutable)</p>
4341
     */
4342 1
    public function swap($swapA, $swapB): self
4343
    {
4344 1
        $array = $this->getArray();
4345
4346 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4347
4348 1
        return static::create(
4349 1
            $array,
4350 1
            $this->iteratorClass,
4351 1
            false
4352
        );
4353
    }
4354
4355
    /**
4356
     * alias: for "Arrayy->getArray()"
4357
     *
4358
     * @see Arrayy::getArray()
4359
     */
4360 205
    public function toArray()
4361
    {
4362 205
        return $this->getArray();
4363
    }
4364
4365
    /**
4366
     * Convert the current array to JSON.
4367
     *
4368
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4369
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4370
     *
4371
     * @return string
4372
     */
4373 6
    public function toJson(int $options = 0, int $depth = 512): string
4374
    {
4375 6
        $return = \json_encode($this->getArray(), $options, $depth);
4376 6
        if ($return === false) {
4377
            return '';
4378
        }
4379
4380 6
        return $return;
4381
    }
4382
4383
    /**
4384
     * Implodes array to a string with specified separator.
4385
     *
4386
     * @param string $separator [optional] <p>The element's separator.</p>
4387
     *
4388
     * @return string
4389
     *                <p>The string representation of array, separated by ",".</p>
4390
     */
4391 20
    public function toString(string $separator = ','): string
4392
    {
4393 20
        return $this->implode($separator);
4394
    }
4395
4396
    /**
4397
     * Return a duplicate free copy of the current array.
4398
     *
4399
     * @return static
4400
     *                <p>(Mutable)</p>
4401
     */
4402 12
    public function unique(): self
4403
    {
4404
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4405
4406 12
        $this->array = $this->reduce(
4407
            static function ($resultArray, $value) {
4408 11
                if (!\in_array($value, $resultArray, true)) {
4409 11
                    $resultArray[] = $value;
4410
                }
4411
4412 11
                return $resultArray;
4413 12
            },
4414 12
            []
4415 12
        )->getArray();
4416 12
        $this->generator = null;
4417
4418 12
        return $this;
4419
    }
4420
4421
    /**
4422
     * Return a duplicate free copy of the current array. (with the old keys)
4423
     *
4424
     * @return static
4425
     *                <p>(Mutable)</p>
4426
     */
4427 11
    public function uniqueKeepIndex(): self
4428
    {
4429
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4430
4431
        // init
4432 11
        $array = $this->getArray();
4433
4434 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...
4435 11
            \array_keys($array),
4436
            static function ($resultArray, $key) use ($array) {
4437 10
                if (!\in_array($array[$key], $resultArray, true)) {
4438 10
                    $resultArray[$key] = $array[$key];
4439
                }
4440
4441 10
                return $resultArray;
4442 11
            },
4443 11
            []
4444
        );
4445 11
        $this->generator = null;
4446
4447 11
        if ($this->array === null) {
4448
            $this->array = [];
4449
        } else {
4450 11
            $this->array = (array) $this->array;
4451
        }
4452
4453 11
        return $this;
4454
    }
4455
4456
    /**
4457
     * alias: for "Arrayy->unique()"
4458
     *
4459
     * @return static
4460
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4461
     *
4462
     * @see Arrayy::unique()
4463
     */
4464 10
    public function uniqueNewIndex(): self
4465
    {
4466 10
        return $this->unique();
4467
    }
4468
4469
    /**
4470
     * Prepends one or more values to the beginning of array at once.
4471
     *
4472
     * @return static
4473
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4474
     */
4475 4 View Code Duplication
    public function unshift(/* variadic arguments allowed */): 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...
4476
    {
4477 4
        $this->generatorToArray();
4478
4479 4
        if (\func_num_args()) {
4480 4
            $args = \func_get_args();
4481 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...
4482
        }
4483
4484 4
        return $this;
4485
    }
4486
4487
    /**
4488
     * Get all values from a array.
4489
     *
4490
     * @return static
4491
     *                <p>(Immutable)</p>
4492
     */
4493 2
    public function values(): self
4494
    {
4495 2
        return static::create(
4496
            function () {
4497
                /** @noinspection YieldFromCanBeUsedInspection */
4498 2
                foreach ($this->getGenerator() as $value) {
4499 2
                    yield $value;
4500
                }
4501 2
            },
4502 2
            $this->iteratorClass,
4503 2
            false
4504
        );
4505
    }
4506
4507
    /**
4508
     * Apply the given function to every element in the array, discarding the results.
4509
     *
4510
     * @param callable $callable
4511
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4512
     *
4513
     * @return static
4514
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4515
     */
4516 11
    public function walk($callable, bool $recursive = false): self
4517
    {
4518 11
        $this->generatorToArray();
4519
4520 11
        if ($recursive === true) {
4521 6
            \array_walk_recursive($this->array, $callable);
4522
        } else {
4523 5
            \array_walk($this->array, $callable);
4524
        }
4525
4526 11
        return $this;
4527
    }
4528
4529
    /**
4530
     * Convert an array into a object.
4531
     *
4532
     * @param array $array PHP array
4533
     *
4534
     * @return \stdClass
4535
     */
4536 4
    protected static function arrayToObject(array $array = []): \stdClass
4537
    {
4538
        // init
4539 4
        $object = new \stdClass();
4540
4541 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4542 1
            return $object;
4543
        }
4544
4545 3
        foreach ($array as $name => $value) {
4546 3
            if (\is_array($value)) {
4547 1
                $object->{$name} = self::arrayToObject($value);
4548
            } else {
4549 3
                $object->{$name} = $value;
4550
            }
4551
        }
4552
4553 3
        return $object;
4554
    }
4555
4556
    /**
4557
     * @param array|\Generator|null $input        <p>
4558
     *                                            An array containing keys to return.
4559
     *                                            </p>
4560
     * @param mixed                 $search_value [optional] <p>
4561
     *                                            If specified, then only keys containing these values are returned.
4562
     *                                            </p>
4563
     * @param bool                  $strict       [optional] <p>
4564
     *                                            Determines if strict comparison (===) should be used during the
4565
     *                                            search.
4566
     *                                            </p>
4567
     *
4568
     * @return array
4569
     *               <p>an array of all the keys in input</p>
4570
     */
4571 10
    protected function array_keys_recursive(
4572
        $input = null,
4573
        $search_value = null,
4574
        bool $strict = true
4575
    ): array {
4576
        // init
4577 10
        $keys = [];
4578 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
4579
4580 10
        if ($input === null) {
4581
            $input = $this->getGenerator();
4582
        }
4583
4584 10
        foreach ($input as $key => $value) {
4585
            if (
4586 10
                $search_value === null
4587
                ||
4588
                (
4589
                    \is_array($search_value) === true
4590
                    &&
4591 10
                    \in_array($key, $search_value, $strict)
4592
                )
4593
            ) {
4594 10
                $keys[] = $key;
4595
            }
4596
4597
            // check if recursive is needed
4598 10
            if (\is_array($value) === true) {
4599 4
                $keysTmp[] = $this->array_keys_recursive($value);
4600
            }
4601
        }
4602
4603 10
        return \array_merge($keys, ...$keysTmp);
4604
    }
4605
4606
    /**
4607
     * @param mixed      $path
4608
     * @param callable   $callable
4609
     * @param array|null $currentOffset
4610
     */
4611 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4612
    {
4613 4
        $this->generatorToArray();
4614
4615 4
        if ($currentOffset === null) {
4616 4
            $currentOffset = &$this->array;
4617
        }
4618
4619 4
        $explodedPath = \explode($this->pathSeparator, $path);
4620 4
        if ($explodedPath === false) {
4621
            return;
4622
        }
4623
4624 4
        $nextPath = \array_shift($explodedPath);
4625
4626 4
        if (!isset($currentOffset[$nextPath])) {
4627
            return;
4628
        }
4629
4630 4
        if (!empty($explodedPath)) {
4631 1
            $this->callAtPath(
4632 1
                \implode($this->pathSeparator, $explodedPath),
4633 1
                $callable,
4634 1
                $currentOffset[$nextPath]
4635
            );
4636
        } else {
4637 4
            $callable($currentOffset[$nextPath]);
4638
        }
4639 4
    }
4640
4641
    /**
4642
     * create a fallback for array
4643
     *
4644
     * 1. use the current array, if it's a array
4645
     * 2. fallback to empty array, if there is nothing
4646
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4647
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4648
     * 5. call "__toArray()" on object, if the method exists
4649
     * 6. cast a string or object with "__toString()" into an array
4650
     * 7. throw a "InvalidArgumentException"-Exception
4651
     *
4652
     * @param mixed $data
4653
     *
4654
     * @throws \InvalidArgumentException
4655
     *
4656
     * @return array
4657
     */
4658 991
    protected function fallbackForArray(&$data): array
4659
    {
4660 991
        $data = $this->internalGetArray($data);
4661
4662 991
        if ($data === null) {
4663 2
            throw new \InvalidArgumentException(
4664 2
                'Passed value should be a array'
4665
            );
4666
        }
4667
4668 989
        return $data;
4669
    }
4670
4671
    /**
4672
     * Get correct PHP constant for direction.
4673
     *
4674
     * @param int|string $direction
4675
     *
4676
     * @return int
4677
     */
4678 39
    protected function getDirection($direction): int
4679
    {
4680 39
        if (\is_string($direction)) {
4681 10
            $direction = \strtolower($direction);
4682
4683 10
            if ($direction === 'desc') {
4684 2
                $direction = \SORT_DESC;
4685
            } else {
4686 8
                $direction = \SORT_ASC;
4687
            }
4688
        }
4689
4690
        if (
4691 39
            $direction !== \SORT_DESC
4692
            &&
4693 39
            $direction !== \SORT_ASC
4694
        ) {
4695
            $direction = \SORT_ASC;
4696
        }
4697
4698 39
        return $direction;
4699
    }
4700
4701
    /**
4702
     * @param mixed $glue
4703
     * @param mixed $pieces
4704
     * @param bool  $useKeys
4705
     *
4706
     * @return string
4707
     */
4708 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4709
    {
4710 36
        if ($pieces instanceof self) {
4711 1
            $pieces = $pieces->getArray();
4712
        }
4713
4714 36
        if (\is_array($pieces)) {
4715 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4716 36
            $pieces_count_not_zero = $pieces_count > 0;
4717
4718 36
            return \implode(
4719 36
                $glue,
4720 36
                \array_map(
4721 36
                    [$this, 'implode_recursive'],
4722 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4723 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4724
                )
4725
            );
4726
        }
4727
4728
        if (
4729 36
            \is_scalar($pieces)
4730
            ||
4731 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
4732
        ) {
4733 31
            return (string) $pieces;
4734
        }
4735
4736 9
        return '';
4737
    }
4738
4739
    /**
4740
     * @param mixed                 $needle   <p>
4741
     *                                        The searched value.
4742
     *                                        </p>
4743
     *                                        <p>
4744
     *                                        If needle is a string, the comparison is done
4745
     *                                        in a case-sensitive manner.
4746
     *                                        </p>
4747
     * @param array|\Generator|null $haystack <p>
4748
     *                                        The array.
4749
     *                                        </p>
4750
     * @param bool                  $strict   [optional] <p>
4751
     *                                        If the third parameter strict is set to true
4752
     *                                        then the in_array function will also check the
4753
     *                                        types of the
4754
     *                                        needle in the haystack.
4755
     *                                        </p>
4756
     *
4757
     * @return bool
4758
     *              <p>true if needle is found in the array, false otherwise</p>
4759
     */
4760 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4761
    {
4762 18
        if ($haystack === null) {
4763
            $haystack = $this->getGenerator();
4764
        }
4765
4766 18
        foreach ($haystack as $item) {
4767 14
            if (\is_array($item) === true) {
4768 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4769
            } else {
4770
                /** @noinspection NestedPositiveIfStatementsInspection */
4771 14
                if ($strict === true) {
4772 14
                    $returnTmp = $item === $needle;
4773
                } else {
4774
                    $returnTmp = $item == $needle;
4775
                }
4776
            }
4777
4778 14
            if ($returnTmp === true) {
4779 10
                return true;
4780
            }
4781
        }
4782
4783 8
        return false;
4784
    }
4785
4786
    /**
4787
     * @param mixed $data
4788
     *
4789
     * @return array|null
4790
     */
4791 991
    protected function internalGetArray(&$data)
4792
    {
4793 991
        if (\is_array($data)) {
4794 988
            return $data;
4795
        }
4796
4797 49
        if (!$data) {
4798 6
            return [];
4799
        }
4800
4801 48
        $isObject = \is_object($data);
4802
4803 48
        if ($isObject && $data instanceof self) {
4804 2
            return $data->getArray();
4805
        }
4806
4807 47
        if ($isObject && $data instanceof \ArrayObject) {
4808
            return $data->getArrayCopy();
4809
        }
4810
4811 47
        if ($isObject && $data instanceof \Generator) {
4812
            return static::createFromGeneratorImmutable($data)->getArray();
4813
        }
4814
4815 47
        if ($isObject && $data instanceof \Traversable) {
4816
            return static::createFromObject($data)->getArray();
4817
        }
4818
4819 47
        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...
4820
            return (array) $data->jsonSerialize();
4821
        }
4822
4823 47
        if (\is_callable($data)) {
4824 40
            $this->generator = new ArrayyRewindableGenerator($data);
4825
4826 40
            return [];
4827
        }
4828
4829 9
        if ($isObject && \method_exists($data, '__toArray')) {
4830
            return (array) $data->__toArray();
4831
        }
4832
4833 9
        if (\is_scalar($data)) {
4834 7
            return [$data];
4835
        }
4836
4837 2
        if ($isObject && \method_exists($data, '__toString')) {
4838
            return [(string) $data];
4839
        }
4840
4841 2
        return null;
4842
    }
4843
4844
    /**
4845
     * Internal mechanics of remove method.
4846
     *
4847
     * @param mixed $key
4848
     *
4849
     * @return bool
4850
     */
4851 18
    protected function internalRemove($key): bool
4852
    {
4853 18
        $this->generatorToArray();
4854
4855
        if (
4856 18
            $this->pathSeparator
4857
            &&
4858 18
            \is_string($key)
4859
            &&
4860 18
            \strpos($key, $this->pathSeparator) !== false
4861
        ) {
4862
            $path = \explode($this->pathSeparator, (string) $key);
4863
4864
            if ($path !== false) {
4865
                // crawl though the keys
4866
                while (\count($path, \COUNT_NORMAL) > 1) {
4867
                    $key = \array_shift($path);
4868
4869
                    if (!$this->has($key)) {
4870
                        return false;
4871
                    }
4872
4873
                    $this->array = &$this->array[$key];
4874
                }
4875
4876
                $key = \array_shift($path);
4877
            }
4878
        }
4879
4880 18
        unset($this->array[$key]);
4881
4882 18
        return true;
4883
    }
4884
4885
    /**
4886
     * Internal mechanic of set method.
4887
     *
4888
     * @param int|string|null $key
4889
     * @param mixed           $value
4890
     * @param bool            $checkProperties
4891
     *
4892
     * @return bool
4893
     */
4894 865
    protected function internalSet($key, $value, $checkProperties = true): bool
4895
    {
4896
        if (
4897 865
            $checkProperties === true
4898
            &&
4899 865
            $this->properties !== []
4900
        ) {
4901 13
            if (isset($this->properties[$key]) === false) {
4902
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4903
            }
4904
4905 13
            $this->properties[$key]->checkType($value);
4906
        }
4907
4908 864
        if ($key === null) {
4909
            return false;
4910
        }
4911
4912 864
        $this->generatorToArray();
4913
4914 864
        $array = &$this->array;
4915
4916
        if (
4917 864
            $this->pathSeparator
4918
            &&
4919 864
            \is_string($key)
4920
            &&
4921 864
            \strpos($key, $this->pathSeparator) !== false
4922
        ) {
4923 3
            $path = \explode($this->pathSeparator, (string) $key);
4924
4925 3
            if ($path !== false) {
4926
                // crawl through the keys
4927 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4928 3
                    $key = \array_shift($path);
4929
4930 3
                    $array = &$array[$key];
4931
                }
4932
4933 3
                $key = \array_shift($path);
4934
            }
4935
        }
4936
4937 864
        $array[$key] = $value;
4938
4939 864
        return true;
4940
    }
4941
4942
    /**
4943
     * Convert a object into an array.
4944
     *
4945
     * @param object $object
4946
     *
4947
     * @return mixed
4948
     */
4949 5
    protected static function objectToArray($object)
4950
    {
4951 5
        if (!\is_object($object)) {
4952 4
            return $object;
4953
        }
4954
4955 5
        if (\is_object($object)) {
4956 5
            $object = \get_object_vars($object);
4957
        }
4958
4959 5
        return \array_map(['static', 'objectToArray'], $object);
4960
    }
4961
4962
    /**
4963
     * sorting keys
4964
     *
4965
     * @param array      $elements
4966
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4967
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4968
     *                              <strong>SORT_NATURAL</strong></p>
4969
     *
4970
     * @return static
4971
     *                <p>(Mutable) Return this Arrayy object.</p>
4972
     */
4973 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4974
    {
4975 18
        $direction = $this->getDirection($direction);
4976
4977 18
        switch ($direction) {
4978 18
            case 'desc':
4979
            case \SORT_DESC:
4980 6
                \krsort($elements, $strategy);
4981
4982 6
                break;
4983 13
            case 'asc':
4984 13
            case \SORT_ASC:
4985
            default:
4986 13
                \ksort($elements, $strategy);
4987
        }
4988
4989 18
        return $this;
4990
    }
4991
4992
    /**
4993
     * @param array      $elements  <p>Warning: used as reference</p>
4994
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4995
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4996
     *                              <strong>SORT_NATURAL</strong></p>
4997
     * @param bool       $keepKeys
4998
     *
4999
     * @return static
5000
     *                <p>(Mutable) Return this Arrayy object.</p>
5001
     */
5002 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
5003
    {
5004 20
        $direction = $this->getDirection($direction);
5005
5006 20
        if (!$strategy) {
5007 20
            $strategy = \SORT_REGULAR;
5008
        }
5009
5010 20
        switch ($direction) {
5011 20
            case 'desc':
5012
            case \SORT_DESC:
5013 9
                if ($keepKeys) {
5014 5
                    \arsort($elements, $strategy);
5015
                } else {
5016 4
                    \rsort($elements, $strategy);
5017
                }
5018
5019 9
                break;
5020 11
            case 'asc':
5021 11
            case \SORT_ASC:
5022
            default:
5023 11
                if ($keepKeys) {
5024 4
                    \asort($elements, $strategy);
5025
                } else {
5026 7
                    \sort($elements, $strategy);
5027
                }
5028
        }
5029
5030 20
        return $this;
5031
    }
5032
5033
    /**
5034
     * @return bool
5035
     */
5036 909
    private function generatorToArray(): bool
5037
    {
5038 909
        if ($this->generator) {
5039 1
            $this->array = $this->getArray();
5040 1
            $this->generator = null;
5041
5042 1
            return true;
5043
        }
5044
5045 909
        return false;
5046
    }
5047
5048
    /**
5049
     * @return Property[]
5050
     */
5051 15
    private function getPropertiesFromPhpDoc(): array
5052
    {
5053 15
        static $PROPERTY_CACHE = [];
5054 15
        $cacheKey = 'Class::' . static::class;
5055
5056 15
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5057 14
            return $PROPERTY_CACHE[$cacheKey];
5058
        }
5059
5060
        // init
5061 2
        $properties = [];
5062
5063 2
        $reflector = new \ReflectionClass($this);
5064 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5065 2
        $docComment = $reflector->getDocComment();
5066 2
        if ($docComment) {
5067 2
            $docblock = $factory->create($docComment);
5068 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5069
                /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
5070 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
5071
            }
5072
        }
5073
5074 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5075
    }
5076
}
5077