Completed
Push — master ( 35eefd...6ead31 )
by Lars
06:45 queued 12s
created

Arrayy::offsetExists()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 62

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 9

Importance

Changes 0
Metric Value
cc 9
nc 9
nop 1
dl 0
loc 62
ccs 24
cts 24
cp 1
crap 9
rs 7.2735
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/** @noinspection ReturnTypeCanBeDeclaredInspection */
4
/** @noinspection ClassReImplementsParentInterfaceInspection */
5
6
declare(strict_types=1);
7
8
namespace Arrayy;
9
10
use Arrayy\Type\TypeInterface;
11
use Arrayy\TypeCheck\TypeCheckArray;
12
use Arrayy\TypeCheck\TypeCheckInterface;
13
use Arrayy\TypeCheck\TypeCheckPhpDoc;
14
15
/**
16
 * Methods to manage arrays.
17
 *
18
 * For the full copyright and license information, please view the LICENSE
19
 * file that was distributed with this source code.
20
 *
21
 * @template TKey of array-key
22
 * @template T
23
 * @template-extends \ArrayObject<TKey,T>
24
 * @template-implements \IteratorAggregate<TKey,T>
25
 * @template-implements \ArrayAccess<TKey|null,T>
26
 */
27
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
28
{
29
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
30
31
    /**
32
     * @var array
33
     *
34
     * @psalm-var array<mixed|TKey,T>
35
     */
36
    protected $array = [];
37
38
    /**
39
     * @var \Arrayy\ArrayyRewindableGenerator|null
40
     *
41
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
42
     */
43
    protected $generator;
44
45
    /**
46
     * @var string
47
     *
48
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
49
     */
50
    protected $iteratorClass = ArrayyIterator::class;
51
52
    /**
53
     * @var string
54
     */
55
    protected $pathSeparator = '.';
56
57
    /**
58
     * @var bool
59
     */
60
    protected $checkPropertyTypes = false;
61
62
    /**
63
     * @var bool
64
     */
65
    protected $checkForMissingPropertiesInConstructor = false;
66
67
    /**
68
     * @var bool
69
     */
70
    protected $checkPropertiesMismatchInConstructor = false;
71
72
    /**
73
     * @var bool
74
     */
75
    protected $checkPropertiesMismatch = true;
76
77
    /**
78
     * @var array<int|string,TypeCheckInterface>|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
79
     */
80
    protected $properties = [];
81
82
    /**
83
     * Initializes
84
     *
85
     * @param mixed  $data                         <p>
86
     *                                             Should be an array or a generator, otherwise it will try
87
     *                                             to convert it into an array.
88
     *                                             </p>
89
     * @param string $iteratorClass                optional <p>
90
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
91
     *                                             need this option.
92
     *                                             </p>
93
     * @param bool   $checkPropertiesInConstructor optional <p>
94
     *                                             You need to extend the "Arrayy"-class and you need to set
95
     *                                             the $checkPropertiesMismatchInConstructor class property
96
     *                                             to
97
     *                                             true, otherwise this option didn't not work anyway.
98
     *                                             </p>
99
     *
100
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
101
     */
102 1100
    public function __construct(
103
        $data = [],
104
        string $iteratorClass = ArrayyIterator::class,
105
        bool $checkPropertiesInConstructor = true
106
    ) {
107 1100
        $data = $this->fallbackForArray($data);
108
109
        // used only for serialize + unserialize, all other methods are overwritten
110
        /**
111
         * @psalm-suppress InvalidArgument - why?
112
         */
113 1098
        parent::__construct([], 0, $iteratorClass);
114
115 1098
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
116
117 1091
        $this->setIteratorClass($iteratorClass);
118 1091
    }
119
120
    /**
121
     * @return void
122
     */
123 47
    public function __clone()
124
    {
125 47
        if (!\is_array($this->properties)) {
126
            $this->properties = clone $this->properties;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->properties of type object is incompatible with the declared type array of property $properties.

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...
127
        }
128
129 47
        if ($this->generator !== null) {
130
            $this->generator = clone $this->generator;
131
        }
132 47
    }
133
134
    /**
135
     * Call object as function.
136
     *
137
     * @param mixed $key
138
     *
139
     * @return mixed
140
     */
141 1
    public function __invoke($key = null)
142
    {
143 1
        if ($key !== null) {
144 1
            $this->generatorToArray();
145
146 1
            return $this->array[$key] ?? false;
147
        }
148
149
        return $this->toArray();
150
    }
151
152
    /**
153
     * Whether or not an element exists by key.
154
     *
155
     * @param mixed $key
156
     *
157
     * @return bool
158
     *              <p>True is the key/index exists, otherwise false.</p>
159
     */
160
    public function __isset($key): bool
161
    {
162
        return $this->offsetExists($key);
163
    }
164
165
    /**
166
     * Assigns a value to the specified element.
167
     *
168
     * @param mixed $key
169
     * @param mixed $value
170
     *
171
     * @return void
172
     */
173 2
    public function __set($key, $value)
174
    {
175 2
        $this->internalSet($key, $value);
176 2
    }
177
178
    /**
179
     * magic to string
180
     *
181
     * @return string
182
     */
183 15
    public function __toString(): string
184
    {
185 15
        return $this->toString();
186
    }
187
188
    /**
189
     * Unset element by key.
190
     *
191
     * @param mixed $key
192
     */
193
    public function __unset($key)
194
    {
195
        $this->internalRemove($key);
196
    }
197
198
    /**
199
     * Get a value by key.
200
     *
201
     * @param mixed $key
202
     *
203
     * @return mixed
204
     *               <p>Get a Value from the current array.</p>
205
     */
206 4
    public function &__get($key)
207
    {
208 4
        $return = $this->get($key);
209
210 4
        if (\is_array($return) === true) {
211
            return static::create($return, $this->iteratorClass, false);
212
        }
213
214 4
        return $return;
215
    }
216
217
    /**
218
     * alias: for "Arrayy->append()"
219
     *
220
     * @param mixed $value
221
     *
222
     * @return static
223
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
224
     *
225
     * @see          Arrayy::append()
226
     *
227
     * @psalm-param  T $value
228
     * @psalm-return static<TKey,T>
229
     */
230 3
    public function add($value)
231
    {
232 3
        return $this->append($value);
233
    }
234
235
    /**
236
     * Append a (key) + value to the current array.
237
     *
238
     * @param mixed $value
239
     * @param mixed $key
240
     *
241
     * @return $this
242
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
243
     *
244
     * @psalm-return static<TKey,T>
245
     */
246 13
    public function append($value, $key = null): self
247
    {
248 13
        $this->generatorToArray();
249
250 13
        if ($this->properties !== []) {
251 4
            $this->checkType($key, $value);
252
        }
253
254 12
        if ($key !== null) {
255
            if (
256
                isset($this->array[$key])
257
                &&
258
                \is_array($this->array[$key]) === true
259
            ) {
260
                $this->array[$key][] = $value;
261
            } else {
262
                $this->array[$key] = $value;
263
            }
264
        } else {
265 12
            $this->array[] = $value;
266
        }
267
268 12
        return $this;
269
    }
270
271
    /**
272
     * Sort the entries by value.
273
     *
274
     * @param int $sort_flags [optional] <p>
275
     *                        You may modify the behavior of the sort using the optional
276
     *                        parameter sort_flags, for details
277
     *                        see sort.
278
     *                        </p>
279
     *
280
     * @return $this
281
     *               <p>(Mutable) Return this Arrayy object.</p>
282
     *
283
     * @psalm-return static<TKey,T>
284
     */
285 4
    public function asort(int $sort_flags = 0): self
286
    {
287 4
        $this->generatorToArray();
288
289 4
        \asort($this->array, $sort_flags);
290
291 4
        return $this;
292
    }
293
294
    /**
295
     * Sort the entries by value.
296
     *
297
     * @param int $sort_flags [optional] <p>
298
     *                        You may modify the behavior of the sort using the optional
299
     *                        parameter sort_flags, for details
300
     *                        see sort.
301
     *                        </p>
302
     *
303
     * @return $this
304
     *               <p>(Immutable) Return this Arrayy object.</p>
305
     *
306
     * @psalm-return static<TKey,T>
307
     * @psalm-mutation-free
308
     */
309 4
    public function asortImmutable(int $sort_flags = 0): self
310
    {
311 4
        $that = clone $this;
312
313
        /**
314
         * @psalm-suppress ImpureMethodCall - object is already cloned
315
         */
316 4
        $that->asort($sort_flags);
317
318 4
        return $that;
319
    }
320
321
    /**
322
     * Counts all elements in an array, or something in an object.
323
     *
324
     * <p>
325
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
326
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
327
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
328
     * implemented and used in PHP.
329
     * </p>
330
     *
331
     * @see http://php.net/manual/en/function.count.php
332
     *
333
     * @param int $mode [optional] If the optional mode parameter is set to
334
     *                  COUNT_RECURSIVE (or 1), count
335
     *                  will recursively count the array. This is particularly useful for
336
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
337
     *
338
     * @return int
339
     *             <p>
340
     *             The number of elements in var, which is
341
     *             typically an array, since anything else will have one
342
     *             element.
343
     *             </p>
344
     *             <p>
345
     *             If var is not an array or an object with
346
     *             implemented Countable interface,
347
     *             1 will be returned.
348
     *             There is one exception, if var is &null;,
349
     *             0 will be returned.
350
     *             </p>
351
     *             <p>
352
     *             Caution: count may return 0 for a variable that isn't set,
353
     *             but it may also return 0 for a variable that has been initialized with an
354
     *             empty array. Use isset to test if a variable is set.
355
     *             </p>
356
     * @psalm-mutation-free
357
     */
358 145
    public function count(int $mode = \COUNT_NORMAL): int
359
    {
360
        if (
361 145
            $this->generator
362
            &&
363 145
            $mode === \COUNT_NORMAL
364
        ) {
365 4
            return \iterator_count($this->generator);
366
        }
367
368 141
        return \count($this->toArray(), $mode);
369
    }
370
371
    /**
372
     * Exchange the array for another one.
373
     *
374
     * @param array|static $data
375
     *
376
     * @return array
377
     *
378
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
379
     * @psalm-return array<TKey,T>
380
     */
381 1
    public function exchangeArray($data): array
382
    {
383 1
        $this->array = $this->fallbackForArray($data);
384
385 1
        return $this->array;
386
    }
387
388
    /**
389
     * Creates a copy of the ArrayyObject.
390
     *
391
     * @return array
392
     *
393
     * @psalm-return array<TKey,T>
394
     */
395 5
    public function getArrayCopy(): array
396
    {
397 5
        $this->generatorToArray();
398
399 5
        return $this->array;
400
    }
401
402
    /**
403
     * Returns a new iterator, thus implementing the \Iterator interface.
404
     *
405
     * @return \Iterator<mixed, mixed>
0 ignored issues
show
Documentation introduced by
The doc-type \Iterator<mixed, could not be parsed: Expected "|" or "end of type", but got "<" at position 9. (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...
406
     *                                 <p>An iterator for the values in the array.</p>
407
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
408
     */
409 24
    public function getIterator(): \Iterator
410
    {
411 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
412 1
            return $this->generator;
413
        }
414
415 23
        $iterator = $this->getIteratorClass();
416
417 23
        if ($iterator === ArrayyIterator::class) {
418 23
            return new $iterator($this->toArray(), 0, static::class);
419
        }
420
421
        $return = new $iterator($this->toArray());
422
        \assert($return instanceof \Iterator);
423
424
        return $return;
425
    }
426
427
    /**
428
     * Gets the iterator classname for the ArrayObject.
429
     *
430
     * @return string
431
     *
432
     * @psalm-return class-string
433
     */
434 23
    public function getIteratorClass(): string
435
    {
436 23
        return $this->iteratorClass;
437
    }
438
439
    /**
440
     * Sort the entries by key.
441
     *
442
     * @param int $sort_flags [optional] <p>
443
     *                        You may modify the behavior of the sort using the optional
444
     *                        parameter sort_flags, for details
445
     *                        see sort.
446
     *                        </p>
447
     *
448
     * @return $this
449
     *               <p>(Mutable) Return this Arrayy object.</p>
450
     *
451
     * @psalm-return static<TKey,T>
452
     */
453 4
    public function ksort(int $sort_flags = 0): self
454
    {
455 4
        $this->generatorToArray();
456
457 4
        \ksort($this->array, $sort_flags);
458
459 4
        return $this;
460
    }
461
462
    /**
463
     * Sort the entries by key.
464
     *
465
     * @param int $sort_flags [optional] <p>
466
     *                        You may modify the behavior of the sort using the optional
467
     *                        parameter sort_flags, for details
468
     *                        see sort.
469
     *                        </p>
470
     *
471
     * @return $this
472
     *               <p>(Immutable) Return this Arrayy object.</p>
473
     *
474
     * @psalm-return static<TKey,T>
475
     */
476 4
    public function ksortImmutable(int $sort_flags = 0): self
477
    {
478 4
        $that = clone $this;
479
480
        /**
481
         * @psalm-suppress ImpureMethodCall - object is already cloned
482
         */
483 4
        $that->ksort($sort_flags);
484
485 4
        return $that;
486
    }
487
488
    /**
489
     * Sort an array using a case insensitive "natural order" algorithm.
490
     *
491
     * @return $this
492
     *               <p>(Mutable) Return this Arrayy object.</p>
493
     *
494
     * @psalm-return static<TKey,T>
495
     */
496 8
    public function natcasesort(): self
497
    {
498 8
        $this->generatorToArray();
499
500 8
        \natcasesort($this->array);
501
502 8
        return $this;
503
    }
504
505
    /**
506
     * Sort an array using a case insensitive "natural order" algorithm.
507
     *
508
     * @return $this
509
     *               <p>(Immutable) Return this Arrayy object.</p>
510
     *
511
     * @psalm-return static<TKey,T>
512
     * @psalm-mutation-free
513
     */
514 4
    public function natcasesortImmutable(): self
515
    {
516 4
        $that = clone $this;
517
518
        /**
519
         * @psalm-suppress ImpureMethodCall - object is already cloned
520
         */
521 4
        $that->natcasesort();
522
523 4
        return $that;
524
    }
525
526
    /**
527
     * Sort entries using a "natural order" algorithm.
528
     *
529
     * @return $this
530
     *               <p>(Mutable) Return this Arrayy object.</p>
531
     *
532
     * @psalm-return static<TKey,T>
533
     */
534 9
    public function natsort(): self
535
    {
536 9
        $this->generatorToArray();
537
538 9
        \natsort($this->array);
539
540 9
        return $this;
541
    }
542
543
    /**
544
     * Sort entries using a "natural order" algorithm.
545
     *
546
     * @return $this
547
     *               <p>(Immutable) Return this Arrayy object.</p>
548
     *
549
     * @psalm-return static<TKey,T>
550
     * @psalm-mutation-free
551
     */
552 4
    public function natsortImmutable(): self
553
    {
554 4
        $that = clone $this;
555
556
        /**
557
         * @psalm-suppress ImpureMethodCall - object is already cloned
558
         */
559 4
        $that->natsort();
560
561 4
        return $that;
562
    }
563
564
    /**
565
     * Whether or not an offset exists.
566
     *
567
     * @param bool|int|string $offset
568
     *
569
     * @return bool
570
     *
571
     * @noinspection PhpSillyAssignmentInspection
572
     *
573
     * @psalm-mutation-free
574
     */
575 130
    public function offsetExists($offset): bool
576
    {
577 130
        $this->generatorToArray();
578
579 130
        if ($this->array === []) {
580 5
            return false;
581
        }
582
583
        // php cast "bool"-index into "int"-index
584 125
        if ((bool) $offset === $offset) {
585 1
            $offset = (int) $offset;
586
        }
587
588
        /** @var int|string $offset - hint for phpstan */
589 125
        $offset = $offset;
0 ignored issues
show
Bug introduced by
Why assign $offset to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
590
591 125
        $tmpReturn = $this->keyExists($offset);
592
593
        if (
594 125
            $tmpReturn === true
595
            ||
596 125
            \strpos((string) $offset, $this->pathSeparator) === false
597
        ) {
598 123
            return $tmpReturn;
599
        }
600
601 3
        $offsetExists = false;
602
603
        /**
604
         * https://github.com/vimeo/psalm/issues/2536
605
         *
606
         * @psalm-suppress PossiblyInvalidArgument
607
         * @psalm-suppress InvalidScalarArgument
608
         */
609
        if (
610 3
            $this->pathSeparator
611
            &&
612 3
            (string) $offset === $offset
613
            &&
614 3
            \strpos($offset, $this->pathSeparator) !== false
615
        ) {
616 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
617 3
            if ($explodedPath !== false) {
618
                /** @var string $lastOffset - helper for phpstan */
619 3
                $lastOffset = \array_pop($explodedPath);
620 3
                $containerPath = \implode($this->pathSeparator, $explodedPath);
621
622
                /**
623
                 * @psalm-suppress MissingClosureReturnType
624
                 * @psalm-suppress MissingClosureParamType
625
                 */
626 3
                $this->callAtPath(
627 3
                    $containerPath,
628
                    static function ($container) use ($lastOffset, &$offsetExists) {
629 3
                        $offsetExists = \array_key_exists($lastOffset, $container);
630 3
                    }
631
                );
632
            }
633
        }
634
635 3
        return $offsetExists;
636
    }
637
638
    /**
639
     * Returns the value at specified offset.
640
     *
641
     * @param int|string $offset
642
     *
643
     * @return mixed
644
     *               <p>Will return null if the offset did not exists.</p>
645
     */
646 101
    public function offsetGet($offset)
647
    {
648 101
        return $this->offsetExists($offset) ? $this->get($offset) : null;
649
    }
650
651
    /**
652
     * Assigns a value to the specified offset + check the type.
653
     *
654
     * @param int|string|null $offset
655
     * @param mixed           $value
656
     *
657
     * @return void
658
     */
659 18
    public function offsetSet($offset, $value)
660
    {
661 18
        $this->generatorToArray();
662
663 18
        if ($offset === null) {
664 5
            if ($this->properties !== []) {
665 1
                $this->checkType(null, $value);
666
            }
667
668 4
            $this->array[] = $value;
669
        } else {
670 13
            $this->internalSet(
671 13
                $offset,
672 13
                $value,
673 13
                true
674
            );
675
        }
676 17
    }
677
678
    /**
679
     * Unset an offset.
680
     *
681
     * @param int|string $offset
682
     *
683
     * @return void
684
     */
685 12
    public function offsetUnset($offset)
686
    {
687 12
        $this->generatorToArray();
688
689 12
        if ($this->array === []) {
690 3
            return;
691
        }
692
693 10
        if ($this->keyExists($offset)) {
694 7
            unset($this->array[$offset]);
695
696 7
            return;
697
        }
698
699
        /**
700
         * https://github.com/vimeo/psalm/issues/2536
701
         *
702
         * @psalm-suppress PossiblyInvalidArgument
703
         * @psalm-suppress InvalidScalarArgument
704
         */
705
        if (
706 5
            $this->pathSeparator
707
            &&
708 5
            (string) $offset === $offset
709
            &&
710 5
            \strpos($offset, $this->pathSeparator) !== false
711
        ) {
712 2
            $path = \explode($this->pathSeparator, (string) $offset);
713
714 2
            if ($path !== false) {
715 2
                $pathToUnset = \array_pop($path);
716
717
                /**
718
                 * @psalm-suppress MissingClosureReturnType
719
                 * @psalm-suppress MissingClosureParamType
720
                 */
721 2
                $this->callAtPath(
722 2
                    \implode($this->pathSeparator, $path),
723
                    static function (&$offset) use ($pathToUnset) {
724 2
                        unset($offset[$pathToUnset]);
725 2
                    }
726
                );
727
            }
728
        }
729
730 5
        unset($this->array[$offset]);
731 5
    }
732
733
    /**
734
     * Serialize the current "Arrayy"-object.
735
     *
736
     * @return string
737
     */
738 2
    public function serialize(): string
739
    {
740 2
        $this->generatorToArray();
741
742 2
        if (\PHP_VERSION_ID < 70400) {
743 2
            return parent::serialize();
744
        }
745
746
        return \serialize($this);
747
    }
748
749
    /**
750
     * Sets the iterator classname for the current "Arrayy"-object.
751
     *
752
     * @param string $iteratorClass
753
     *
754
     * @throws \InvalidArgumentException
755
     *
756
     * @return void
757
     *
758
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
759
     */
760 1091
    public function setIteratorClass($iteratorClass)
761
    {
762 1091
        if (\class_exists($iteratorClass)) {
763 1091
            $this->iteratorClass = $iteratorClass;
764
765 1091
            return;
766
        }
767
768
        if (\strpos($iteratorClass, '\\') === 0) {
769
            $iteratorClass = '\\' . $iteratorClass;
770
            if (\class_exists($iteratorClass)) {
771
                /**
772
                 * @psalm-suppress PropertyTypeCoercion
773
                 */
774
                $this->iteratorClass = $iteratorClass;
775
776
                return;
777
            }
778
        }
779
780
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
781
    }
782
783
    /**
784
     * Sort the entries with a user-defined comparison function and maintain key association.
785
     *
786
     * @param callable $function
787
     *
788
     * @throws \InvalidArgumentException
789
     *
790
     * @return $this
791
     *               <p>(Mutable) Return this Arrayy object.</p>
792
     *
793
     * @psalm-return static<TKey,T>
794
     */
795 8 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...
796
    {
797 8
        if (!\is_callable($function)) {
798
            throw new \InvalidArgumentException('Passed function must be callable');
799
        }
800
801 8
        $this->generatorToArray();
802
803 8
        \uasort($this->array, $function);
804
805 8
        return $this;
806
    }
807
808
    /**
809
     * Sort the entries with a user-defined comparison function and maintain key association.
810
     *
811
     * @param callable $function
812
     *
813
     * @throws \InvalidArgumentException
814
     *
815
     * @return $this
816
     *               <p>(Immutable) Return this Arrayy object.</p>
817
     *
818
     * @psalm-return static<TKey,T>
819
     * @psalm-mutation-free
820
     */
821 4
    public function uasortImmutable($function): self
822
    {
823 4
        $that = clone $this;
824
825
        /**
826
         * @psalm-suppress ImpureMethodCall - object is already cloned
827
         */
828 4
        $that->uasort($function);
829
830 4
        return $that;
831
    }
832
833
    /**
834
     * Sort the entries by keys using a user-defined comparison function.
835
     *
836
     * @param callable $function
837
     *
838
     * @throws \InvalidArgumentException
839
     *
840
     * @return static
841
     *                <p>(Mutable) Return this Arrayy object.</p>
842
     *
843
     * @psalm-return static<TKey,T>
844
     */
845 5
    public function uksort($function): self
846
    {
847 5
        return $this->customSortKeys($function);
848
    }
849
850
    /**
851
     * Sort the entries by keys using a user-defined comparison function.
852
     *
853
     * @param callable $function
854
     *
855
     * @throws \InvalidArgumentException
856
     *
857
     * @return static
858
     *                <p>(Immutable) Return this Arrayy object.</p>
859
     *
860
     * @psalm-return static<TKey,T>
861
     * @psalm-mutation-free
862
     */
863 1
    public function uksortImmutable($function): self
864
    {
865 1
        return $this->customSortKeysImmutable($function);
866
    }
867
868
    /**
869
     * Unserialize an string and return the instance of the "Arrayy"-class.
870
     *
871
     * @param string $string
872
     *
873
     * @return $this
874
     *
875
     * @psalm-return static<TKey,T>
876
     */
877 2
    public function unserialize($string): self
878
    {
879 2
        if (\PHP_VERSION_ID < 70400) {
880 2
            parent::unserialize($string);
881
882 2
            return $this;
883
        }
884
885
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
886
    }
887
888
    /**
889
     * Append a (key) + values to the current array.
890
     *
891
     * @param array $values
892
     * @param mixed $key
893
     *
894
     * @return $this
895
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
896
     *
897
     * @psalm-param  array<mixed,T> $values
898
     * @psalm-param  TKey|null $key
899
     * @psalm-return static<TKey,T>
900
     */
901 1
    public function appendArrayValues(array $values, $key = null)
902
    {
903 1
        $this->generatorToArray();
904
905 1
        if ($key !== null) {
906
            if (
907 1
                isset($this->array[$key])
908
                &&
909 1
                \is_array($this->array[$key]) === true
910
            ) {
911 1
                foreach ($values as $value) {
912 1
                    $this->array[$key][] = $value;
913
                }
914
            } else {
915 1
                foreach ($values as $value) {
916
                    $this->array[$key] = $value;
917
                }
918
            }
919
        } else {
920
            foreach ($values as $value) {
921
                $this->array[] = $value;
922
            }
923
        }
924
925 1
        return $this;
926
    }
927
928
    /**
929
     * Add a suffix to each key.
930
     *
931
     * @param mixed $prefix
932
     *
933
     * @return static
934
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
935
     *
936
     * @psalm-return static<TKey,T>
937
     * @psalm-mutation-free
938
     */
939 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...
940
    {
941
        // init
942 10
        $result = [];
943
944 10
        foreach ($this->getGenerator() as $key => $item) {
945 9
            if ($item instanceof self) {
946
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
947 9
            } elseif (\is_array($item) === true) {
948
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
949
                    ->appendToEachKey($prefix)
950
                    ->toArray();
951
            } else {
952 9
                $result[$prefix . $key] = $item;
953
            }
954
        }
955
956 10
        return self::create($result, $this->iteratorClass, false);
957
    }
958
959
    /**
960
     * Add a prefix to each value.
961
     *
962
     * @param mixed $prefix
963
     *
964
     * @return static
965
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
966
     *
967
     * @psalm-return static<TKey,T>
968
     * @psalm-mutation-free
969
     */
970 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...
971
    {
972
        // init
973 10
        $result = [];
974
975 10
        foreach ($this->getGenerator() as $key => $item) {
976 9
            if ($item instanceof self) {
977
                $result[$key] = $item->appendToEachValue($prefix);
978 9
            } elseif (\is_array($item) === true) {
979
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
980 9
            } elseif (\is_object($item) === true) {
981 1
                $result[$key] = $item;
982
            } else {
983 8
                $result[$key] = $prefix . $item;
984
            }
985
        }
986
987 10
        return self::create($result, $this->iteratorClass, false);
988
    }
989
990
    /**
991
     * Sort an array in reverse order and maintain index association.
992
     *
993
     * @return $this
994
     *               <p>(Mutable) Return this Arrayy object.</p>
995
     *
996
     * @psalm-return static<TKey,T>
997
     */
998 4
    public function arsort(): self
999
    {
1000 4
        $this->generatorToArray();
1001
1002 4
        \arsort($this->array);
1003
1004 4
        return $this;
1005
    }
1006
1007
    /**
1008
     * Sort an array in reverse order and maintain index association.
1009
     *
1010
     * @return $this
1011
     *               <p>(Immutable) Return this Arrayy object.</p>
1012
     *
1013
     * @psalm-return static<TKey,T>
1014
     * @psalm-mutation-free
1015
     */
1016 10
    public function arsortImmutable(): self
1017
    {
1018 10
        $that = clone $this;
1019
1020 10
        $that->generatorToArray();
1021
1022 10
        \arsort($that->array);
1023
1024 10
        return $that;
1025
    }
1026
1027
    /**
1028
     * Iterate over the current array and execute a callback for each loop.
1029
     *
1030
     * @param \Closure $closure
1031
     *
1032
     * @return static
1033
     *                <p>(Immutable)</p>
1034
     *
1035
     * @psalm-return static<TKey,T>
1036
     * @psalm-mutation-free
1037
     */
1038 2
    public function at(\Closure $closure): self
1039
    {
1040 2
        $that = clone $this;
1041
1042 2
        foreach ($that->getGenerator() as $key => $value) {
1043 2
            $closure($value, $key);
1044
        }
1045
1046 2
        return static::create(
1047 2
            $that->toArray(),
1048 2
            $this->iteratorClass,
1049 2
            false
1050
        );
1051
    }
1052
1053
    /**
1054
     * Returns the average value of the current array.
1055
     *
1056
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1057
     *
1058
     * @return float|int
1059
     *                   <p>The average value.</p>
1060
     * @psalm-mutation-free
1061
     */
1062 10
    public function average($decimals = 0)
1063
    {
1064 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1065
1066 10
        if (!$count) {
1067 2
            return 0;
1068
        }
1069
1070 8
        if ((int) $decimals !== $decimals) {
1071 3
            $decimals = 0;
1072
        }
1073
1074 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1075
    }
1076
1077
    /**
1078
     * Changes all keys in an array.
1079
     *
1080
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1081
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1082
     *
1083
     * @return static
1084
     *                <p>(Immutable)</p>
1085
     *
1086
     * @psalm-return static<TKey,T>
1087
     * @psalm-mutation-free
1088
     */
1089 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1090
    {
1091
        if (
1092 1
            $case !== \CASE_LOWER
1093
            &&
1094 1
            $case !== \CASE_UPPER
1095
        ) {
1096
            $case = \CASE_LOWER;
1097
        }
1098
1099 1
        $return = [];
1100 1
        foreach ($this->getGenerator() as $key => $value) {
1101 1
            if ($case === \CASE_LOWER) {
1102 1
                $key = \mb_strtolower((string) $key);
1103
            } else {
1104 1
                $key = \mb_strtoupper((string) $key);
1105
            }
1106
1107 1
            $return[$key] = $value;
1108
        }
1109
1110 1
        return static::create(
1111 1
            $return,
1112 1
            $this->iteratorClass,
1113 1
            false
1114
        );
1115
    }
1116
1117
    /**
1118
     * Change the path separator of the array wrapper.
1119
     *
1120
     * By default, the separator is: "."
1121
     *
1122
     * @param string $separator <p>Separator to set.</p>
1123
     *
1124
     * @return $this
1125
     *               <p>(Mutable) Return this Arrayy object.</p>
1126
     *
1127
     * @psalm-return static<TKey,T>
1128
     */
1129 11
    public function changeSeparator($separator): self
1130
    {
1131 11
        $this->pathSeparator = $separator;
1132
1133 11
        return $this;
1134
    }
1135
1136
    /**
1137
     * Create a chunked version of the current array.
1138
     *
1139
     * @param int  $size         <p>Size of each chunk.</p>
1140
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1141
     *
1142
     * @return static
1143
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1144
     *
1145
     * @psalm-return static<TKey,T>
1146
     * @psalm-mutation-free
1147
     */
1148 5
    public function chunk($size, $preserveKeys = false): self
1149
    {
1150 5
        return static::create(
1151 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1152 5
            $this->iteratorClass,
1153 5
            false
1154
        );
1155
    }
1156
1157
    /**
1158
     * Clean all falsy values from the current array.
1159
     *
1160
     * @return static
1161
     *                <p>(Immutable)</p>
1162
     *
1163
     * @psalm-return static<TKey,T>
1164
     * @psalm-mutation-free
1165
     */
1166 8
    public function clean(): self
1167
    {
1168 8
        return $this->filter(
1169
            static function ($value) {
1170 7
                return (bool) $value;
1171 8
            }
1172
        );
1173
    }
1174
1175
    /**
1176
     * WARNING!!! -> Clear the current array.
1177
     *
1178
     * @return $this
1179
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1180
     *
1181
     * @psalm-return static<TKey,T>
1182
     */
1183 5
    public function clear(): self
1184
    {
1185 5
        $this->array = [];
1186 5
        $this->generator = null;
1187
1188 5
        return $this;
1189
    }
1190
1191
    /**
1192
     * Check if an item is in the current array.
1193
     *
1194
     * @param float|int|string $value
1195
     * @param bool             $recursive
1196
     * @param bool             $strict
1197
     *
1198
     * @return bool
1199
     * @psalm-mutation-free
1200
     */
1201 24
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1202
    {
1203 24
        if ($recursive === true) {
1204 19
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1205
        }
1206
1207 14
        foreach ($this->getGenerator() as $valueFromArray) {
1208 11
            if ($strict) {
1209 11
                if ($value === $valueFromArray) {
1210 11
                    return true;
1211
                }
1212
            } else {
1213
                /** @noinspection NestedPositiveIfStatementsInspection */
1214
                if ($value == $valueFromArray) {
1215
                    return true;
1216
                }
1217
            }
1218
        }
1219
1220 7
        return false;
1221
    }
1222
1223
    /**
1224
     * Check if an (case-insensitive) string is in the current array.
1225
     *
1226
     * @param mixed $value
1227
     * @param bool  $recursive
1228
     *
1229
     * @return bool
1230
     * @psalm-mutation-free
1231
     *
1232
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1233
     */
1234 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1235
    {
1236 26
        if ($value === null) {
1237 2
            return false;
1238
        }
1239
1240 24
        if ($recursive === true) {
1241 24
            foreach ($this->getGenerator() as $key => $valueTmp) {
1242 22
                if (\is_array($valueTmp) === true) {
1243 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1244 5
                    if ($return === true) {
1245 5
                        return $return;
1246
                    }
1247 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1248 16
                    return true;
1249
                }
1250
            }
1251
1252 8
            return false;
1253
        }
1254
1255 12
        foreach ($this->getGenerator() as $key => $valueTmp) {
1256 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1257 8
                return true;
1258
            }
1259
        }
1260
1261 4
        return false;
1262
    }
1263
1264
    /**
1265
     * Check if the given key/index exists in the array.
1266
     *
1267
     * @param int|string $key <p>key/index to search for</p>
1268
     *
1269
     * @return bool
1270
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1271
     *
1272
     * @psalm-mutation-free
1273
     */
1274 4
    public function containsKey($key): bool
1275
    {
1276 4
        return $this->offsetExists($key);
1277
    }
1278
1279
    /**
1280
     * Check if all given needles are present in the array as key/index.
1281
     *
1282
     * @param array $needles   <p>The keys you are searching for.</p>
1283
     * @param bool  $recursive
1284
     *
1285
     * @return bool
1286
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1287
     *
1288
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1289
     * @psalm-mutation-free
1290
     */
1291 2
    public function containsKeys(array $needles, $recursive = false): bool
1292
    {
1293 2
        if ($recursive === true) {
1294
            return
1295 2
                \count(
1296 2
                    \array_intersect(
1297 2
                        $needles,
1298 2
                        $this->keys(true)->toArray()
1299
                    ),
1300 2
                    \COUNT_RECURSIVE
1301
                )
1302
                ===
1303 2
                \count(
1304 2
                    $needles,
1305 2
                    \COUNT_RECURSIVE
1306
                );
1307
        }
1308
1309 1
        return \count(
1310 1
            \array_intersect($needles, $this->keys()->toArray()),
1311 1
            \COUNT_NORMAL
1312
        )
1313
               ===
1314 1
               \count(
1315 1
                   $needles,
1316 1
                   \COUNT_NORMAL
1317
               );
1318
    }
1319
1320
    /**
1321
     * Check if all given needles are present in the array as key/index.
1322
     *
1323
     * @param array $needles <p>The keys you are searching for.</p>
1324
     *
1325
     * @return bool
1326
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1327
     *
1328
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1329
     * @psalm-mutation-free
1330
     */
1331 1
    public function containsKeysRecursive(array $needles): bool
1332
    {
1333 1
        return $this->containsKeys($needles, true);
1334
    }
1335
1336
    /**
1337
     * alias: for "Arrayy->contains()"
1338
     *
1339
     * @param float|int|string $value
1340
     *
1341
     * @return bool
1342
     *
1343
     * @see Arrayy::contains()
1344
     * @psalm-mutation-free
1345
     */
1346 9
    public function containsValue($value): bool
1347
    {
1348 9
        return $this->contains($value);
1349
    }
1350
1351
    /**
1352
     * alias: for "Arrayy->contains($value, true)"
1353
     *
1354
     * @param float|int|string $value
1355
     *
1356
     * @return bool
1357
     *
1358
     * @see Arrayy::contains()
1359
     * @psalm-mutation-free
1360
     */
1361 18
    public function containsValueRecursive($value): bool
1362
    {
1363 18
        return $this->contains($value, true);
1364
    }
1365
1366
    /**
1367
     * Check if all given needles are present in the array.
1368
     *
1369
     * @param array $needles
1370
     *
1371
     * @return bool
1372
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1373
     *
1374
     * @psalm-param array<mixed>|array<T> $needles
1375
     * @psalm-mutation-free
1376
     */
1377 1
    public function containsValues(array $needles): bool
1378
    {
1379 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1380
               ===
1381 1
               \count($needles, \COUNT_NORMAL);
1382
    }
1383
1384
    /**
1385
     * Counts all the values of an array
1386
     *
1387
     * @see          http://php.net/manual/en/function.array-count-values.php
1388
     *
1389
     * @return static
1390
     *                <p>
1391
     *                (Immutable)
1392
     *                An associative Arrayy-object of values from input as
1393
     *                keys and their count as value.
1394
     *                </p>
1395
     *
1396
     * @psalm-return static<TKey,T>
1397
     * @psalm-mutation-free
1398
     */
1399 7
    public function countValues(): self
1400
    {
1401 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1402
    }
1403
1404
    /**
1405
     * Creates an Arrayy object.
1406
     *
1407
     * @param mixed  $data
1408
     * @param string $iteratorClass
1409
     * @param bool   $checkPropertiesInConstructor
1410
     *
1411
     * @return static
1412
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1413
     *
1414
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1415
     *
1416
     * @psalm-mutation-free
1417
     */
1418 671
    public static function create(
1419
        $data = [],
1420
        string $iteratorClass = ArrayyIterator::class,
1421
        bool $checkPropertiesInConstructor = true
1422
    ) {
1423 671
        return new static(
1424 671
            $data,
1425 671
            $iteratorClass,
1426 671
            $checkPropertiesInConstructor
1427
        );
1428
    }
1429
1430
    /**
1431
     * WARNING: Creates an Arrayy object by reference.
1432
     *
1433
     * @param array $array
1434
     *
1435
     * @return $this
1436
     *               <p>(Mutable) Return this Arrayy object.</p>
1437
     *
1438
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1439
     */
1440 1
    public function createByReference(array &$array = []): self
1441
    {
1442 1
        $array = $this->fallbackForArray($array);
1443
1444 1
        $this->array = &$array;
1445
1446 1
        return $this;
1447
    }
1448
1449
    /**
1450
     * Create an new instance from a callable function which will return an Generator.
1451
     *
1452
     * @param callable $generatorFunction
1453
     *
1454
     * @return static
1455
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1456
     *
1457
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1458
     *
1459
     * @psalm-mutation-free
1460
     */
1461 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1462
    {
1463 7
        return self::create($generatorFunction);
1464
    }
1465
1466
    /**
1467
     * Create an new instance filled with a copy of values from a "Generator"-object.
1468
     *
1469
     * @param \Generator $generator
1470
     *
1471
     * @return static
1472
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1473
     *
1474
     * @psalm-param \Generator<array-key,mixed> $generator
1475
     *
1476
     * @psalm-mutation-free
1477
     */
1478 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1479
    {
1480 4
        return self::create(\iterator_to_array($generator, true));
1481
    }
1482
1483
    /**
1484
     * Create an new Arrayy object via JSON.
1485
     *
1486
     * @param string $json
1487
     *
1488
     * @return static
1489
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1490
     *
1491
     * @psalm-mutation-free
1492
     */
1493 5
    public static function createFromJson(string $json): self
1494
    {
1495 5
        return static::create(\json_decode($json, true));
1496
    }
1497
1498
    /**
1499
     * Create an new instance filled with values from an object that is iterable.
1500
     *
1501
     * @param \Traversable $object <p>iterable object</p>
1502
     *
1503
     * @return static
1504
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1505
     *
1506
     * @psalm-param \Traversable<array-key,mixed> $object
1507
     *
1508
     * @psalm-mutation-free
1509
     */
1510 4
    public static function createFromObject(\Traversable $object): self
1511
    {
1512
        // init
1513 4
        $arrayy = new static();
1514
1515 4
        if ($object instanceof self) {
1516 4
            $objectArray = $object->getGenerator();
1517
        } else {
1518
            $objectArray = $object;
1519
        }
1520
1521 4
        foreach ($objectArray as $key => $value) {
1522
            /**
1523
             * @psalm-suppress ImpureMethodCall - object is already re-created
1524
             */
1525 3
            $arrayy->internalSet($key, $value);
1526
        }
1527
1528 4
        return $arrayy;
1529
    }
1530
1531
    /**
1532
     * Create an new instance filled with values from an object.
1533
     *
1534
     * @param object $object
1535
     *
1536
     * @return static
1537
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1538
     *
1539
     * @psalm-mutation-free
1540
     */
1541 5
    public static function createFromObjectVars($object): self
1542
    {
1543 5
        return self::create(self::objectToArray($object));
1544
    }
1545
1546
    /**
1547
     * Create an new Arrayy object via string.
1548
     *
1549
     * @param string      $str       <p>The input string.</p>
1550
     * @param string|null $delimiter <p>The boundary string.</p>
1551
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1552
     *                               used.</p>
1553
     *
1554
     * @return static
1555
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1556
     *
1557
     * @psalm-mutation-free
1558
     */
1559 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1560
    {
1561 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...
1562 1
            \preg_match_all($regEx, $str, $array);
1563
1564 1
            if (!empty($array)) {
1565 1
                $array = $array[0];
1566
            }
1567
        } else {
1568
            /** @noinspection NestedPositiveIfStatementsInspection */
1569 9
            if ($delimiter !== null) {
1570 7
                $array = \explode($delimiter, $str);
1571
            } else {
1572 2
                $array = [$str];
1573
            }
1574
        }
1575
1576
        // trim all string in the array
1577
        /**
1578
         * @psalm-suppress MissingClosureParamType
1579
         */
1580 10
        \array_walk(
1581 10
            $array,
1582
            static function (&$val) {
1583 10
                if ((string) $val === $val) {
1584 10
                    $val = \trim($val);
1585
                }
1586 10
            }
1587
        );
1588
1589 10
        return static::create($array);
1590
    }
1591
1592
    /**
1593
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1594
     *
1595
     * @param \Traversable $traversable
1596
     *
1597
     * @return static
1598
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1599
     *
1600
     * @psalm-param \Traversable<array-key,mixed> $traversable
1601
     *
1602
     * @psalm-mutation-free
1603
     */
1604 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1605
    {
1606 1
        return self::create(\iterator_to_array($traversable, true));
1607
    }
1608
1609
    /**
1610
     * Create an new instance containing a range of elements.
1611
     *
1612
     * @param float|int|string $low  <p>First value of the sequence.</p>
1613
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1614
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1615
     *
1616
     * @return static
1617
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1618
     *
1619
     * @psalm-mutation-free
1620
     */
1621 2
    public static function createWithRange($low, $high, $step = 1): self
1622
    {
1623 2
        return static::create(\range($low, $high, $step));
1624
    }
1625
1626
    /**
1627
     * Gets the element of the array at the current internal iterator position.
1628
     *
1629
     * @return false|mixed
1630
     */
1631
    public function current()
1632
    {
1633
        return \current($this->array);
1634
    }
1635
1636
    /**
1637
     * Custom sort by index via "uksort".
1638
     *
1639
     * @see          http://php.net/manual/en/function.uksort.php
1640
     *
1641
     * @param callable $function
1642
     *
1643
     * @throws \InvalidArgumentException
1644
     *
1645
     * @return $this
1646
     *               <p>(Mutable) Return this Arrayy object.</p>
1647
     *
1648
     * @psalm-return static<TKey,T>
1649
     */
1650 5
    public function customSortKeys(callable $function): self
1651
    {
1652 5
        $this->generatorToArray();
1653
1654 5
        \uksort($this->array, $function);
1655
1656 5
        return $this;
1657
    }
1658
1659
    /**
1660
     * Custom sort by index via "uksort".
1661
     *
1662
     * @see          http://php.net/manual/en/function.uksort.php
1663
     *
1664
     * @param callable $function
1665
     *
1666
     * @throws \InvalidArgumentException
1667
     *
1668
     * @return $this
1669
     *               <p>(Immutable) Return this Arrayy object.</p>
1670
     *
1671
     * @psalm-return static<TKey,T>
1672
     * @psalm-mutation-free
1673
     */
1674 1
    public function customSortKeysImmutable(callable $function): self
1675
    {
1676 1
        $that = clone $this;
1677
1678 1
        $that->generatorToArray();
1679
1680 1
        \uksort($that->array, $function);
1681
1682 1
        return $that;
1683
    }
1684
1685
    /**
1686
     * Custom sort by value via "usort".
1687
     *
1688
     * @see          http://php.net/manual/en/function.usort.php
1689
     *
1690
     * @param callable $function
1691
     *
1692
     * @throws \InvalidArgumentException
1693
     *
1694
     * @return $this
1695
     *               <p>(Mutable) Return this Arrayy object.</p>
1696
     *
1697
     * @psalm-return static<TKey,T>
1698
     */
1699 10 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...
1700
    {
1701 10
        if (\is_callable($function) === false) {
1702
            throw new \InvalidArgumentException('Passed function must be callable');
1703
        }
1704
1705 10
        $this->generatorToArray();
1706
1707 10
        \usort($this->array, $function);
1708
1709 10
        return $this;
1710
    }
1711
1712
    /**
1713
     * Custom sort by value via "usort".
1714
     *
1715
     * @see          http://php.net/manual/en/function.usort.php
1716
     *
1717
     * @param callable $function
1718
     *
1719
     * @throws \InvalidArgumentException
1720
     *
1721
     * @return $this
1722
     *               <p>(Immutable) Return this Arrayy object.</p>
1723
     *
1724
     * @psalm-return static<TKey,T>
1725
     * @psalm-mutation-free
1726
     */
1727 4
    public function customSortValuesImmutable($function): self
1728
    {
1729 4
        $that = clone $this;
1730
1731
        /**
1732
         * @psalm-suppress ImpureMethodCall - object is already cloned
1733
         */
1734 4
        $that->customSortValues($function);
1735
1736 4
        return $that;
1737
    }
1738
1739
    /**
1740
     * Delete the given key or keys.
1741
     *
1742
     * @param int|int[]|string|string[] $keyOrKeys
1743
     *
1744
     * @return void
1745
     */
1746 4
    public function delete($keyOrKeys)
1747
    {
1748 4
        $keyOrKeys = (array) $keyOrKeys;
1749
1750 4
        foreach ($keyOrKeys as $key) {
1751 4
            $this->offsetUnset($key);
1752
        }
1753 4
    }
1754
1755
    /**
1756
     * Return values that are only in the current array.
1757
     *
1758
     * @param array ...$array
1759
     *
1760
     * @return static
1761
     *                <p>(Immutable)</p>
1762
     *
1763
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1764
     * @psalm-return static<TKey,T>
1765
     * @psalm-mutation-free
1766
     */
1767 13
    public function diff(...$array): self
1768
    {
1769 13
        return static::create(
1770 13
            \array_diff($this->toArray(), ...$array),
1771 13
            $this->iteratorClass,
1772 13
            false
1773
        );
1774
    }
1775
1776
    /**
1777
     * Return values that are only in the current array.
1778
     *
1779
     * @param array ...$array
1780
     *
1781
     * @return static
1782
     *                <p>(Immutable)</p>
1783
     *
1784
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1785
     * @psalm-return static<TKey,T>
1786
     * @psalm-mutation-free
1787
     */
1788 8
    public function diffKey(...$array): self
1789
    {
1790 8
        return static::create(
1791 8
            \array_diff_key($this->toArray(), ...$array),
1792 8
            $this->iteratorClass,
1793 8
            false
1794
        );
1795
    }
1796
1797
    /**
1798
     * Return values and Keys that are only in the current array.
1799
     *
1800
     * @param array $array
1801
     *
1802
     * @return static
1803
     *                <p>(Immutable)</p>
1804
     *
1805
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1806
     * @psalm-return static<TKey,T>
1807
     * @psalm-mutation-free
1808
     */
1809 8
    public function diffKeyAndValue(array $array = []): self
1810
    {
1811 8
        return static::create(
1812 8
            \array_diff_assoc($this->toArray(), $array),
1813 8
            $this->iteratorClass,
1814 8
            false
1815
        );
1816
    }
1817
1818
    /**
1819
     * Return values that are only in the current multi-dimensional array.
1820
     *
1821
     * @param array                 $array
1822
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
1823
     *
1824
     * @return static
1825
     *                <p>(Immutable)</p>
1826
     *
1827
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1828
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
1829
     * @psalm-return static<TKey,T>
1830
     * @psalm-mutation-free
1831
     */
1832 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1833
    {
1834
        // init
1835 1
        $result = [];
1836
1837
        if (
1838 1
            $helperVariableForRecursion !== null
1839
            &&
1840 1
            \is_array($helperVariableForRecursion) === true
1841
        ) {
1842
            $arrayForTheLoop = $helperVariableForRecursion;
1843
        } else {
1844 1
            $arrayForTheLoop = $this->getGenerator();
1845
        }
1846
1847 1
        foreach ($arrayForTheLoop as $key => $value) {
1848 1
            if ($value instanceof self) {
1849
                $value = $value->toArray();
1850
            }
1851
1852 1
            if (\array_key_exists($key, $array)) {
1853 1
                if ($value !== $array[$key]) {
1854 1
                    $result[$key] = $value;
1855
                }
1856
            } else {
1857 1
                $result[$key] = $value;
1858
            }
1859
        }
1860
1861 1
        return static::create(
1862 1
            $result,
1863 1
            $this->iteratorClass,
1864 1
            false
1865
        );
1866
    }
1867
1868
    /**
1869
     * Return values that are only in the new $array.
1870
     *
1871
     * @param array $array
1872
     *
1873
     * @return static
1874
     *                <p>(Immutable)</p>
1875
     *
1876
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1877
     * @psalm-return static<TKey,T>
1878
     * @psalm-mutation-free
1879
     */
1880 8
    public function diffReverse(array $array = []): self
1881
    {
1882 8
        return static::create(
1883 8
            \array_diff($array, $this->toArray()),
1884 8
            $this->iteratorClass,
1885 8
            false
1886
        );
1887
    }
1888
1889
    /**
1890
     * Divide an array into two arrays. One with keys and the other with values.
1891
     *
1892
     * @return static
1893
     *                <p>(Immutable)</p>
1894
     *
1895
     * @psalm-return static<TKey,T>
1896
     * @psalm-mutation-free
1897
     */
1898 1
    public function divide(): self
1899
    {
1900 1
        return static::create(
1901
            [
1902 1
                $this->keys(),
1903 1
                $this->values(),
1904
            ],
1905 1
            $this->iteratorClass,
1906 1
            false
1907
        );
1908
    }
1909
1910
    /**
1911
     * Iterate over the current array and modify the array's value.
1912
     *
1913
     * @param \Closure $closure
1914
     *
1915
     * @return static
1916
     *                <p>(Immutable)</p>
1917
     *
1918
     * @psalm-return static<TKey,T>
1919
     * @psalm-mutation-free
1920
     */
1921 5 View Code Duplication
    public function each(\Closure $closure): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1922
    {
1923
        // init
1924 5
        $array = [];
1925
1926 5
        foreach ($this->getGenerator() as $key => $value) {
1927 5
            $array[$key] = $closure($value, $key);
1928
        }
1929
1930 5
        return static::create(
1931 5
            $array,
1932 5
            $this->iteratorClass,
1933 5
            false
1934
        );
1935
    }
1936
1937
    /**
1938
     * Sets the internal iterator to the last element in the array and returns this element.
1939
     *
1940
     * @return mixed
1941
     */
1942
    public function end()
1943
    {
1944
        return \end($this->array);
1945
    }
1946
1947
    /**
1948
     * Check if a value is in the current array using a closure.
1949
     *
1950
     * @param \Closure $closure
1951
     *
1952
     * @return bool
1953
     *              <p>Returns true if the given value is found, false otherwise.</p>
1954
     */
1955 4
    public function exists(\Closure $closure): bool
1956
    {
1957
        // init
1958 4
        $isExists = false;
1959
1960 4
        foreach ($this->getGenerator() as $key => $value) {
1961 3
            if ($closure($value, $key)) {
1962 1
                $isExists = true;
1963
1964 1
                break;
1965
            }
1966
        }
1967
1968 4
        return $isExists;
1969
    }
1970
1971
    /**
1972
     * Fill the array until "$num" with "$default" values.
1973
     *
1974
     * @param int   $num
1975
     * @param mixed $default
1976
     *
1977
     * @return static
1978
     *                <p>(Immutable)</p>
1979
     *
1980
     * @psalm-return static<TKey,T>
1981
     * @psalm-mutation-free
1982
     */
1983 8
    public function fillWithDefaults(int $num, $default = null): self
1984
    {
1985 8
        if ($num < 0) {
1986 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1987
        }
1988
1989 7
        $this->generatorToArray();
1990
1991 7
        $tmpArray = $this->array;
1992
1993 7
        $count = \count($tmpArray);
1994
1995 7
        while ($count < $num) {
1996 4
            $tmpArray[] = $default;
1997 4
            ++$count;
1998
        }
1999
2000 7
        return static::create(
2001 7
            $tmpArray,
2002 7
            $this->iteratorClass,
2003 7
            false
2004
        );
2005
    }
2006
2007
    /**
2008
     * Find all items in an array that pass the truth test.
2009
     *
2010
     * @param \Closure|null $closure [optional] <p>
2011
     *                               The callback function to use
2012
     *                               </p>
2013
     *                               <p>
2014
     *                               If no callback is supplied, all entries of
2015
     *                               input equal to false (see
2016
     *                               converting to
2017
     *                               boolean) will be removed.
2018
     *                               </p>
2019
     * @param int           $flag    [optional] <p>
2020
     *                               Flag determining what arguments are sent to <i>callback</i>:
2021
     *                               </p><ul>
2022
     *                               <li>
2023
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
2024
     *                               to <i>callback</i> instead of the value</span>
2025
     *                               </li>
2026
     *                               <li>
2027
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
2028
     *                               arguments to <i>callback</i> instead of the value</span>
2029
     *                               </li>
2030
     *                               </ul>
2031
     *
2032
     * @return static
2033
     *                <p>(Immutable)</p>
2034
     *
2035
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
2036
     * @psalm-return static<TKey,T>
2037
     * @psalm-mutation-free
2038
     */
2039 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2040
    {
2041 12
        if (!$closure) {
2042 1
            return $this->clean();
2043
        }
2044
2045 12
        return static::create(
2046 12
            \array_filter($this->toArray(), $closure, $flag),
2047 12
            $this->iteratorClass,
2048 12
            false
2049
        );
2050
    }
2051
2052
    /**
2053
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2054
     * property within that.
2055
     *
2056
     * @param string          $property
2057
     * @param string|string[] $value
2058
     * @param string          $comparisonOp
2059
     *                                      <p>
2060
     *                                      'eq' (equals),<br />
2061
     *                                      'gt' (greater),<br />
2062
     *                                      'gte' || 'ge' (greater or equals),<br />
2063
     *                                      'lt' (less),<br />
2064
     *                                      'lte' || 'le' (less or equals),<br />
2065
     *                                      'ne' (not equals),<br />
2066
     *                                      'contains',<br />
2067
     *                                      'notContains',<br />
2068
     *                                      'newer' (via strtotime),<br />
2069
     *                                      'older' (via strtotime),<br />
2070
     *                                      </p>
2071
     *
2072
     * @return static
2073
     *                <p>(Immutable)</p>
2074
     *
2075
     * @psalm-return static<TKey,T>
2076
     * @psalm-mutation-free
2077
     *
2078
     * @psalm-suppress MissingClosureReturnType
2079
     * @psalm-suppress MissingClosureParamType
2080
     */
2081 1
    public function filterBy(
2082
        string $property,
2083
        $value,
2084
        string $comparisonOp = null
2085
    ): self {
2086 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...
2087 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2088
        }
2089
2090
        $ops = [
2091
            'eq' => static function ($item, $prop, $value): bool {
2092 1
                return $item[$prop] === $value;
2093 1
            },
2094
            'gt' => static function ($item, $prop, $value): bool {
2095
                return $item[$prop] > $value;
2096 1
            },
2097
            'ge' => static function ($item, $prop, $value): bool {
2098
                return $item[$prop] >= $value;
2099 1
            },
2100
            'gte' => static function ($item, $prop, $value): bool {
2101
                return $item[$prop] >= $value;
2102 1
            },
2103
            'lt' => static function ($item, $prop, $value): bool {
2104 1
                return $item[$prop] < $value;
2105 1
            },
2106
            'le' => static function ($item, $prop, $value): bool {
2107
                return $item[$prop] <= $value;
2108 1
            },
2109
            'lte' => static function ($item, $prop, $value): bool {
2110
                return $item[$prop] <= $value;
2111 1
            },
2112
            'ne' => static function ($item, $prop, $value): bool {
2113
                return $item[$prop] !== $value;
2114 1
            },
2115
            'contains' => static function ($item, $prop, $value): bool {
2116 1
                return \in_array($item[$prop], (array) $value, true);
2117 1
            },
2118
            'notContains' => static function ($item, $prop, $value): bool {
2119
                return !\in_array($item[$prop], (array) $value, true);
2120 1
            },
2121
            'newer' => static function ($item, $prop, $value): bool {
2122
                return \strtotime($item[$prop]) > \strtotime($value);
2123 1
            },
2124
            'older' => static function ($item, $prop, $value): bool {
2125
                return \strtotime($item[$prop]) < \strtotime($value);
2126 1
            },
2127
        ];
2128
2129 1
        $result = \array_values(
2130 1
            \array_filter(
2131 1
                $this->toArray(false, true),
2132
                static function ($item) use (
2133 1
                    $property,
2134 1
                    $value,
2135 1
                    $ops,
2136 1
                    $comparisonOp
2137
                ) {
2138 1
                    $item = (array) $item;
2139 1
                    $itemArrayy = static::create($item);
2140 1
                    $item[$property] = $itemArrayy->get($property, []);
2141
2142 1
                    return $ops[$comparisonOp]($item, $property, $value);
2143 1
                }
2144
            )
2145
        );
2146
2147 1
        return static::create(
2148 1
            $result,
2149 1
            $this->iteratorClass,
2150 1
            false
2151
        );
2152
    }
2153
2154
    /**
2155
     * Find the first item in an array that passes the truth test,
2156
     *  otherwise return false
2157
     *
2158
     * @param \Closure $closure
2159
     *
2160
     * @return false|mixed
2161
     *                     <p>Return false if we did not find the value.</p>
2162
     */
2163 8 View Code Duplication
    public function find(\Closure $closure)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2164
    {
2165 8
        foreach ($this->getGenerator() as $key => $value) {
2166 6
            if ($closure($value, $key)) {
2167 5
                return $value;
2168
            }
2169
        }
2170
2171 3
        return false;
2172
    }
2173
2174
    /**
2175
     * find by ...
2176
     *
2177
     * @param string          $property
2178
     * @param string|string[] $value
2179
     * @param string          $comparisonOp
2180
     *
2181
     * @return static
2182
     *                <p>(Immutable)</p>
2183
     *
2184
     * @psalm-return static<TKey,T>
2185
     * @psalm-mutation-free
2186
     */
2187 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2188
    {
2189 1
        return $this->filterBy($property, $value, $comparisonOp);
2190
    }
2191
2192
    /**
2193
     * Get the first value from the current array.
2194
     *
2195
     * @return mixed
2196
     *               <p>Return null if there wasn't a element.</p>
2197
     */
2198 22
    public function first()
2199
    {
2200 22
        $key_first = $this->firstKey();
2201 22
        if ($key_first === null) {
2202 3
            return null;
2203
        }
2204
2205 19
        return $this->get($key_first);
2206
    }
2207
2208
    /**
2209
     * Get the first key from the current array.
2210
     *
2211
     * @return mixed
2212
     *               <p>Return null if there wasn't a element.</p>
2213
     * @psalm-mutation-free
2214
     */
2215 29
    public function firstKey()
2216
    {
2217 29
        $this->generatorToArray();
2218
2219 29
        return \array_key_first($this->array);
2220
    }
2221
2222
    /**
2223
     * Get the first value(s) from the current array.
2224
     * And will return an empty array if there was no first entry.
2225
     *
2226
     * @param int|null $number <p>How many values you will take?</p>
2227
     *
2228
     * @return static
2229
     *                <p>(Immutable)</p>
2230
     *
2231
     * @psalm-return static<TKey,T>
2232
     * @psalm-mutation-free
2233
     */
2234 37 View Code Duplication
    public function firstsImmutable(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2235
    {
2236 37
        $arrayTmp = $this->toArray();
2237
2238 37
        if ($number === null) {
2239 14
            $array = (array) \array_shift($arrayTmp);
2240
        } else {
2241 23
            $number = (int) $number;
2242 23
            $array = \array_splice($arrayTmp, 0, $number);
2243
        }
2244
2245 37
        return static::create(
2246 37
            $array,
2247 37
            $this->iteratorClass,
2248 37
            false
2249
        );
2250
    }
2251
2252
    /**
2253
     * Get the first value(s) from the current array.
2254
     * And will return an empty array if there was no first entry.
2255
     *
2256
     * @param int|null $number <p>How many values you will take?</p>
2257
     *
2258
     * @return static
2259
     *                <p>(Immutable)</p>
2260
     *
2261
     * @psalm-return static<TKey,T>
2262
     * @psalm-mutation-free
2263
     */
2264 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...
2265
    {
2266 3
        $arrayTmp = $this->keys()->toArray();
2267
2268 3
        if ($number === null) {
2269
            $array = (array) \array_shift($arrayTmp);
2270
        } else {
2271 3
            $number = (int) $number;
2272 3
            $array = \array_splice($arrayTmp, 0, $number);
2273
        }
2274
2275 3
        return static::create(
2276 3
            $array,
2277 3
            $this->iteratorClass,
2278 3
            false
2279
        );
2280
    }
2281
2282
    /**
2283
     * Get and rmove the first value(s) from the current array.
2284
     * And will return an empty array if there was no first entry.
2285
     *
2286
     * @param int|null $number <p>How many values you will take?</p>
2287
     *
2288
     * @return $this
2289
     *               <p>(Mutable)</p>
2290
     *
2291
     * @psalm-return static<TKey,T>
2292
     */
2293 34
    public function firstsMutable(int $number = null): self
2294
    {
2295 34
        $this->generatorToArray();
2296
2297 34
        if ($number === null) {
2298 19
            $this->array = (array) \array_shift($this->array);
2299
        } else {
2300 15
            $number = (int) $number;
2301 15
            $this->array = \array_splice($this->array, 0, $number);
2302
        }
2303
2304 34
        return $this;
2305
    }
2306
2307
    /**
2308
     * Exchanges all keys with their associated values in an array.
2309
     *
2310
     * @return static
2311
     *                <p>(Immutable)</p>
2312
     *
2313
     * @psalm-return static<TKey,T>
2314
     * @psalm-mutation-free
2315
     */
2316 1
    public function flip(): self
2317
    {
2318 1
        return static::create(
2319 1
            \array_flip($this->toArray()),
2320 1
            $this->iteratorClass,
2321 1
            false
2322
        );
2323
    }
2324
2325
    /**
2326
     * Get a value from an array (optional using dot-notation).
2327
     *
2328
     * @param mixed $key      <p>The key to look for.</p>
2329
     * @param mixed $fallback <p>Value to fallback to.</p>
2330
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
2331
     *                        class.</p>
2332
     *
2333
     * @return mixed|static
2334
     *
2335
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2336
     * @psalm-mutation-free
2337
     */
2338 172
    public function get($key, $fallback = null, array $array = null)
2339
    {
2340 172
        if ($array !== null) {
2341 4
            $usedArray = $array;
2342
        } else {
2343 169
            $this->generatorToArray();
2344
2345 169
            $usedArray = $this->array;
2346
        }
2347
2348 172
        if ($key === null) {
2349 1
            return static::create(
2350 1
                $usedArray,
2351 1
                $this->iteratorClass,
2352 1
                false
2353
            );
2354
        }
2355
2356
        // php cast "bool"-index into "int"-index
2357 172
        if ((bool) $key === $key) {
2358 3
            $key = (int) $key;
2359
        }
2360
2361 172
        if (\array_key_exists($key, $usedArray) === true) {
2362 162
            if (\is_array($usedArray[$key]) === true) {
2363 12
                return static::create(
2364 12
                    $usedArray[$key],
2365 12
                    $this->iteratorClass,
2366 12
                    false
2367
                );
2368
            }
2369
2370 154
            return $usedArray[$key];
2371
        }
2372
2373
        // crawl through array, get key according to object or not
2374 24
        $usePath = false;
2375
        if (
2376 24
            $this->pathSeparator
2377
            &&
2378 24
            (string) $key === $key
2379
            &&
2380 24
            \strpos($key, $this->pathSeparator) !== false
2381
        ) {
2382 7
            $segments = \explode($this->pathSeparator, (string) $key);
2383 7
            if ($segments !== false) {
2384 7
                $usePath = true;
2385
2386 7
                foreach ($segments as $segment) {
2387
                    if (
2388
                        (
2389 7
                            \is_array($usedArray) === true
2390
                            ||
2391 7
                            $usedArray instanceof \ArrayAccess
2392
                        )
2393
                        &&
2394 7
                        isset($usedArray[$segment])
2395
                    ) {
2396 7
                        $usedArray = $usedArray[$segment];
2397
2398 7
                        continue;
2399
                    }
2400
2401
                    if (
2402 6
                        \is_object($usedArray) === true
2403
                        &&
2404 6
                        \property_exists($usedArray, $segment)
2405
                    ) {
2406 1
                        $usedArray = $usedArray->{$segment};
2407
2408 1
                        continue;
2409
                    }
2410
2411 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2412
                }
2413
            }
2414
        }
2415
2416 24
        if (!$usePath && !isset($usedArray[$key])) {
2417 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2418
        }
2419
2420 7
        if (\is_array($usedArray) === true) {
2421 1
            return static::create(
2422 1
                $usedArray,
2423 1
                $this->iteratorClass,
2424 1
                false
2425
            );
2426
        }
2427
2428 7
        return $usedArray;
2429
    }
2430
2431
    /**
2432
     * alias: for "Arrayy->toArray()"
2433
     *
2434
     * @return array
2435
     *
2436
     * @see          Arrayy::getArray()
2437
     *
2438
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2439
     */
2440 1
    public function getAll(): array
2441
    {
2442 1
        return $this->toArray();
2443
    }
2444
2445
    /**
2446
     * Get the current array from the "Arrayy"-object.
2447
     *
2448
     * alias for "toArray()"
2449
     *
2450
     * @param bool $convertAllArrayyElements <p>
2451
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2452
     *                                       </p>
2453
     * @param bool $preserveKeys             <p>
2454
     *                                       e.g.: A generator maybe return the same key more then once,
2455
     *                                       so maybe you will ignore the keys.
2456
     *                                       </p>
2457
     *
2458
     * @return array
2459
     *
2460
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2461
     * @psalm-mutation-free
2462
     *
2463
     * @see Arrayy::toArray()
2464
     */
2465 481
    public function getArray(
2466
        bool $convertAllArrayyElements = false,
2467
        bool $preserveKeys = true
2468
    ): array {
2469 481
        return $this->toArray(
2470 481
            $convertAllArrayyElements,
2471 481
            $preserveKeys
2472
        );
2473
    }
2474
2475
    /**
2476
     * Get the current array from the "Arrayy"-object as list.
2477
     *
2478
     * alias for "toList()"
2479
     *
2480
     * @param bool $convertAllArrayyElements <p>
2481
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2482
     *                                       </p>
2483
     *
2484
     * @return array
2485
     *
2486
     * @psalm-return array<int,mixed>|array<int,T>
2487
     * @psalm-mutation-free
2488
     *
2489
     * @see Arrayy::toList()
2490
     */
2491 1
    public function getList(bool $convertAllArrayyElements = false): array
2492
    {
2493 1
        return $this->toList($convertAllArrayyElements);
2494
    }
2495
2496
    /**
2497
     * Returns the values from a single column of the input array, identified by
2498
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2499
     *
2500
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2501
     * array by the values from the $indexKey column in the input array.
2502
     *
2503
     * @param mixed $columnKey
2504
     * @param mixed $indexKey
2505
     *
2506
     * @return static
2507
     *                <p>(Immutable)</p>
2508
     *
2509
     * @psalm-return static<TKey,T>
2510
     * @psalm-mutation-free
2511
     */
2512 1
    public function getColumn($columnKey = null, $indexKey = null): self
2513
    {
2514 1
        return static::create(
2515 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2516 1
            $this->iteratorClass,
2517 1
            false
2518
        );
2519
    }
2520
2521
    /**
2522
     * Get the current array from the "Arrayy"-object as generator.
2523
     *
2524
     * @return \Generator
2525
     *
2526
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2527
     * @psalm-mutation-free
2528
     */
2529 974
    public function getGenerator(): \Generator
2530
    {
2531 974
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2532 42
            yield from $this->generator;
2533
        }
2534
2535 974
        yield from $this->array;
2536 930
    }
2537
2538
    /**
2539
     * alias: for "Arrayy->keys()"
2540
     *
2541
     * @return static
2542
     *                <p>(Immutable)</p>
2543
     *
2544
     * @see          Arrayy::keys()
2545
     *
2546
     * @psalm-return static<array-key,TKey>
2547
     * @psalm-mutation-free
2548
     */
2549 2
    public function getKeys()
2550
    {
2551 2
        return $this->keys();
2552
    }
2553
2554
    /**
2555
     * Get the current array from the "Arrayy"-object as object.
2556
     *
2557
     * @return \stdClass
2558
     */
2559 4
    public function getObject(): \stdClass
2560
    {
2561 4
        return self::arrayToObject($this->toArray());
2562
    }
2563
2564
    /**
2565
     * alias: for "Arrayy->randomImmutable()"
2566
     *
2567
     * @return static
2568
     *                <p>(Immutable)</p>
2569
     *
2570
     * @see          Arrayy::randomImmutable()
2571
     *
2572
     * @psalm-return static<int|array-key,T>
2573
     */
2574 4
    public function getRandom(): self
2575
    {
2576 4
        return $this->randomImmutable();
2577
    }
2578
2579
    /**
2580
     * alias: for "Arrayy->randomKey()"
2581
     *
2582
     * @return mixed
2583
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2584
     *
2585
     * @see Arrayy::randomKey()
2586
     */
2587 3
    public function getRandomKey()
2588
    {
2589 3
        return $this->randomKey();
2590
    }
2591
2592
    /**
2593
     * alias: for "Arrayy->randomKeys()"
2594
     *
2595
     * @param int $number
2596
     *
2597
     * @return static
2598
     *                <p>(Immutable)</p>
2599
     *
2600
     * @see          Arrayy::randomKeys()
2601
     *
2602
     * @psalm-return static<TKey,T>
2603
     */
2604 8
    public function getRandomKeys(int $number): self
2605
    {
2606 8
        return $this->randomKeys($number);
2607
    }
2608
2609
    /**
2610
     * alias: for "Arrayy->randomValue()"
2611
     *
2612
     * @return mixed
2613
     *               <p>Get a random value or null if there wasn't a value.</p>
2614
     *
2615
     * @see Arrayy::randomValue()
2616
     */
2617 3
    public function getRandomValue()
2618
    {
2619 3
        return $this->randomValue();
2620
    }
2621
2622
    /**
2623
     * alias: for "Arrayy->randomValues()"
2624
     *
2625
     * @param int $number
2626
     *
2627
     * @return static
2628
     *                <p>(Immutable)</p>
2629
     *
2630
     * @see          Arrayy::randomValues()
2631
     *
2632
     * @psalm-return static<TKey,T>
2633
     */
2634 6
    public function getRandomValues(int $number): self
2635
    {
2636 6
        return $this->randomValues($number);
2637
    }
2638
2639
    /**
2640
     * Gets all values.
2641
     *
2642
     * @return static
2643
     *                <p>The values of all elements in this array, in the order they
2644
     *                appear in the array.</p>
2645
     *
2646
     * @psalm-return static<TKey,T>
2647
     */
2648 4
    public function getValues()
2649
    {
2650 4
        $this->generatorToArray(false);
2651
2652 4
        return static::create(
2653 4
            \array_values($this->array),
2654 4
            $this->iteratorClass,
2655 4
            false
2656
        );
2657
    }
2658
2659
    /**
2660
     * Gets all values via Generator.
2661
     *
2662
     * @return \Generator
2663
     *                    <p>The values of all elements in this array, in the order they
2664
     *                    appear in the array as Generator.</p>
2665
     *
2666
     * @psalm-return \Generator<TKey,T>
2667
     */
2668 4
    public function getValuesYield(): \Generator
2669
    {
2670 4
        yield from $this->getGenerator();
2671 4
    }
2672
2673
    /**
2674
     * Group values from a array according to the results of a closure.
2675
     *
2676
     * @param callable|string $grouper  <p>A callable function name.</p>
2677
     * @param bool            $saveKeys
2678
     *
2679
     * @return static
2680
     *                <p>(Immutable)</p>
2681
     *
2682
     * @psalm-return static<TKey,T>
2683
     * @psalm-mutation-free
2684
     */
2685 4
    public function group($grouper, bool $saveKeys = false): self
2686
    {
2687
        // init
2688 4
        $result = [];
2689
2690
        // Iterate over values, group by property/results from closure.
2691 4
        foreach ($this->getGenerator() as $key => $value) {
2692 4
            if (\is_callable($grouper) === true) {
2693 3
                $groupKey = $grouper($value, $key);
2694
            } else {
2695 1
                $groupKey = $this->get($grouper);
2696
            }
2697
2698 4
            $newValue = $this->get($groupKey, null, $result);
2699
2700 4
            if ($groupKey instanceof self) {
2701
                $groupKey = $groupKey->toArray();
2702
            }
2703
2704 4
            if ($newValue instanceof self) {
2705 4
                $newValue = $newValue->toArray();
2706
            }
2707
2708
            // Add to results.
2709 4
            if ($groupKey !== null) {
2710 3
                if ($saveKeys) {
2711 2
                    $result[$groupKey] = $newValue;
2712 2
                    $result[$groupKey][$key] = $value;
2713
                } else {
2714 1
                    $result[$groupKey] = $newValue;
2715 1
                    $result[$groupKey][] = $value;
2716
                }
2717
            }
2718
        }
2719
2720 4
        return static::create(
2721 4
            $result,
2722 4
            $this->iteratorClass,
2723 4
            false
2724
        );
2725
    }
2726
2727
    /**
2728
     * Check if an array has a given key.
2729
     *
2730
     * @param mixed $key
2731
     *
2732
     * @return bool
2733
     */
2734 23
    public function has($key): bool
2735
    {
2736 23
        static $UN_FOUND = null;
2737
2738 23
        if ($UN_FOUND === null) {
2739
            // Generate unique string to use as marker.
2740 1
            $UN_FOUND = \uniqid('arrayy', true);
2741
        }
2742
2743 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2744
    }
2745
2746
    /**
2747
     * Check if an array has a given value.
2748
     *
2749
     * INFO: if you need to search recursive please use ```contains()```
2750
     *
2751
     * @param mixed $value
2752
     *
2753
     * @return bool
2754
     */
2755 1
    public function hasValue($value): bool
2756
    {
2757 1
        return $this->contains($value);
2758
    }
2759
2760
    /**
2761
     * Implodes the values of this array.
2762
     *
2763
     * @param string $glue
2764
     *
2765
     * @return string
2766
     */
2767 28
    public function implode(string $glue = ''): string
2768
    {
2769 28
        return $this->implode_recursive($glue, $this->toArray(), false);
2770
    }
2771
2772
    /**
2773
     * Implodes the keys of this array.
2774
     *
2775
     * @param string $glue
2776
     *
2777
     * @return string
2778
     */
2779 8
    public function implodeKeys(string $glue = ''): string
2780
    {
2781 8
        return $this->implode_recursive($glue, $this->toArray(), true);
2782
    }
2783
2784
    /**
2785
     * Given a list and an iterate-function that returns
2786
     * a key for each element in the list (or a property name),
2787
     * returns an object with an index of each item.
2788
     *
2789
     * @param mixed $key
2790
     *
2791
     * @return static
2792
     *                <p>(Immutable)</p>
2793
     *
2794
     * @psalm-return static<TKey,T>
2795
     * @psalm-mutation-free
2796
     */
2797 4
    public function indexBy($key): self
2798
    {
2799
        // init
2800 4
        $results = [];
2801
2802 4
        foreach ($this->getGenerator() as $a) {
2803 4
            if (\array_key_exists($key, $a) === true) {
2804 3
                $results[$a[$key]] = $a;
2805
            }
2806
        }
2807
2808 4
        return static::create(
2809 4
            $results,
2810 4
            $this->iteratorClass,
2811 4
            false
2812
        );
2813
    }
2814
2815
    /**
2816
     * alias: for "Arrayy->searchIndex()"
2817
     *
2818
     * @param mixed $value <p>The value to search for.</p>
2819
     *
2820
     * @return false|mixed
2821
     *
2822
     * @see Arrayy::searchIndex()
2823
     */
2824 4
    public function indexOf($value)
2825
    {
2826 4
        return $this->searchIndex($value);
2827
    }
2828
2829
    /**
2830
     * Get everything but the last..$to items.
2831
     *
2832
     * @param int $to
2833
     *
2834
     * @return static
2835
     *                <p>(Immutable)</p>
2836
     *
2837
     * @psalm-return static<TKey,T>
2838
     * @psalm-mutation-free
2839
     */
2840 12
    public function initial(int $to = 1): self
2841
    {
2842 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
2843
    }
2844
2845
    /**
2846
     * Return an array with all elements found in input array.
2847
     *
2848
     * @param array $search
2849
     * @param bool  $keepKeys
2850
     *
2851
     * @return static
2852
     *                <p>(Immutable)</p>
2853
     *
2854
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
2855
     * @psalm-return static<TKey,T>
2856
     * @psalm-mutation-free
2857
     */
2858 4
    public function intersection(array $search, bool $keepKeys = false): self
2859
    {
2860 4
        if ($keepKeys) {
2861
            /**
2862
             * @psalm-suppress MissingClosureReturnType
2863
             * @psalm-suppress MissingClosureParamType
2864
             */
2865 1
            return static::create(
2866 1
                \array_uintersect(
2867 1
                    $this->toArray(),
2868 1
                    $search,
2869
                    static function ($a, $b) {
2870 1
                        return $a === $b ? 0 : -1;
2871 1
                    }
2872
                ),
2873 1
                $this->iteratorClass,
2874 1
                false
2875
            );
2876
        }
2877
2878 3
        return static::create(
2879 3
            \array_values(\array_intersect($this->toArray(), $search)),
2880 3
            $this->iteratorClass,
2881 3
            false
2882
        );
2883
    }
2884
2885
    /**
2886
     * Return an array with all elements found in input array.
2887
     *
2888
     * @param array ...$array
2889
     *
2890
     * @return static
2891
     *                <p>(Immutable)</p>
2892
     *
2893
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2894
     * @psalm-return static<TKey,T>
2895
     * @psalm-mutation-free
2896
     */
2897 1
    public function intersectionMulti(...$array): self
2898
    {
2899 1
        return static::create(
2900 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
2901 1
            $this->iteratorClass,
2902 1
            false
2903
        );
2904
    }
2905
2906
    /**
2907
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2908
     *
2909
     * @param array $search
2910
     *
2911
     * @return bool
2912
     *
2913
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
2914
     */
2915 1
    public function intersects(array $search): bool
2916
    {
2917 1
        return $this->intersection($search)->count() > 0;
2918
    }
2919
2920
    /**
2921
     * Invoke a function on all of an array's values.
2922
     *
2923
     * @param callable $callable
2924
     * @param mixed    $arguments
2925
     *
2926
     * @return static
2927
     *                <p>(Immutable)</p>
2928
     *
2929
     * @psalm-param  callable(T=,mixed):mixed $callable
2930
     * @psalm-return static<TKey,T>
2931
     * @psalm-mutation-free
2932
     */
2933 1
    public function invoke($callable, $arguments = []): self
2934
    {
2935
        // If one argument given for each iteration, create an array for it.
2936 1
        if (\is_array($arguments) === false) {
2937 1
            $arguments = \array_fill(
2938 1
                0,
2939 1
                $this->count(),
2940 1
                $arguments
2941
            );
2942
        }
2943
2944
        // If the callable has arguments, pass them.
2945 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...
2946 1
            $array = \array_map($callable, $this->toArray(), $arguments);
2947
        } else {
2948 1
            $array = $this->map($callable);
2949
        }
2950
2951 1
        return static::create(
2952 1
            $array,
2953 1
            $this->iteratorClass,
2954 1
            false
2955
        );
2956
    }
2957
2958
    /**
2959
     * Check whether array is associative or not.
2960
     *
2961
     * @param bool $recursive
2962
     *
2963
     * @return bool
2964
     *              <p>Returns true if associative, false otherwise.</p>
2965
     */
2966 15 View Code Duplication
    public function isAssoc(bool $recursive = false): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2967
    {
2968 15
        if ($this->isEmpty()) {
2969 3
            return false;
2970
        }
2971
2972 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2973 13
            if ((string) $key !== $key) {
2974 11
                return false;
2975
            }
2976
        }
2977
2978 3
        return true;
2979
    }
2980
2981
    /**
2982
     * Check if a given key or keys are empty.
2983
     *
2984
     * @param int|int[]|string|string[]|null $keys
2985
     *
2986
     * @return bool
2987
     *              <p>Returns true if empty, false otherwise.</p>
2988
     * @psalm-mutation-free
2989
     */
2990 38
    public function isEmpty($keys = null): bool
2991
    {
2992 38
        if ($this->generator) {
2993
            return $this->toArray() === [];
2994
        }
2995
2996 38
        if ($keys === null) {
2997 38
            return $this->array === [];
2998
        }
2999
3000
        foreach ((array) $keys as $key) {
3001
            if (!empty($this->get($key))) {
3002
                return false;
3003
            }
3004
        }
3005
3006
        return true;
3007
    }
3008
3009
    /**
3010
     * Check if the current array is equal to the given "$array" or not.
3011
     *
3012
     * @param array $array
3013
     *
3014
     * @return bool
3015
     *
3016
     * @psalm-param array<mixed,mixed> $array
3017
     */
3018 1
    public function isEqual(array $array): bool
3019
    {
3020 1
        return $this->toArray() === $array;
3021
    }
3022
3023
    /**
3024
     * Check if the current array is a multi-array.
3025
     *
3026
     * @return bool
3027
     */
3028 22
    public function isMultiArray(): bool
3029
    {
3030
        return !(
3031 22
            \count($this->toArray(), \COUNT_NORMAL)
3032
            ===
3033 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3034
        );
3035
    }
3036
3037
    /**
3038
     * Check whether array is numeric or not.
3039
     *
3040
     * @return bool
3041
     *              <p>Returns true if numeric, false otherwise.</p>
3042
     */
3043 5 View Code Duplication
    public function isNumeric(): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3044
    {
3045 5
        if ($this->isEmpty()) {
3046 2
            return false;
3047
        }
3048
3049 4
        foreach ($this->keys()->getGenerator() as $key) {
3050 4
            if ((int) $key !== $key) {
3051 2
                return false;
3052
            }
3053
        }
3054
3055 2
        return true;
3056
    }
3057
3058
    /**
3059
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3060
     *
3061
     * @param bool $recursive
3062
     *
3063
     * @return bool
3064
     * @psalm-mutation-free
3065
     */
3066 9
    public function isSequential(bool $recursive = false): bool
3067
    {
3068
3069
        // recursive
3070
3071 9
        if ($recursive === true) {
3072
            return $this->array_keys_recursive($this->toArray())
3073
                   ===
3074
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3075
        }
3076
3077
        // non recursive
3078
3079 9
        return \array_keys($this->toArray())
3080
               ===
3081 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3082
    }
3083
3084
    /**
3085
     * @return array
3086
     *
3087
     * @psalm-return array<TKey,T>
3088
     */
3089
    public function jsonSerialize(): array
3090
    {
3091
        return $this->toArray();
3092
    }
3093
3094
    /**
3095
     * Gets the key/index of the element at the current internal iterator position.
3096
     *
3097
     * @return int|string|null
3098
     */
3099
    public function key()
3100
    {
3101
        return \key($this->array);
3102
    }
3103
3104
    /**
3105
     * Checks if the given key exists in the provided array.
3106
     *
3107
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3108
     *       then you need to use "Arrayy->offsetExists()".
3109
     *
3110
     * @param int|string $key the key to look for
3111
     *
3112
     * @return bool
3113
     * @psalm-mutation-free
3114
     */
3115 127
    public function keyExists($key): bool
3116
    {
3117 127
        return \array_key_exists($key, $this->array);
3118
    }
3119
3120
    /**
3121
     * Get all keys from the current array.
3122
     *
3123
     * @param bool       $recursive     [optional] <p>
3124
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3125
     *                                  </p>
3126
     * @param mixed|null $search_values [optional] <p>
3127
     *                                  If specified, then only keys containing these values are returned.
3128
     *                                  </p>
3129
     * @param bool       $strict        [optional] <p>
3130
     *                                  Determines if strict comparison (===) should be used during the search.
3131
     *                                  </p>
3132
     *
3133
     * @return static
3134
     *                <p>(Immutable) An array of all the keys in input.</p>
3135
     *
3136
     * @psalm-return static<array-key,TKey>
3137
     * @psalm-mutation-free
3138
     */
3139 29
    public function keys(
3140
        bool $recursive = false,
3141
        $search_values = null,
3142
        bool $strict = true
3143
    ): self {
3144
3145
        // recursive
3146
3147 29
        if ($recursive === true) {
3148 4
            $array = $this->array_keys_recursive(
3149 4
                null,
3150 4
                $search_values,
3151 4
                $strict
3152
            );
3153
3154 4
            return static::create(
3155 4
                $array,
3156 4
                $this->iteratorClass,
3157 4
                false
3158
            );
3159
        }
3160
3161
        // non recursive
3162
3163 28
        if ($search_values === null) {
3164
            $arrayFunction = function (): \Generator {
3165 28
                foreach ($this->getGenerator() as $key => $value) {
3166 26
                    yield $key;
3167
                }
3168 28
            };
3169
        } else {
3170
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3171 1
                $is_array_tmp = \is_array($search_values);
3172
3173 1
                foreach ($this->getGenerator() as $key => $value) {
3174 View Code Duplication
                    if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3175
                        (
3176 1
                            $is_array_tmp === false
3177
                            &&
3178 1
                            $strict === true
3179
                            &&
3180 1
                            $search_values === $value
3181
                        )
3182
                        ||
3183
                        (
3184 1
                            $is_array_tmp === false
3185
                            &&
3186 1
                            $strict === false
3187
                            &&
3188 1
                            $search_values == $value
3189
                        )
3190
                        ||
3191
                        (
3192 1
                            $is_array_tmp === true
3193
                            &&
3194 1
                            \in_array($value, $search_values, $strict)
3195
                        )
3196
                    ) {
3197 1
                        yield $key;
3198
                    }
3199
                }
3200 1
            };
3201
        }
3202
3203 28
        return static::create(
3204 28
            $arrayFunction,
3205 28
            $this->iteratorClass,
3206 28
            false
3207
        );
3208
    }
3209
3210
    /**
3211
     * Sort an array by key in reverse order.
3212
     *
3213
     * @param int $sort_flags [optional] <p>
3214
     *                        You may modify the behavior of the sort using the optional
3215
     *                        parameter sort_flags, for details
3216
     *                        see sort.
3217
     *                        </p>
3218
     *
3219
     * @return $this
3220
     *               <p>(Mutable) Return this Arrayy object.</p>
3221
     *
3222
     * @psalm-return static<TKey,T>
3223
     */
3224 4
    public function krsort(int $sort_flags = 0): self
3225
    {
3226 4
        $this->generatorToArray();
3227
3228 4
        \krsort($this->array, $sort_flags);
3229
3230 4
        return $this;
3231
    }
3232
3233
    /**
3234
     * Sort an array by key in reverse order.
3235
     *
3236
     * @param int $sort_flags [optional] <p>
3237
     *                        You may modify the behavior of the sort using the optional
3238
     *                        parameter sort_flags, for details
3239
     *                        see sort.
3240
     *                        </p>
3241
     *
3242
     * @return $this
3243
     *               <p>(Immutable) Return this Arrayy object.</p>
3244
     *
3245
     * @psalm-return static<TKey,T>
3246
     * @psalm-mutation-free
3247
     */
3248 4
    public function krsortImmutable(int $sort_flags = 0): self
3249
    {
3250 4
        $that = clone $this;
3251
3252
        /**
3253
         * @psalm-suppress ImpureMethodCall - object is already cloned
3254
         */
3255 4
        $that->krsort($sort_flags);
3256
3257 4
        return $that;
3258
    }
3259
3260
    /**
3261
     * Get the last value from the current array.
3262
     *
3263
     * @return mixed|null
3264
     *                    <p>Return null if there wasn't a element.</p>
3265
     * @psalm-mutation-free
3266
     */
3267 17
    public function last()
3268
    {
3269 17
        $key_last = $this->lastKey();
3270 17
        if ($key_last === null) {
3271 2
            return null;
3272
        }
3273
3274 15
        return $this->get($key_last);
3275
    }
3276
3277
    /**
3278
     * Get the last key from the current array.
3279
     *
3280
     * @return mixed|null
3281
     *                    <p>Return null if there wasn't a element.</p>
3282
     * @psalm-mutation-free
3283
     */
3284 21
    public function lastKey()
3285
    {
3286 21
        $this->generatorToArray();
3287
3288 21
        return \array_key_last($this->array);
3289
    }
3290
3291
    /**
3292
     * Get the last value(s) from the current array.
3293
     *
3294
     * @param int|null $number
3295
     *
3296
     * @return static
3297
     *                <p>(Immutable)</p>
3298
     *
3299
     * @psalm-return static<TKey,T>
3300
     * @psalm-mutation-free
3301
     */
3302 13
    public function lastsImmutable(int $number = null): self
3303
    {
3304 13
        if ($this->isEmpty()) {
3305 1
            return static::create(
3306 1
                [],
3307 1
                $this->iteratorClass,
3308 1
                false
3309
            );
3310
        }
3311
3312 12
        if ($number === null) {
3313 8
            $poppedValue = $this->last();
3314
3315 8
            if ($poppedValue === null) {
3316 1
                $poppedValue = [$poppedValue];
3317
            } else {
3318 7
                $poppedValue = (array) $poppedValue;
3319
            }
3320
3321 8
            $arrayy = static::create(
3322 8
                $poppedValue,
3323 8
                $this->iteratorClass,
3324 8
                false
3325
            );
3326
        } else {
3327 4
            $number = (int) $number;
3328 4
            $arrayy = $this->rest(-$number);
3329
        }
3330
3331 12
        return $arrayy;
3332
    }
3333
3334
    /**
3335
     * Get the last value(s) from the current array.
3336
     *
3337
     * @param int|null $number
3338
     *
3339
     * @return $this
3340
     *               <p>(Mutable)</p>
3341
     *
3342
     * @psalm-return static<TKey,T>
3343
     */
3344 13
    public function lastsMutable(int $number = null): self
3345
    {
3346 13
        if ($this->isEmpty()) {
3347 1
            return $this;
3348
        }
3349
3350 12
        if ($number === null) {
3351 8
            $poppedValue = $this->last();
3352
3353 8
            if ($poppedValue === null) {
3354 1
                $poppedValue = [$poppedValue];
3355
            } else {
3356 7
                $poppedValue = (array) $poppedValue;
3357
            }
3358
3359 8
            $this->array = static::create(
3360 8
                $poppedValue,
3361 8
                $this->iteratorClass,
3362 8
                false
3363 8
            )->toArray();
3364
        } else {
3365 4
            $number = (int) $number;
3366 4
            $this->array = $this->rest(-$number)->toArray();
3367
        }
3368
3369 12
        $this->generator = null;
3370
3371 12
        return $this;
3372
    }
3373
3374
    /**
3375
     * Count the values from the current array.
3376
     *
3377
     * alias: for "Arrayy->count()"
3378
     *
3379
     * @param int $mode
3380
     *
3381
     * @return int
3382
     *
3383
     * @see Arrayy::count()
3384
     */
3385 20
    public function length(int $mode = \COUNT_NORMAL): int
3386
    {
3387 20
        return $this->count($mode);
3388
    }
3389
3390
    /**
3391
     * Apply the given function to the every element of the array,
3392
     * collecting the results.
3393
     *
3394
     * @param callable $callable
3395
     * @param bool     $useKeyAsSecondParameter
3396
     * @param mixed    ...$arguments
3397
     *
3398
     * @return static
3399
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3400
     *
3401
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3402
     * @psalm-return static<TKey,T>
3403
     * @psalm-mutation-free
3404
     */
3405 5
    public function map(
3406
        callable $callable,
3407
        bool $useKeyAsSecondParameter = false,
3408
        ...$arguments
3409
    ) {
3410
        /**
3411
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3412
         */
3413 5
        $useArguments = \func_num_args() > 2;
3414
3415 5
        return static::create(
3416
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3417 5
                foreach ($this->getGenerator() as $key => $value) {
3418 4
                    if ($useArguments) {
3419 3
                        if ($useKeyAsSecondParameter) {
3420
                            yield $key => $callable($value, $key, ...$arguments);
3421
                        } else {
3422 3
                            yield $key => $callable($value, ...$arguments);
3423
                        }
3424
                    } else {
3425
                        /** @noinspection NestedPositiveIfStatementsInspection */
3426 4
                        if ($useKeyAsSecondParameter) {
3427
                            yield $key => $callable($value, $key);
3428
                        } else {
3429 4
                            yield $key => $callable($value);
3430
                        }
3431
                    }
3432
                }
3433 5
            },
3434 5
            $this->iteratorClass,
3435 5
            false
3436
        );
3437
    }
3438
3439
    /**
3440
     * Check if all items in current array match a truth test.
3441
     *
3442
     * @param \Closure $closure
3443
     *
3444
     * @return bool
3445
     */
3446 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...
3447
    {
3448 15
        if ($this->count() === 0) {
3449 2
            return false;
3450
        }
3451
3452 13
        foreach ($this->getGenerator() as $key => $value) {
3453 13
            $value = $closure($value, $key);
3454
3455 13
            if ($value === false) {
3456 7
                return false;
3457
            }
3458
        }
3459
3460 7
        return true;
3461
    }
3462
3463
    /**
3464
     * Check if any item in the current array matches a truth test.
3465
     *
3466
     * @param \Closure $closure
3467
     *
3468
     * @return bool
3469
     */
3470 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...
3471
    {
3472 14
        if ($this->count() === 0) {
3473 2
            return false;
3474
        }
3475
3476 12
        foreach ($this->getGenerator() as $key => $value) {
3477 12
            $value = $closure($value, $key);
3478
3479 12
            if ($value === true) {
3480 9
                return true;
3481
            }
3482
        }
3483
3484 4
        return false;
3485
    }
3486
3487
    /**
3488
     * Get the max value from an array.
3489
     *
3490
     * @return false|mixed
3491
     *                     <p>Will return false if there are no values.</p>
3492
     */
3493 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...
3494
    {
3495 10
        if ($this->count() === 0) {
3496 1
            return false;
3497
        }
3498
3499 9
        $max = false;
3500 9
        foreach ($this->getGenerator() as $value) {
3501
            if (
3502 9
                $max === false
3503
                ||
3504 9
                $value > $max
3505
            ) {
3506 9
                $max = $value;
3507
            }
3508
        }
3509
3510 9
        return $max;
3511
    }
3512
3513
    /**
3514
     * Merge the new $array into the current array.
3515
     *
3516
     * - keep key,value from the current array, also if the index is in the new $array
3517
     *
3518
     * @param array $array
3519
     * @param bool  $recursive
3520
     *
3521
     * @return static
3522
     *                <p>(Immutable)</p>
3523
     *
3524
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3525
     * @psalm-return static<int|TKey,T>
3526
     * @psalm-mutation-free
3527
     */
3528 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...
3529
    {
3530 25
        if ($recursive === true) {
3531 4
            $result = \array_replace_recursive($this->toArray(), $array);
3532
        } else {
3533 21
            $result = \array_replace($this->toArray(), $array);
3534
        }
3535
3536 25
        return static::create(
3537 25
            $result,
3538 25
            $this->iteratorClass,
3539 25
            false
3540
        );
3541
    }
3542
3543
    /**
3544
     * Merge the new $array into the current array.
3545
     *
3546
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3547
     * - create new indexes
3548
     *
3549
     * @param array $array
3550
     * @param bool  $recursive
3551
     *
3552
     * @return static
3553
     *                <p>(Immutable)</p>
3554
     *
3555
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3556
     * @psalm-return static<TKey,T>
3557
     * @psalm-mutation-free
3558
     */
3559 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...
3560
    {
3561 16
        if ($recursive === true) {
3562 4
            $result = \array_merge_recursive($this->toArray(), $array);
3563
        } else {
3564 12
            $result = \array_merge($this->toArray(), $array);
3565
        }
3566
3567 16
        return static::create(
3568 16
            $result,
3569 16
            $this->iteratorClass,
3570 16
            false
3571
        );
3572
    }
3573
3574
    /**
3575
     * Merge the the current array into the $array.
3576
     *
3577
     * - use key,value from the new $array, also if the index is in the current array
3578
     *
3579
     * @param array $array
3580
     * @param bool  $recursive
3581
     *
3582
     * @return static
3583
     *                <p>(Immutable)</p>
3584
     *
3585
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3586
     * @psalm-return static<TKey,T>
3587
     * @psalm-mutation-free
3588
     */
3589 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...
3590
    {
3591 16
        if ($recursive === true) {
3592 4
            $result = \array_replace_recursive($array, $this->toArray());
3593
        } else {
3594 12
            $result = \array_replace($array, $this->toArray());
3595
        }
3596
3597 16
        return static::create(
3598 16
            $result,
3599 16
            $this->iteratorClass,
3600 16
            false
3601
        );
3602
    }
3603
3604
    /**
3605
     * Merge the current array into the new $array.
3606
     *
3607
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3608
     * - create new indexes
3609
     *
3610
     * @param array $array
3611
     * @param bool  $recursive
3612
     *
3613
     * @return static
3614
     *                <p>(Immutable)</p>
3615
     *
3616
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3617
     * @psalm-return static<TKey,T>
3618
     * @psalm-mutation-free
3619
     */
3620 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...
3621
    {
3622 17
        if ($recursive === true) {
3623 4
            $result = \array_merge_recursive($array, $this->toArray());
3624
        } else {
3625 13
            $result = \array_merge($array, $this->toArray());
3626
        }
3627
3628 17
        return static::create(
3629 17
            $result,
3630 17
            $this->iteratorClass,
3631 17
            false
3632
        );
3633
    }
3634
3635
    /**
3636
     * @return ArrayyMeta|static
3637
     */
3638 15
    public static function meta()
3639
    {
3640 15
        return (new ArrayyMeta())->getMetaObject(static::class);
3641
    }
3642
3643
    /**
3644
     * Get the min value from an array.
3645
     *
3646
     * @return false|mixed
3647
     *                     <p>Will return false if there are no values.</p>
3648
     */
3649 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...
3650
    {
3651 10
        if ($this->count() === 0) {
3652 1
            return false;
3653
        }
3654
3655 9
        $min = false;
3656 9
        foreach ($this->getGenerator() as $value) {
3657
            if (
3658 9
                $min === false
3659
                ||
3660 9
                $value < $min
3661
            ) {
3662 9
                $min = $value;
3663
            }
3664
        }
3665
3666 9
        return $min;
3667
    }
3668
3669
    /**
3670
     * Get the most used value from the array.
3671
     *
3672
     * @return mixed|null
3673
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
3674
     * @psalm-mutation-free
3675
     */
3676 3
    public function mostUsedValue()
3677
    {
3678 3
        return $this->countValues()->arsortImmutable()->firstKey();
3679
    }
3680
3681
    /**
3682
     * Get the most used value from the array.
3683
     *
3684
     * @param int|null $number <p>How many values you will take?</p>
3685
     *
3686
     * @return static
3687
     *                <p>(Immutable)</p>
3688
     *
3689
     * @psalm-return static<TKey,T>
3690
     * @psalm-mutation-free
3691
     */
3692 3
    public function mostUsedValues(int $number = null): self
3693
    {
3694 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
3695
    }
3696
3697
    /**
3698
     * Move an array element to a new index.
3699
     *
3700
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3701
     *
3702
     * @param int|string $from
3703
     * @param int        $to
3704
     *
3705
     * @return static
3706
     *                <p>(Immutable)</p>
3707
     *
3708
     * @psalm-return static<TKey,T>
3709
     * @psalm-mutation-free
3710
     */
3711 1
    public function moveElement($from, $to): self
3712
    {
3713 1
        $array = $this->toArray();
3714
3715 1
        if ((int) $from === $from) {
3716 1
            $tmp = \array_splice($array, $from, 1);
3717 1
            \array_splice($array, (int) $to, 0, $tmp);
3718 1
            $output = $array;
3719 1
        } elseif ((string) $from === $from) {
3720 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3721 1
            $itemToMove = $array[$from];
3722 1
            if ($indexToMove !== false) {
3723 1
                \array_splice($array, $indexToMove, 1);
3724
            }
3725 1
            $i = 0;
3726 1
            $output = [];
3727 1
            foreach ($array as $key => $item) {
3728 1
                if ($i === $to) {
3729 1
                    $output[$from] = $itemToMove;
3730
                }
3731 1
                $output[$key] = $item;
3732 1
                ++$i;
3733
            }
3734
        } else {
3735
            $output = [];
3736
        }
3737
3738 1
        return static::create(
3739 1
            $output,
3740 1
            $this->iteratorClass,
3741 1
            false
3742
        );
3743
    }
3744
3745
    /**
3746
     * Move an array element to the first place.
3747
     *
3748
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3749
     *       loss the keys of an indexed array.
3750
     *
3751
     * @param int|string $key
3752
     *
3753
     * @return static
3754
     *                <p>(Immutable)</p>
3755
     *
3756
     * @psalm-return static<TKey,T>
3757
     * @psalm-mutation-free
3758
     */
3759 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...
3760
    {
3761 1
        $array = $this->toArray();
3762
3763 1
        if ($this->offsetExists($key)) {
3764 1
            $tmpValue = $this->get($key);
3765 1
            unset($array[$key]);
3766 1
            $array = [$key => $tmpValue] + $array;
3767
        }
3768
3769 1
        return static::create(
3770 1
            $array,
3771 1
            $this->iteratorClass,
3772 1
            false
3773
        );
3774
    }
3775
3776
    /**
3777
     * Move an array element to the last place.
3778
     *
3779
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3780
     *       loss the keys of an indexed array.
3781
     *
3782
     * @param int|string $key
3783
     *
3784
     * @return static
3785
     *                <p>(Immutable)</p>
3786
     *
3787
     * @psalm-return static<TKey,T>
3788
     * @psalm-mutation-free
3789
     */
3790 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...
3791
    {
3792 1
        $array = $this->toArray();
3793
3794 1
        if ($this->offsetExists($key)) {
3795 1
            $tmpValue = $this->get($key);
3796 1
            unset($array[$key]);
3797 1
            $array += [$key => $tmpValue];
3798
        }
3799
3800 1
        return static::create(
3801 1
            $array,
3802 1
            $this->iteratorClass,
3803 1
            false
3804
        );
3805
    }
3806
3807
    /**
3808
     * Moves the internal iterator position to the next element and returns this element.
3809
     *
3810
     * @return false|mixed
3811
     *                     <p>(Mutable) Will return false if there are no values.</p>
3812
     */
3813
    public function next()
3814
    {
3815
        return \next($this->array);
3816
    }
3817
3818
    /**
3819
     * Get the next nth keys and values from the array.
3820
     *
3821
     * @param int $step
3822
     * @param int $offset
3823
     *
3824
     * @return static
3825
     *                <p>(Immutable)</p>
3826
     *
3827
     * @psalm-return static<TKey,T>
3828
     * @psalm-mutation-free
3829
     */
3830 1
    public function nth(int $step, int $offset = 0): self
3831
    {
3832
        $arrayFunction = function () use ($step, $offset): \Generator {
3833 1
            $position = 0;
3834 1
            foreach ($this->getGenerator() as $key => $value) {
3835 1
                if ($position++ % $step !== $offset) {
3836 1
                    continue;
3837
                }
3838
3839 1
                yield $key => $value;
3840
            }
3841 1
        };
3842
3843 1
        return static::create(
3844 1
            $arrayFunction,
3845 1
            $this->iteratorClass,
3846 1
            false
3847
        );
3848
    }
3849
3850
    /**
3851
     * Get a subset of the items from the given array.
3852
     *
3853
     * @param mixed[] $keys
3854
     *
3855
     * @return static
3856
     *                <p>(Immutable)</p>
3857
     *
3858
     * @psalm-return static<TKey,T>
3859
     * @psalm-mutation-free
3860
     */
3861 1
    public function only(array $keys): self
3862
    {
3863 1
        $array = $this->toArray();
3864
3865 1
        return static::create(
3866 1
            \array_intersect_key($array, \array_flip($keys)),
3867 1
            $this->iteratorClass,
3868 1
            false
3869
        );
3870
    }
3871
3872
    /**
3873
     * Pad array to the specified size with a given value.
3874
     *
3875
     * @param int   $size  <p>Size of the result array.</p>
3876
     * @param mixed $value <p>Empty value by default.</p>
3877
     *
3878
     * @return static
3879
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3880
     *
3881
     * @psalm-return static<TKey,T>
3882
     * @psalm-mutation-free
3883
     */
3884 5
    public function pad(int $size, $value): self
3885
    {
3886 5
        return static::create(
3887 5
            \array_pad($this->toArray(), $size, $value),
3888 5
            $this->iteratorClass,
3889 5
            false
3890
        );
3891
    }
3892
3893
    /**
3894
     * Partitions this array in two array according to a predicate.
3895
     * Keys are preserved in the resulting array.
3896
     *
3897
     * @param \Closure $closure
3898
     *                          <p>The predicate on which to partition.</p>
3899
     *
3900
     * @return array<int, static>
0 ignored issues
show
Documentation introduced by
The doc-type array<int, could not be parsed: Expected ">" at position 5, but found "end of type". (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...
3901
     *                    <p>An array with two elements. The first element contains the array
3902
     *                    of elements where the predicate returned TRUE, the second element
3903
     *                    contains the array of elements where the predicate returned FALSE.</p>
3904
     *
3905
     * @psalm-return array<int, static<TKey,T>>
3906
     */
3907 1
    public function partition(\Closure $closure): array
3908
    {
3909
        // init
3910 1
        $matches = [];
3911 1
        $noMatches = [];
3912
3913 1
        foreach ($this->array as $key => $value) {
3914 1
            if ($closure($value, $key)) {
3915 1
                $matches[$key] = $value;
3916
            } else {
3917 1
                $noMatches[$key] = $value;
3918
            }
3919
        }
3920
3921 1
        return [self::create($matches), self::create($noMatches)];
3922
    }
3923
3924
    /**
3925
     * Pop a specified value off the end of the current array.
3926
     *
3927
     * @return mixed|null
3928
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
3929
     */
3930 5
    public function pop()
3931
    {
3932 5
        $this->generatorToArray();
3933
3934 5
        return \array_pop($this->array);
3935
    }
3936
3937
    /**
3938
     * Prepend a (key) + value to the current array.
3939
     *
3940
     * @param mixed $value
3941
     * @param mixed $key
3942
     *
3943
     * @return $this
3944
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3945
     *
3946
     * @psalm-return static<TKey,T>
3947
     */
3948 11
    public function prepend($value, $key = null)
3949
    {
3950 11
        $this->generatorToArray();
3951
3952 11
        if ($this->properties !== []) {
3953 3
            $this->checkType($key, $value);
3954
        }
3955
3956 9
        if ($key === null) {
3957 8
            \array_unshift($this->array, $value);
3958
        } else {
3959 2
            $this->array = [$key => $value] + $this->array;
3960
        }
3961
3962 9
        return $this;
3963
    }
3964
3965
    /**
3966
     * Add a suffix to each key.
3967
     *
3968
     * @param mixed $suffix
3969
     *
3970
     * @return static
3971
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3972
     *
3973
     * @psalm-return static<TKey,T>
3974
     * @psalm-mutation-free
3975
     */
3976 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...
3977
    {
3978
        // init
3979 10
        $result = [];
3980
3981 10
        foreach ($this->getGenerator() as $key => $item) {
3982 9
            if ($item instanceof self) {
3983
                $result[$key] = $item->prependToEachKey($suffix);
3984 9
            } elseif (\is_array($item) === true) {
3985
                $result[$key] = self::create(
3986
                    $item,
3987
                    $this->iteratorClass,
3988
                    false
3989
                )->prependToEachKey($suffix)
3990
                    ->toArray();
3991
            } else {
3992 9
                $result[$key . $suffix] = $item;
3993
            }
3994
        }
3995
3996 10
        return self::create(
3997 10
            $result,
3998 10
            $this->iteratorClass,
3999 10
            false
4000
        );
4001
    }
4002
4003
    /**
4004
     * Add a suffix to each value.
4005
     *
4006
     * @param mixed $suffix
4007
     *
4008
     * @return static
4009
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4010
     *
4011
     * @psalm-return static<TKey,T>
4012
     * @psalm-mutation-free
4013
     */
4014 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...
4015
    {
4016
        // init
4017 10
        $result = [];
4018
4019 10
        foreach ($this->getGenerator() as $key => $item) {
4020 9
            if ($item instanceof self) {
4021
                $result[$key] = $item->prependToEachValue($suffix);
4022 9
            } elseif (\is_array($item) === true) {
4023
                $result[$key] = self::create(
4024
                    $item,
4025
                    $this->iteratorClass,
4026
                    false
4027
                )->prependToEachValue($suffix)
4028
                    ->toArray();
4029 9
            } elseif (\is_object($item) === true) {
4030 1
                $result[$key] = $item;
4031
            } else {
4032 8
                $result[$key] = $item . $suffix;
4033
            }
4034
        }
4035
4036 10
        return self::create(
4037 10
            $result,
4038 10
            $this->iteratorClass,
4039 10
            false
4040
        );
4041
    }
4042
4043
    /**
4044
     * Return the value of a given key and
4045
     * delete the key.
4046
     *
4047
     * @param int|int[]|string|string[]|null $keyOrKeys
4048
     * @param mixed                          $fallback
4049
     *
4050
     * @return mixed
4051
     */
4052 1
    public function pull($keyOrKeys = null, $fallback = null)
4053
    {
4054 1
        if ($keyOrKeys === null) {
4055
            $array = $this->toArray();
4056
            $this->clear();
4057
4058
            return $array;
4059
        }
4060
4061 1
        if (\is_array($keyOrKeys) === true) {
4062 1
            $valueOrValues = [];
4063 1
            foreach ($keyOrKeys as $key) {
4064 1
                $valueOrValues[] = $this->get($key, $fallback);
4065 1
                $this->offsetUnset($key);
4066
            }
4067
        } else {
4068 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4069 1
            $this->offsetUnset($keyOrKeys);
4070
        }
4071
4072 1
        return $valueOrValues;
4073
    }
4074
4075
    /**
4076
     * Push one or more values onto the end of array at once.
4077
     *
4078
     * @param array ...$args
4079
     *
4080
     * @return $this
4081
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4082
     *
4083
     * @noinspection ReturnTypeCanBeDeclaredInspection
4084
     *
4085
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4086
     * @psalm-return static<TKey,T>
4087
     */
4088 5
    public function push(...$args)
4089
    {
4090 5
        $this->generatorToArray();
4091
4092
        if (
4093 5
            $this->checkPropertyTypes
4094
            &&
4095 5
            $this->properties !== []
4096
        ) {
4097 1
            foreach ($args as $key => $value) {
4098 1
                $this->checkType($key, $value);
4099
            }
4100
        }
4101
4102 5
        \array_push($this->array, ...$args);
4103
4104 5
        return $this;
4105
    }
4106
4107
    /**
4108
     * Get a random value from the current array.
4109
     *
4110
     * @param int|null $number <p>How many values you will take?</p>
4111
     *
4112
     * @return static
4113
     *                <p>(Immutable)</p>
4114
     *
4115
     * @psalm-return static<int|array-key,T>
4116
     */
4117 19
    public function randomImmutable(int $number = null): self
4118
    {
4119 19
        $this->generatorToArray();
4120
4121 19
        if ($this->count() === 0) {
4122 1
            return static::create(
4123 1
                [],
4124 1
                $this->iteratorClass,
4125 1
                false
4126
            );
4127
        }
4128
4129 18 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4130
            /** @noinspection NonSecureArrayRandUsageInspection */
4131 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4132
4133 13
            return static::create(
4134 13
                $arrayRandValue,
4135 13
                $this->iteratorClass,
4136 13
                false
4137
            );
4138
        }
4139
4140 6
        $arrayTmp = $this->array;
4141
        /** @noinspection NonSecureShuffleUsageInspection */
4142 6
        \shuffle($arrayTmp);
4143
4144 6
        return static::create(
4145 6
            $arrayTmp,
4146 6
            $this->iteratorClass,
4147 6
            false
4148 6
        )->firstsImmutable($number);
4149
    }
4150
4151
    /**
4152
     * Pick a random key/index from the keys of this array.
4153
     *
4154
     * @throws \RangeException If array is empty
4155
     *
4156
     * @return mixed
4157
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4158
     */
4159 4
    public function randomKey()
4160
    {
4161 4
        $result = $this->randomKeys(1);
4162
4163 4
        if (!isset($result[0])) {
4164
            $result[0] = null;
4165
        }
4166
4167 4
        return $result[0];
4168
    }
4169
4170
    /**
4171
     * Pick a given number of random keys/indexes out of this array.
4172
     *
4173
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4174
     *
4175
     * @throws \RangeException If array is empty
4176
     *
4177
     * @return static
4178
     *                <p>(Immutable)</p>
4179
     *
4180
     * @psalm-return static<TKey,T>
4181
     */
4182 13
    public function randomKeys(int $number): self
4183
    {
4184 13
        $this->generatorToArray();
4185
4186 13
        $count = $this->count();
4187
4188
        if (
4189 13
            $number === 0
4190
            ||
4191 13
            $number > $count
4192
        ) {
4193 2
            throw new \RangeException(
4194 2
                \sprintf(
4195 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4196 2
                    $number,
4197 2
                    $count
4198
                )
4199
            );
4200
        }
4201
4202 11
        $result = (array) \array_rand($this->array, $number);
4203
4204 11
        return static::create(
4205 11
            $result,
4206 11
            $this->iteratorClass,
4207 11
            false
4208
        );
4209
    }
4210
4211
    /**
4212
     * Get a random value from the current array.
4213
     *
4214
     * @param int|null $number <p>How many values you will take?</p>
4215
     *
4216
     * @return $this
4217
     *               <p>(Mutable) Return this Arrayy object.</p>
4218
     *
4219
     * @psalm-return static<TKey,T>
4220
     */
4221 17
    public function randomMutable(int $number = null): self
4222
    {
4223 17
        $this->generatorToArray();
4224
4225 17
        if ($this->count() === 0) {
4226
            return static::create(
4227
                [],
4228
                $this->iteratorClass,
4229
                false
4230
            );
4231
        }
4232
4233 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...
4234
            /** @noinspection NonSecureArrayRandUsageInspection */
4235 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4236 7
            $this->array = $arrayRandValue;
4237
4238 7
            return $this;
4239
        }
4240
4241
        /** @noinspection NonSecureShuffleUsageInspection */
4242 11
        \shuffle($this->array);
4243
4244 11
        return $this->firstsMutable($number);
4245
    }
4246
4247
    /**
4248
     * Pick a random value from the values of this array.
4249
     *
4250
     * @return mixed
4251
     *               <p>Get a random value or null if there wasn't a value.</p>
4252
     */
4253 4
    public function randomValue()
4254
    {
4255 4
        $result = $this->randomImmutable();
4256
4257 4
        if (!isset($result[0])) {
4258
            $result[0] = null;
4259
        }
4260
4261 4
        return $result[0];
4262
    }
4263
4264
    /**
4265
     * Pick a given number of random values out of this array.
4266
     *
4267
     * @param int $number
4268
     *
4269
     * @return static
4270
     *                <p>(Mutable)</p>
4271
     *
4272
     * @psalm-return static<TKey,T>
4273
     */
4274 7
    public function randomValues(int $number): self
4275
    {
4276 7
        return $this->randomMutable($number);
4277
    }
4278
4279
    /**
4280
     * Get a random value from an array, with the ability to skew the results.
4281
     *
4282
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4283
     *
4284
     * @param array    $array
4285
     * @param int|null $number <p>How many values you will take?</p>
4286
     *
4287
     * @return static<int,mixed>
0 ignored issues
show
Documentation introduced by
The doc-type static<int,mixed> could not be parsed: Expected "|" or "end of type", but got "<" at position 6. (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...
4288
     *                           <p>(Immutable)</p>
4289
     *
4290
     * @psalm-param  array<mixed,mixed> $array
4291
     * @psalm-return static<int|array-key,T>
4292
     */
4293 9
    public function randomWeighted(array $array, int $number = null): self
4294
    {
4295
        // init
4296 9
        $options = [];
4297
4298 9
        foreach ($array as $option => $weight) {
4299 9
            if ($this->searchIndex($option) !== false) {
4300 2
                for ($i = 0; $i < $weight; ++$i) {
4301 1
                    $options[] = $option;
4302
                }
4303
            }
4304
        }
4305
4306 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4307
    }
4308
4309
    /**
4310
     * Reduce the current array via callable e.g. anonymous-function.
4311
     *
4312
     * @param callable $callable
4313
     * @param mixed    $init
4314
     *
4315
     * @return static
4316
     *                <p>(Immutable)</p>
4317
     *
4318
     * @psalm-return static<TKey,T>
4319
     * @psalm-mutation-free
4320
     */
4321 18
    public function reduce($callable, $init = []): self
4322
    {
4323 18
        if ($this->generator) {
4324 1
            $result = $init;
4325
4326 1
            foreach ($this->getGenerator() as $value) {
4327 1
                $result = $callable($result, $value);
4328
            }
4329
4330 1
            return static::create(
4331 1
                $result,
4332 1
                $this->iteratorClass,
4333 1
                false
4334
            );
4335
        }
4336
4337 18
        $result = \array_reduce($this->array, $callable, $init);
4338
4339 18
        if ($result === null) {
4340
            $this->array = [];
4341
        } else {
4342 18
            $this->array = (array) $result;
4343
        }
4344
4345 18
        return static::create(
4346 18
            $this->array,
4347 18
            $this->iteratorClass,
4348 18
            false
4349
        );
4350
    }
4351
4352
    /**
4353
     * @param bool $unique
4354
     *
4355
     * @return static
4356
     *                <p>(Immutable)</p>
4357
     *
4358
     * @psalm-return static<TKey,T>
4359
     * @psalm-mutation-free
4360
     */
4361 14
    public function reduce_dimension(bool $unique = true): self
4362
    {
4363
        // init
4364 14
        $result = [];
4365
4366 14
        foreach ($this->getGenerator() as $val) {
4367 12
            if (\is_array($val) === true) {
4368 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4369
            } else {
4370 12
                $result[] = [$val];
4371
            }
4372
        }
4373
4374 14
        $result = $result === [] ? [] : \array_merge(...$result);
4375
4376 14
        $resultArrayy = new static($result);
4377
4378
        /**
4379
         * @psalm-suppress ImpureMethodCall - object is already re-created
4380
         * @psalm-suppress InvalidReturnStatement - why?
4381
         */
4382 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4383
    }
4384
4385
    /**
4386
     * Create a numerically re-indexed Arrayy object.
4387
     *
4388
     * @return $this
4389
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4390
     *
4391
     * @psalm-return static<TKey,T>
4392
     */
4393 9
    public function reindex(): self
4394
    {
4395 9
        $this->generatorToArray(false);
4396
4397 9
        $this->array = \array_values($this->array);
4398
4399 9
        return $this;
4400
    }
4401
4402
    /**
4403
     * Return all items that fail the truth test.
4404
     *
4405
     * @param \Closure $closure
4406
     *
4407
     * @return static
4408
     *                <p>(Immutable)</p>
4409
     *
4410
     * @psalm-return static<TKey,T>
4411
     * @psalm-mutation-free
4412
     */
4413 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...
4414
    {
4415
        // init
4416 1
        $filtered = [];
4417
4418 1
        foreach ($this->getGenerator() as $key => $value) {
4419 1
            if (!$closure($value, $key)) {
4420 1
                $filtered[$key] = $value;
4421
            }
4422
        }
4423
4424 1
        return static::create(
4425 1
            $filtered,
4426 1
            $this->iteratorClass,
4427 1
            false
4428
        );
4429
    }
4430
4431
    /**
4432
     * Remove a value from the current array (optional using dot-notation).
4433
     *
4434
     * @param mixed $key
4435
     *
4436
     * @return static
4437
     *                <p>(Mutable)</p>
4438
     *
4439
     * @psalm-param  TKey $key
4440
     * @psalm-return static<TKey,T>
4441
     */
4442 18
    public function remove($key)
4443
    {
4444
        // recursive call
4445 18
        if (\is_array($key) === true) {
4446
            foreach ($key as $k) {
4447
                $this->internalRemove($k);
4448
            }
4449
4450
            return static::create(
4451
                $this->toArray(),
4452
                $this->iteratorClass,
4453
                false
4454
            );
4455
        }
4456
4457 18
        $this->internalRemove($key);
4458
4459 18
        return static::create(
4460 18
            $this->toArray(),
4461 18
            $this->iteratorClass,
4462 18
            false
4463
        );
4464
    }
4465
4466
    /**
4467
     * alias: for "Arrayy->removeValue()"
4468
     *
4469
     * @param mixed $element
4470
     *
4471
     * @return static
4472
     *                <p>(Immutable)</p>
4473
     *
4474
     * @psalm-param  T $element
4475
     * @psalm-return static<TKey,T>
4476
     * @psalm-mutation-free
4477
     */
4478 8
    public function removeElement($element)
4479
    {
4480 8
        return $this->removeValue($element);
4481
    }
4482
4483
    /**
4484
     * Remove the first value from the current array.
4485
     *
4486
     * @return static
4487
     *                <p>(Immutable)</p>
4488
     *
4489
     * @psalm-return static<TKey,T>
4490
     * @psalm-mutation-free
4491
     */
4492 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...
4493
    {
4494 7
        $tmpArray = $this->toArray();
4495
4496 7
        \array_shift($tmpArray);
4497
4498 7
        return static::create(
4499 7
            $tmpArray,
4500 7
            $this->iteratorClass,
4501 7
            false
4502
        );
4503
    }
4504
4505
    /**
4506
     * Remove the last value from the current array.
4507
     *
4508
     * @return static
4509
     *                <p>(Immutable)</p>
4510
     *
4511
     * @psalm-return static<TKey,T>
4512
     * @psalm-mutation-free
4513
     */
4514 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...
4515
    {
4516 7
        $tmpArray = $this->toArray();
4517
4518 7
        \array_pop($tmpArray);
4519
4520 7
        return static::create(
4521 7
            $tmpArray,
4522 7
            $this->iteratorClass,
4523 7
            false
4524
        );
4525
    }
4526
4527
    /**
4528
     * Removes a particular value from an array (numeric or associative).
4529
     *
4530
     * @param mixed $value
4531
     *
4532
     * @return static
4533
     *                <p>(Immutable)</p>
4534
     *
4535
     * @psalm-param  T $value
4536
     * @psalm-return static<TKey,T>
4537
     * @psalm-mutation-free
4538
     */
4539 8
    public function removeValue($value): self
4540
    {
4541 8
        $this->generatorToArray();
4542
4543
        // init
4544 8
        $isSequentialArray = $this->isSequential();
4545
4546 8
        foreach ($this->array as $key => $item) {
4547 7
            if ($item === $value) {
4548 7
                unset($this->array[$key]);
4549
            }
4550
        }
4551
4552 8
        if ($isSequentialArray) {
4553 6
            $this->array = \array_values($this->array);
4554
        }
4555
4556 8
        return static::create(
4557 8
            $this->array,
4558 8
            $this->iteratorClass,
4559 8
            false
4560
        );
4561
    }
4562
4563
    /**
4564
     * Generate array of repeated arrays.
4565
     *
4566
     * @param int $times <p>How many times has to be repeated.</p>
4567
     *
4568
     * @return static
4569
     *                <p>(Immutable)</p>
4570
     *
4571
     * @psalm-return static<TKey,T>
4572
     * @psalm-mutation-free
4573
     */
4574 1
    public function repeat($times): self
4575
    {
4576 1
        if ($times === 0) {
4577 1
            return static::create([], $this->iteratorClass);
4578
        }
4579
4580 1
        return static::create(
4581 1
            \array_fill(0, (int) $times, $this->toArray()),
4582 1
            $this->iteratorClass,
4583 1
            false
4584
        );
4585
    }
4586
4587
    /**
4588
     * Replace a key with a new key/value pair.
4589
     *
4590
     * @param mixed $replace
4591
     * @param mixed $key
4592
     * @param mixed $value
4593
     *
4594
     * @return static
4595
     *                <p>(Immutable)</p>
4596
     *
4597
     * @psalm-return static<TKey,T>
4598
     * @psalm-mutation-free
4599
     */
4600 2
    public function replace($replace, $key, $value): self
4601
    {
4602 2
        $that = clone $this;
4603
4604
        /**
4605
         * @psalm-suppress ImpureMethodCall - object is already cloned
4606
         */
4607 2
        return $that->remove($replace)
4608 2
            ->set($key, $value);
4609
    }
4610
4611
    /**
4612
     * Create an array using the current array as values and the other array as keys.
4613
     *
4614
     * @param array $keys <p>An array of keys.</p>
4615
     *
4616
     * @return static
4617
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4618
     *
4619
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4620
     * @psalm-return static<TKey,T>
4621
     * @psalm-mutation-free
4622
     */
4623 2
    public function replaceAllKeys(array $keys): self
4624
    {
4625 2
        return static::create(
4626 2
            \array_combine($keys, $this->toArray()),
4627 2
            $this->iteratorClass,
4628 2
            false
4629
        );
4630
    }
4631
4632
    /**
4633
     * Create an array using the current array as keys and the other array as values.
4634
     *
4635
     * @param array $array <p>An array o values.</p>
4636
     *
4637
     * @return static
4638
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4639
     *
4640
     * @psalm-param  array<mixed,T> $array
4641
     * @psalm-return static<TKey,T>
4642
     * @psalm-mutation-free
4643
     */
4644 2
    public function replaceAllValues(array $array): self
4645
    {
4646 2
        return static::create(
4647 2
            \array_combine($this->array, $array),
4648 2
            $this->iteratorClass,
4649 2
            false
4650
        );
4651
    }
4652
4653
    /**
4654
     * Replace the keys in an array with another set.
4655
     *
4656
     * @param array $keys <p>An array of keys matching the array's size</p>
4657
     *
4658
     * @return static
4659
     *                <p>(Immutable)</p>
4660
     *
4661
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4662
     * @psalm-return static<TKey,T>
4663
     * @psalm-mutation-free
4664
     */
4665 1
    public function replaceKeys(array $keys): self
4666
    {
4667 1
        $values = \array_values($this->toArray());
4668 1
        $result = \array_combine($keys, $values);
4669
4670 1
        return static::create(
4671 1
            $result,
4672 1
            $this->iteratorClass,
4673 1
            false
4674
        );
4675
    }
4676
4677
    /**
4678
     * Replace the first matched value in an array.
4679
     *
4680
     * @param mixed $search      <p>The value to replace.</p>
4681
     * @param mixed $replacement <p>The value to replace.</p>
4682
     *
4683
     * @return static
4684
     *                <p>(Immutable)</p>
4685
     *
4686
     * @psalm-return static<TKey,T>
4687
     * @psalm-mutation-free
4688
     */
4689 3
    public function replaceOneValue($search, $replacement = ''): self
4690
    {
4691 3
        $array = $this->toArray();
4692 3
        $key = \array_search($search, $array, true);
4693
4694 3
        if ($key !== false) {
4695 3
            $array[$key] = $replacement;
4696
        }
4697
4698 3
        return static::create(
4699 3
            $array,
4700 3
            $this->iteratorClass,
4701 3
            false
4702
        );
4703
    }
4704
4705
    /**
4706
     * Replace values in the current array.
4707
     *
4708
     * @param mixed $search      <p>The value to replace.</p>
4709
     * @param mixed $replacement <p>What to replace it with.</p>
4710
     *
4711
     * @return static
4712
     *                <p>(Immutable)</p>
4713
     *
4714
     * @psalm-return static<TKey,T>
4715
     * @psalm-mutation-free
4716
     */
4717 1
    public function replaceValues($search, $replacement = ''): self
4718
    {
4719
        /**
4720
         * @psalm-suppress MissingClosureReturnType
4721
         * @psalm-suppress MissingClosureParamType
4722
         */
4723 1
        return $this->each(
4724
            static function ($value) use ($search, $replacement) {
4725 1
                return \str_replace($search, $replacement, $value);
4726 1
            }
4727
        );
4728
    }
4729
4730
    /**
4731
     * Get the last elements from index $from until the end of this array.
4732
     *
4733
     * @param int $from
4734
     *
4735
     * @return static
4736
     *                <p>(Immutable)</p>
4737
     *
4738
     * @psalm-return static<TKey,T>
4739
     * @psalm-mutation-free
4740
     */
4741 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...
4742
    {
4743 15
        $tmpArray = $this->toArray();
4744
4745 15
        return static::create(
4746 15
            \array_splice($tmpArray, $from),
4747 15
            $this->iteratorClass,
4748 15
            false
4749
        );
4750
    }
4751
4752
    /**
4753
     * Return the array in the reverse order.
4754
     *
4755
     * @return $this
4756
     *               <p>(Mutable) Return this Arrayy object.</p>
4757
     *
4758
     * @psalm-return static<TKey,T>
4759
     */
4760 9
    public function reverse(): self
4761
    {
4762 9
        $this->generatorToArray();
4763
4764 9
        $this->array = \array_reverse($this->array);
4765
4766 9
        return $this;
4767
    }
4768
4769
    /**
4770
     * Sort an array in reverse order.
4771
     *
4772
     * @param int $sort_flags [optional] <p>
4773
     *                        You may modify the behavior of the sort using the optional
4774
     *                        parameter sort_flags, for details
4775
     *                        see sort.
4776
     *                        </p>
4777
     *
4778
     * @return $this
4779
     *               <p>(Mutable) Return this Arrayy object.</p>
4780
     *
4781
     * @psalm-return static<TKey,T>
4782
     */
4783 4
    public function rsort(int $sort_flags = 0): self
4784
    {
4785 4
        $this->generatorToArray();
4786
4787 4
        \rsort($this->array, $sort_flags);
4788
4789 4
        return $this;
4790
    }
4791
4792
    /**
4793
     * Sort an array in reverse order.
4794
     *
4795
     * @param int $sort_flags [optional] <p>
4796
     *                        You may modify the behavior of the sort using the optional
4797
     *                        parameter sort_flags, for details
4798
     *                        see sort.
4799
     *                        </p>
4800
     *
4801
     * @return $this
4802
     *               <p>(Immutable) Return this Arrayy object.</p>
4803
     *
4804
     * @psalm-return static<TKey,T>
4805
     * @psalm-mutation-free
4806
     */
4807 4
    public function rsortImmutable(int $sort_flags = 0): self
4808
    {
4809 4
        $that = clone $this;
4810
4811
        /**
4812
         * @psalm-suppress ImpureMethodCall - object is already cloned
4813
         */
4814 4
        $that->rsort($sort_flags);
4815
4816 4
        return $that;
4817
    }
4818
4819
    /**
4820
     * Search for the first index of the current array via $value.
4821
     *
4822
     * @param mixed $value
4823
     *
4824
     * @return false|float|int|string
4825
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4826
     * @psalm-mutation-free
4827
     */
4828 21
    public function searchIndex($value)
4829
    {
4830 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
4831 20
            if ($value === $valueFromArray) {
4832 10
                return $keyFromArray;
4833
            }
4834
        }
4835
4836 11
        return false;
4837
    }
4838
4839
    /**
4840
     * Search for the value of the current array via $index.
4841
     *
4842
     * @param mixed $index
4843
     *
4844
     * @return static
4845
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
4846
     *
4847
     * @psalm-return static<TKey,T>
4848
     * @psalm-mutation-free
4849
     */
4850 9
    public function searchValue($index): self
4851
    {
4852 9
        $this->generatorToArray();
4853
4854
        // init
4855 9
        $return = [];
4856
4857 9
        if ($this->array === []) {
4858
            return static::create(
4859
                [],
4860
                $this->iteratorClass,
4861
                false
4862
            );
4863
        }
4864
4865
        // php cast "bool"-index into "int"-index
4866 9
        if ((bool) $index === $index) {
4867 1
            $index = (int) $index;
4868
        }
4869
4870 9
        if ($this->offsetExists($index)) {
4871 7
            $return = [$this->array[$index]];
4872
        }
4873
4874 9
        return static::create(
4875 9
            $return,
4876 9
            $this->iteratorClass,
4877 9
            false
4878
        );
4879
    }
4880
4881
    /**
4882
     * Set a value for the current array (optional using dot-notation).
4883
     *
4884
     * @param string $key   <p>The key to set.</p>
4885
     * @param mixed  $value <p>Its value.</p>
4886
     *
4887
     * @return $this
4888
     *               <p>(Mutable) Return this Arrayy object.</p>
4889
     *
4890
     * @psalm-param  TKey $key
4891
     * @psalm-param  T $value
4892
     * @psalm-return static<TKey,T>
4893
     */
4894 18
    public function set($key, $value): self
4895
    {
4896 18
        $this->internalSet($key, $value);
4897
4898 18
        return $this;
4899
    }
4900
4901
    /**
4902
     * Get a value from a array and set it if it was not.
4903
     *
4904
     * WARNING: this method only set the value, if the $key is not already set
4905
     *
4906
     * @param mixed $key      <p>The key</p>
4907
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
4908
     *
4909
     * @return mixed
4910
     *               <p>(Mutable)</p>
4911
     */
4912 11
    public function setAndGet($key, $fallback = null)
4913
    {
4914 11
        $this->generatorToArray();
4915
4916
        // If the key doesn't exist, set it.
4917 11
        if (!$this->has($key)) {
4918 4
            $this->array = $this->set($key, $fallback)->toArray();
4919
        }
4920
4921 11
        return $this->get($key);
4922
    }
4923
4924
    /**
4925
     * Shifts a specified value off the beginning of array.
4926
     *
4927
     * @return mixed
4928
     *               <p>(Mutable) A shifted element from the current array.</p>
4929
     */
4930 5
    public function shift()
4931
    {
4932 5
        $this->generatorToArray();
4933
4934 5
        return \array_shift($this->array);
4935
    }
4936
4937
    /**
4938
     * Shuffle the current array.
4939
     *
4940
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
4941
     * @param array $array  [optional]
4942
     *
4943
     * @return static
4944
     *                <p>(Immutable)</p>
4945
     *
4946
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4947
     * @psalm-return static<TKey,T>
4948
     *
4949
     * @noinspection BadExceptionsProcessingInspection
4950
     * @noinspection RandomApiMigrationInspection
4951
     * @noinspection NonSecureShuffleUsageInspection
4952
     */
4953 2
    public function shuffle(bool $secure = false, array $array = null): self
4954
    {
4955 2
        if ($array === null) {
4956 2
            $array = $this->toArray(false);
4957
        }
4958
4959 2
        if ($secure !== true) {
4960 2
            \shuffle($array);
4961
        } else {
4962 1
            $size = \count($array, \COUNT_NORMAL);
4963 1
            $keys = \array_keys($array);
4964 1
            for ($i = $size - 1; $i > 0; --$i) {
4965
                try {
4966 1
                    $r = \random_int(0, $i);
4967
                } catch (\Exception $e) {
4968
                    $r = \mt_rand(0, $i);
4969
                }
4970 1
                if ($r !== $i) {
4971
                    $temp = $array[$keys[$r]];
4972
                    $array[$keys[$r]] = $array[$keys[$i]];
4973
                    $array[$keys[$i]] = $temp;
4974
                }
4975
            }
4976
        }
4977
4978 2
        foreach ($array as $key => $value) {
4979
            // check if recursive is needed
4980 2
            if (\is_array($value) === true) {
4981
                $array[$key] = $this->shuffle($secure, $value);
4982
            }
4983
        }
4984
4985 2
        return static::create(
4986 2
            $array,
4987 2
            $this->iteratorClass,
4988 2
            false
4989
        );
4990
    }
4991
4992
    /**
4993
     * Count the values from the current array.
4994
     *
4995
     * alias: for "Arrayy->count()"
4996
     *
4997
     * @param int $mode
4998
     *
4999
     * @return int
5000
     */
5001 20
    public function size(int $mode = \COUNT_NORMAL): int
5002
    {
5003 20
        return $this->count($mode);
5004
    }
5005
5006
    /**
5007
     * Checks whether array has exactly $size items.
5008
     *
5009
     * @param int $size
5010
     *
5011
     * @return bool
5012
     */
5013 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...
5014
    {
5015
        // init
5016 1
        $itemsTempCount = 0;
5017
5018 1
        foreach ($this->getGenerator() as $key => $value) {
5019 1
            ++$itemsTempCount;
5020 1
            if ($itemsTempCount > $size) {
5021 1
                return false;
5022
            }
5023
        }
5024
5025 1
        return $itemsTempCount === $size;
5026
    }
5027
5028
    /**
5029
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5030
     * smaller than $fromSize.
5031
     *
5032
     * @param int $fromSize
5033
     * @param int $toSize
5034
     *
5035
     * @return bool
5036
     */
5037 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5038
    {
5039 1
        if ($fromSize > $toSize) {
5040 1
            $tmp = $toSize;
5041 1
            $toSize = $fromSize;
5042 1
            $fromSize = $tmp;
5043
        }
5044
5045
        // init
5046 1
        $itemsTempCount = 0;
5047
5048 1
        foreach ($this->getGenerator() as $key => $value) {
5049 1
            ++$itemsTempCount;
5050 1
            if ($itemsTempCount > $toSize) {
5051 1
                return false;
5052
            }
5053
        }
5054
5055 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5056
    }
5057
5058
    /**
5059
     * Checks whether array has more than $size items.
5060
     *
5061
     * @param int $size
5062
     *
5063
     * @return bool
5064
     */
5065 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...
5066
    {
5067
        // init
5068 1
        $itemsTempCount = 0;
5069
5070 1
        foreach ($this->getGenerator() as $key => $value) {
5071 1
            ++$itemsTempCount;
5072 1
            if ($itemsTempCount > $size) {
5073 1
                return true;
5074
            }
5075
        }
5076
5077 1
        return $itemsTempCount > $size;
5078
    }
5079
5080
    /**
5081
     * Checks whether array has less than $size items.
5082
     *
5083
     * @param int $size
5084
     *
5085
     * @return bool
5086
     */
5087 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...
5088
    {
5089
        // init
5090 1
        $itemsTempCount = 0;
5091
5092 1
        foreach ($this->getGenerator() as $key => $value) {
5093 1
            ++$itemsTempCount;
5094 1
            if ($itemsTempCount > $size) {
5095 1
                return false;
5096
            }
5097
        }
5098
5099 1
        return $itemsTempCount < $size;
5100
    }
5101
5102
    /**
5103
     * Counts all elements in an array, or something in an object.
5104
     *
5105
     * <p>
5106
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5107
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5108
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5109
     * implemented and used in PHP.
5110
     * </p>
5111
     *
5112
     * @return int
5113
     *             <p>
5114
     *             The number of elements in var, which is
5115
     *             typically an array, since anything else will have one
5116
     *             element.
5117
     *             </p>
5118
     *             <p>
5119
     *             If var is not an array or an object with
5120
     *             implemented Countable interface,
5121
     *             1 will be returned.
5122
     *             There is one exception, if var is &null;,
5123
     *             0 will be returned.
5124
     *             </p>
5125
     *             <p>
5126
     *             Caution: count may return 0 for a variable that isn't set,
5127
     *             but it may also return 0 for a variable that has been initialized with an
5128
     *             empty array. Use isset to test if a variable is set.
5129
     *             </p>
5130
     */
5131 10
    public function sizeRecursive(): int
5132
    {
5133 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5134
    }
5135
5136
    /**
5137
     * Extract a slice of the array.
5138
     *
5139
     * @param int      $offset       <p>Slice begin index.</p>
5140
     * @param int|null $length       <p>Length of the slice.</p>
5141
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5142
     *
5143
     * @return static
5144
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5145
     *
5146
     * @psalm-return static<TKey,T>
5147
     * @psalm-mutation-free
5148
     */
5149 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5150
    {
5151 5
        return static::create(
5152 5
            \array_slice(
5153 5
                $this->toArray(),
5154 5
                $offset,
5155 5
                $length,
5156 5
                $preserveKeys
5157
            ),
5158 5
            $this->iteratorClass,
5159 5
            false
5160
        );
5161
    }
5162
5163
    /**
5164
     * Sort the current array and optional you can keep the keys.
5165
     *
5166
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5167
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5168
     *                              <strong>SORT_NATURAL</strong></p>
5169
     * @param bool       $keepKeys
5170
     *
5171
     * @return static
5172
     *                <p>(Mutable) Return this Arrayy object.</p>
5173
     *
5174
     * @psalm-return static<TKey,T>
5175
     */
5176 20
    public function sort(
5177
        $direction = \SORT_ASC,
5178
        int $strategy = \SORT_REGULAR,
5179
        bool $keepKeys = false
5180
    ): self {
5181 20
        $this->generatorToArray();
5182
5183 20
        return $this->sorting(
5184 20
            $this->array,
5185 20
            $direction,
5186 20
            $strategy,
5187 20
            $keepKeys
5188
        );
5189
    }
5190
5191
    /**
5192
     * Sort the current array and optional you can keep the keys.
5193
     *
5194
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5195
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5196
     *                              <strong>SORT_NATURAL</strong></p>
5197
     * @param bool       $keepKeys
5198
     *
5199
     * @return static
5200
     *                <p>(Immutable) Return this Arrayy object.</p>
5201
     *
5202
     * @psalm-return static<TKey,T>
5203
     */
5204 12
    public function sortImmutable(
5205
        $direction = \SORT_ASC,
5206
        int $strategy = \SORT_REGULAR,
5207
        bool $keepKeys = false
5208
    ): self {
5209 12
        $that = clone $this;
5210
5211 12
        $that->generatorToArray();
5212
5213 12
        return $that->sorting(
5214 12
            $that->array,
5215 12
            $direction,
5216 12
            $strategy,
5217 12
            $keepKeys
5218
        );
5219
    }
5220
5221
    /**
5222
     * Sort the current array by key.
5223
     *
5224
     * @see          http://php.net/manual/en/function.ksort.php
5225
     * @see          http://php.net/manual/en/function.krsort.php
5226
     *
5227
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5228
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5229
     *                              <strong>SORT_NATURAL</strong></p>
5230
     *
5231
     * @return $this
5232
     *               <p>(Mutable) Return this Arrayy object.</p>
5233
     *
5234
     * @psalm-return static<TKey,T>
5235
     */
5236 18
    public function sortKeys(
5237
        $direction = \SORT_ASC,
5238
        int $strategy = \SORT_REGULAR
5239
    ): self {
5240 18
        $this->generatorToArray();
5241
5242 18
        $this->sorterKeys($this->array, $direction, $strategy);
5243
5244 18
        return $this;
5245
    }
5246
5247
    /**
5248
     * Sort the current array by key.
5249
     *
5250
     * @see          http://php.net/manual/en/function.ksort.php
5251
     * @see          http://php.net/manual/en/function.krsort.php
5252
     *
5253
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5254
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5255
     *                              <strong>SORT_NATURAL</strong></p>
5256
     *
5257
     * @return $this
5258
     *               <p>(Immutable) Return this Arrayy object.</p>
5259
     *
5260
     * @psalm-return static<TKey,T>
5261
     * @psalm-mutation-free
5262
     */
5263 8
    public function sortKeysImmutable(
5264
        $direction = \SORT_ASC,
5265
        int $strategy = \SORT_REGULAR
5266
    ): self {
5267 8
        $that = clone $this;
5268
5269
        /**
5270
         * @psalm-suppress ImpureMethodCall - object is already cloned
5271
         */
5272 8
        $that->sortKeys($direction, $strategy);
5273
5274 8
        return $that;
5275
    }
5276
5277
    /**
5278
     * Sort the current array by value.
5279
     *
5280
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5281
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5282
     *                              <strong>SORT_NATURAL</strong></p>
5283
     *
5284
     * @return static
5285
     *                <p>(Mutable)</p>
5286
     *
5287
     * @psalm-return static<TKey,T>
5288
     */
5289 1
    public function sortValueKeepIndex(
5290
        $direction = \SORT_ASC,
5291
        int $strategy = \SORT_REGULAR
5292
    ): self {
5293 1
        return $this->sort($direction, $strategy, true);
5294
    }
5295
5296
    /**
5297
     * Sort the current array by value.
5298
     *
5299
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5300
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5301
     *                              <strong>SORT_NATURAL</strong></p>
5302
     *
5303
     * @return static
5304
     *                <p>(Mutable)</p>
5305
     *
5306
     * @psalm-return static<TKey,T>
5307
     */
5308 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5309
    {
5310 1
        return $this->sort($direction, $strategy, false);
5311
    }
5312
5313
    /**
5314
     * Sort a array by value, by a closure or by a property.
5315
     *
5316
     * - If the sorter is null, the array is sorted naturally.
5317
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5318
     *
5319
     * @param callable|string|null $sorter
5320
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5321
     *                                        <strong>SORT_DESC</strong></p>
5322
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5323
     *                                        <strong>SORT_NATURAL</strong></p>
5324
     *
5325
     * @return static
5326
     *                <p>(Immutable)</p>
5327
     *
5328
     * @psalm-return static<TKey,T>
5329
     * @psalm-mutation-free
5330
     */
5331 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5332
    {
5333 1
        $array = $this->toArray();
5334 1
        $direction = $this->getDirection($direction);
5335
5336
        // Transform all values into their results.
5337 1
        if ($sorter) {
5338 1
            $arrayy = static::create(
5339 1
                $array,
5340 1
                $this->iteratorClass,
5341 1
                false
5342
            );
5343
5344
            /**
5345
             * @psalm-suppress MissingClosureReturnType
5346
             * @psalm-suppress MissingClosureParamType
5347
             */
5348 1
            $results = $arrayy->each(
5349
                function ($value) use ($sorter) {
5350 1
                    if (\is_callable($sorter) === true) {
5351 1
                        return $sorter($value);
5352
                    }
5353
5354 1
                    return $this->get($sorter);
5355 1
                }
5356
            );
5357
5358 1
            $results = $results->toArray();
5359
        } else {
5360 1
            $results = $array;
5361
        }
5362
5363
        // Sort by the results and replace by original values
5364 1
        \array_multisort($results, $direction, $strategy, $array);
5365
5366 1
        return static::create(
5367 1
            $array,
5368 1
            $this->iteratorClass,
5369 1
            false
5370
        );
5371
    }
5372
5373
    /**
5374
     * @param int      $offset
5375
     * @param int|null $length
5376
     * @param array    $replacement
5377
     *
5378
     * @return static
5379
     *                <p>(Immutable)</p>
5380
     *
5381
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5382
     * @psalm-return static<TKey,T>
5383
     * @psalm-mutation-free
5384
     */
5385 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5386
    {
5387 1
        $tmpArray = $this->toArray();
5388
5389 1
        \array_splice(
5390 1
            $tmpArray,
5391 1
            $offset,
5392 1
            $length ?? $this->count(),
5393 1
            $replacement
5394
        );
5395
5396 1
        return static::create(
5397 1
            $tmpArray,
5398 1
            $this->iteratorClass,
5399 1
            false
5400
        );
5401
    }
5402
5403
    /**
5404
     * Split an array in the given amount of pieces.
5405
     *
5406
     * @param int  $numberOfPieces
5407
     * @param bool $keepKeys
5408
     *
5409
     * @return static
5410
     *                <p>(Immutable)</p>
5411
     *
5412
     * @psalm-return static<TKey,T>
5413
     * @psalm-mutation-free
5414
     */
5415 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5416
    {
5417 1
        $this->generatorToArray();
5418
5419 1
        $count = $this->count();
5420
5421 1
        if ($count === 0) {
5422 1
            $result = [];
5423
        } else {
5424 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5425 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5426
        }
5427
5428 1
        return static::create(
5429 1
            $result,
5430 1
            $this->iteratorClass,
5431 1
            false
5432
        );
5433
    }
5434
5435
    /**
5436
     * Stripe all empty items.
5437
     *
5438
     * @return static
5439
     *                <p>(Immutable)</p>
5440
     *
5441
     * @psalm-return static<TKey,T>
5442
     * @psalm-mutation-free
5443
     */
5444 1
    public function stripEmpty(): self
5445
    {
5446 1
        return $this->filter(
5447
            static function ($item) {
5448 1
                if ($item === null) {
5449 1
                    return false;
5450
                }
5451
5452 1
                return (bool) \trim((string) $item);
5453 1
            }
5454
        );
5455
    }
5456
5457
    /**
5458
     * Swap two values between positions by key.
5459
     *
5460
     * @param int|string $swapA <p>a key in the array</p>
5461
     * @param int|string $swapB <p>a key in the array</p>
5462
     *
5463
     * @return static
5464
     *                <p>(Immutable)</p>
5465
     *
5466
     * @psalm-return static<TKey,T>
5467
     * @psalm-mutation-free
5468
     */
5469 1
    public function swap($swapA, $swapB): self
5470
    {
5471 1
        $array = $this->toArray();
5472
5473 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5474
5475 1
        return static::create(
5476 1
            $array,
5477 1
            $this->iteratorClass,
5478 1
            false
5479
        );
5480
    }
5481
5482
    /**
5483
     * Get the current array from the "Arrayy"-object.
5484
     * alias for "getArray()"
5485
     *
5486
     * @param bool $convertAllArrayyElements <p>
5487
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5488
     *                                       </p>
5489
     * @param bool $preserveKeys             <p>
5490
     *                                       e.g.: A generator maybe return the same key more then once,
5491
     *                                       so maybe you will ignore the keys.
5492
     *                                       </p>
5493
     *
5494
     * @return array
5495
     *
5496
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5497
     * @psalm-mutation-free
5498
     */
5499 893
    public function toArray(
5500
        bool $convertAllArrayyElements = false,
5501
        bool $preserveKeys = true
5502
    ): array {
5503
        // init
5504 893
        $array = [];
5505
5506 893
        if ($convertAllArrayyElements) {
5507 2
            foreach ($this->getGenerator() as $key => $value) {
5508 2
                if ($value instanceof self) {
5509 1
                    $value = $value->toArray(true);
5510
                }
5511
5512 2
                if ($preserveKeys) {
5513 1
                    $array[$key] = $value;
5514
                } else {
5515 1
                    $array[] = $value;
5516
                }
5517
            }
5518
        } else {
5519 893
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5520
        }
5521
5522 893
        return $array;
5523
    }
5524
5525
    /**
5526
     * Get the current array from the "Arrayy"-object as list.
5527
     *
5528
     * @param bool $convertAllArrayyElements <p>
5529
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5530
     *                                       </p>
5531
     *
5532
     * @return array
5533
     *
5534
     * @psalm-return array<int,mixed>|array<int,T>
5535
     * @psalm-mutation-free
5536
     */
5537 1
    public function toList(bool $convertAllArrayyElements = false): array
5538
    {
5539 1
        return $this->toArray(
5540 1
            $convertAllArrayyElements,
5541 1
            false
5542
        );
5543
    }
5544
5545
    /**
5546
     * Convert the current array to JSON.
5547
     *
5548
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
5549
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
5550
     *
5551
     * @return string
5552
     */
5553 7
    public function toJson(int $options = 0, int $depth = 512): string
5554
    {
5555 7
        $return = \json_encode($this->toArray(), $options, $depth);
5556 7
        if ($return === false) {
5557
            return '';
5558
        }
5559
5560 7
        return $return;
5561
    }
5562
5563
    /**
5564
     * @param string[]|null $items
5565
     * @param string[]      $helper
5566
     *
5567
     * @return static
5568
     *
5569
     * @psalm-return static<TKey,T>
5570
     */
5571 1
    public function toPermutation(array $items = null, array $helper = []): self
5572
    {
5573
        // init
5574 1
        $return = [];
5575
5576 1
        if ($items === null) {
5577 1
            $items = $this->toArray();
5578
        }
5579
5580 1
        if (empty($items)) {
5581 1
            $return[] = $helper;
5582
        } else {
5583 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5584 1
                $new_items = $items;
5585 1
                $new_helper = $helper;
5586 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5587
                /** @noinspection PhpSillyAssignmentInspection */
5588
                /** @var string[] $new_items */
5589 1
                $new_items = $new_items;
0 ignored issues
show
Bug introduced by
Why assign $new_items to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
5590 1
                \array_unshift($new_helper, $tmp_helper);
5591
                /** @noinspection SlowArrayOperationsInLoopInspection */
5592 1
                $return = \array_merge(
5593 1
                    $return,
5594 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5595
                );
5596
            }
5597
        }
5598
5599 1
        return static::create(
5600 1
            $return,
5601 1
            $this->iteratorClass,
5602 1
            false
5603
        );
5604
    }
5605
5606
    /**
5607
     * Implodes array to a string with specified separator.
5608
     *
5609
     * @param string $separator [optional] <p>The element's separator.</p>
5610
     *
5611
     * @return string
5612
     *                <p>The string representation of array, separated by ",".</p>
5613
     */
5614 19
    public function toString(string $separator = ','): string
5615
    {
5616 19
        return $this->implode($separator);
5617
    }
5618
5619
    /**
5620
     * Return a duplicate free copy of the current array.
5621
     *
5622
     * @return $this
5623
     *               <p>(Mutable)</p>
5624
     *
5625
     * @psalm-return static<TKey,T>
5626
     */
5627 13
    public function unique(): self
5628
    {
5629
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5630
5631
        /**
5632
         * @psalm-suppress MissingClosureReturnType
5633
         * @psalm-suppress MissingClosureParamType
5634
         */
5635 13
        $this->array = $this->reduce(
5636
            static function ($resultArray, $value) {
5637 12
                if (!\in_array($value, $resultArray, true)) {
5638 12
                    $resultArray[] = $value;
5639
                }
5640
5641 12
                return $resultArray;
5642 13
            },
5643 13
            []
5644 13
        )->toArray();
5645 13
        $this->generator = null;
5646
5647 13
        return $this;
5648
    }
5649
5650
    /**
5651
     * Return a duplicate free copy of the current array. (with the old keys)
5652
     *
5653
     * @return $this
5654
     *               <p>(Mutable)</p>
5655
     *
5656
     * @psalm-return static<TKey,T>
5657
     */
5658 11
    public function uniqueKeepIndex(): self
5659
    {
5660
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5661
5662
        // init
5663 11
        $array = $this->toArray();
5664
5665
        /**
5666
         * @psalm-suppress MissingClosureReturnType
5667
         * @psalm-suppress MissingClosureParamType
5668
         */
5669 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...
5670 11
            \array_keys($array),
5671
            static function ($resultArray, $key) use ($array) {
5672 10
                if (!\in_array($array[$key], $resultArray, true)) {
5673 10
                    $resultArray[$key] = $array[$key];
5674
                }
5675
5676 10
                return $resultArray;
5677 11
            },
5678 11
            []
5679
        );
5680 11
        $this->generator = null;
5681
5682 11
        return $this;
5683
    }
5684
5685
    /**
5686
     * alias: for "Arrayy->unique()"
5687
     *
5688
     * @return static
5689
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
5690
     *
5691
     * @see          Arrayy::unique()
5692
     *
5693
     * @psalm-return static<TKey,T>
5694
     */
5695 10
    public function uniqueNewIndex(): self
5696
    {
5697 10
        return $this->unique();
5698
    }
5699
5700
    /**
5701
     * Prepends one or more values to the beginning of array at once.
5702
     *
5703
     * @param array ...$args
5704
     *
5705
     * @return $this
5706
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
5707
     *
5708
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
5709
     * @psalm-return static<TKey,T>
5710
     */
5711 4
    public function unshift(...$args): self
5712
    {
5713 4
        $this->generatorToArray();
5714
5715 4
        \array_unshift($this->array, ...$args);
5716
5717 4
        return $this;
5718
    }
5719
5720
    /**
5721
     * Tests whether the given closure return something valid for all elements of this array.
5722
     *
5723
     * @param \Closure $closure the predicate
5724
     *
5725
     * @return bool
5726
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
5727
     */
5728 1 View Code Duplication
    public function validate(\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...
5729
    {
5730 1
        foreach ($this->getGenerator() as $key => $value) {
5731 1
            if (!$closure($value, $key)) {
5732 1
                return false;
5733
            }
5734
        }
5735
5736 1
        return true;
5737
    }
5738
5739
    /**
5740
     * Get all values from a array.
5741
     *
5742
     * @return static
5743
     *                <p>(Immutable)</p>
5744
     *
5745
     * @psalm-return static<TKey,T>
5746
     * @psalm-mutation-free
5747
     */
5748 2
    public function values(): self
5749
    {
5750 2
        return static::create(
5751
            function () {
5752
                /** @noinspection YieldFromCanBeUsedInspection */
5753 2
                foreach ($this->getGenerator() as $value) {
5754 2
                    yield $value;
5755
                }
5756 2
            },
5757 2
            $this->iteratorClass,
5758 2
            false
5759
        );
5760
    }
5761
5762
    /**
5763
     * Apply the given function to every element in the array, discarding the results.
5764
     *
5765
     * @param callable $callable
5766
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
5767
     *
5768
     * @return $this
5769
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
5770
     *
5771
     * @psalm-return static<TKey,T>
5772
     */
5773 12
    public function walk($callable, bool $recursive = false): self
5774
    {
5775 12
        $this->generatorToArray();
5776
5777 12
        if ($recursive === true) {
5778 6
            \array_walk_recursive($this->array, $callable);
5779
        } else {
5780 6
            \array_walk($this->array, $callable);
5781
        }
5782
5783 12
        return $this;
5784
    }
5785
5786
    /**
5787
     * Returns a collection of matching items.
5788
     *
5789
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
5790
     * @param mixed  $value                 the value to match
5791
     *
5792
     * @throws \InvalidArgumentException if property or method is not defined
5793
     *
5794
     * @return static
5795
     *
5796
     * @psalm-param  T $value
5797
     * @psalm-return static<TKey,T>
5798
     */
5799 1
    public function where(string $keyOrPropertyOrMethod, $value): self
5800
    {
5801 1
        return $this->filter(
5802
            function ($item) use ($keyOrPropertyOrMethod, $value) {
5803 1
                $accessorValue = $this->extractValue(
5804 1
                    $item,
5805 1
                    $keyOrPropertyOrMethod
5806
                );
5807
5808 1
                return $accessorValue === $value;
5809 1
            }
5810
        );
5811
    }
5812
5813
    /**
5814
     * Convert an array into a object.
5815
     *
5816
     * @param array $array
5817
     *
5818
     * @return \stdClass
5819
     *
5820
     * @psalm-param array<mixed,mixed> $array
5821
     */
5822 4
    final protected static function arrayToObject(array $array = []): \stdClass
5823
    {
5824
        // init
5825 4
        $object = new \stdClass();
5826
5827 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
5828 1
            return $object;
5829
        }
5830
5831 3
        foreach ($array as $name => $value) {
5832 3
            if (\is_array($value) === true) {
5833 1
                $object->{$name} = static::arrayToObject($value);
5834
            } else {
5835 3
                $object->{$name} = $value;
5836
            }
5837
        }
5838
5839 3
        return $object;
5840
    }
5841
5842
    /**
5843
     * @param array|\Generator|null $input         <p>
5844
     *                                             An array containing keys to return.
5845
     *                                             </p>
5846
     * @param mixed|null            $search_values [optional] <p>
5847
     *                                             If specified, then only keys containing these values are returned.
5848
     *                                             </p>
5849
     * @param bool                  $strict        [optional] <p>
5850
     *                                             Determines if strict comparison (===) should be used during the
5851
     *                                             search.
5852
     *                                             </p>
5853
     *
5854
     * @return array
5855
     *               <p>an array of all the keys in input</p>
5856
     *
5857
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
5858
     * @psalm-return array<TKey|mixed>
5859
     * @psalm-mutation-free
5860
     */
5861 11
    protected function array_keys_recursive(
5862
        $input = null,
5863
        $search_values = null,
5864
        bool $strict = true
5865
    ): array {
5866
        // init
5867 11
        $keys = [];
5868 11
        $keysTmp = [];
5869
5870 11
        if ($input === null) {
5871 4
            $input = $this->getGenerator();
5872
        }
5873
5874 11
        if ($search_values === null) {
5875 11
            foreach ($input as $key => $value) {
5876 11
                $keys[] = $key;
5877
5878
                // check if recursive is needed
5879 11
                if (\is_array($value) === true) {
5880 4
                    $keysTmp[] = $this->array_keys_recursive($value);
5881
                }
5882
            }
5883
        } else {
5884 1
            $is_array_tmp = \is_array($search_values);
5885
5886 1
            foreach ($input as $key => $value) {
5887 View Code Duplication
                if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5888
                    (
5889 1
                        $is_array_tmp === false
5890
                        &&
5891 1
                        $strict === true
5892
                        &&
5893 1
                        $search_values === $value
5894
                    )
5895
                    ||
5896
                    (
5897 1
                        $is_array_tmp === false
5898
                        &&
5899 1
                        $strict === false
5900
                        &&
5901 1
                        $search_values == $value
5902
                    )
5903
                    ||
5904
                    (
5905 1
                        $is_array_tmp === true
5906
                        &&
5907 1
                        \in_array($value, $search_values, $strict)
5908
                    )
5909
                ) {
5910 1
                    $keys[] = $key;
5911
                }
5912
5913
                // check if recursive is needed
5914 1
                if (\is_array($value) === true) {
5915 1
                    $keysTmp[] = $this->array_keys_recursive($value);
5916
                }
5917
            }
5918
        }
5919
5920 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
5921
    }
5922
5923
    /**
5924
     * @param mixed      $path
5925
     * @param callable   $callable
5926
     * @param array|null $currentOffset
5927
     *
5928
     * @return void
5929
     *
5930
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
5931
     * @psalm-mutation-free
5932
     */
5933 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
5934
    {
5935 4
        $this->generatorToArray();
5936
5937 4
        if ($currentOffset === null) {
5938 4
            $currentOffset = &$this->array;
5939
        }
5940
5941 4
        $explodedPath = \explode($this->pathSeparator, $path);
5942 4
        if ($explodedPath === false) {
5943
            return;
5944
        }
5945
5946 4
        $nextPath = \array_shift($explodedPath);
5947
5948 4
        if (!isset($currentOffset[$nextPath])) {
5949
            return;
5950
        }
5951
5952 4
        if (!empty($explodedPath)) {
5953 1
            $this->callAtPath(
5954 1
                \implode($this->pathSeparator, $explodedPath),
5955 1
                $callable,
5956 1
                $currentOffset[$nextPath]
5957
            );
5958
        } else {
5959 4
            $callable($currentOffset[$nextPath]);
5960
        }
5961 4
    }
5962
5963
    /**
5964
     * Extracts the value of the given property or method from the object.
5965
     *
5966
     * @param static $object                <p>The object to extract the value from.</p>
5967
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
5968
     *                                      value should be extracted.</p>
5969
     *
5970
     * @throws \InvalidArgumentException if the method or property is not defined
5971
     *
5972
     * @return mixed
5973
     *               <p>The value extracted from the specified property or method.</p>
5974
     *
5975
     * @psalm-param self<TKey,T> $object
5976
     */
5977 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
5978
    {
5979 2
        if (isset($object[$keyOrPropertyOrMethod])) {
5980 2
            $return = $object->get($keyOrPropertyOrMethod);
5981
5982 2
            if ($return instanceof self) {
5983 1
                return $return->toArray();
5984
            }
5985
5986 1
            return $return;
5987
        }
5988
5989
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
5990
            return $object->{$keyOrPropertyOrMethod};
5991
        }
5992
5993
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
5994
            return $object->{$keyOrPropertyOrMethod}();
5995
        }
5996
5997
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
5998
    }
5999
6000
    /**
6001
     * create a fallback for array
6002
     *
6003
     * 1. use the current array, if it's a array
6004
     * 2. fallback to empty array, if there is nothing
6005
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6006
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6007
     * 5. call "__toArray()" on object, if the method exists
6008
     * 6. cast a string or object with "__toString()" into an array
6009
     * 7. throw a "InvalidArgumentException"-Exception
6010
     *
6011
     * @param mixed $data
6012
     *
6013
     * @throws \InvalidArgumentException
6014
     *
6015
     * @return array
6016
     *
6017
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6018
     */
6019 1100
    protected function fallbackForArray(&$data): array
6020
    {
6021 1100
        $data = $this->internalGetArray($data);
6022
6023 1100
        if ($data === null) {
6024 2
            throw new \InvalidArgumentException('Passed value should be a array');
6025
        }
6026
6027 1098
        return $data;
6028
    }
6029
6030
    /**
6031
     * @param bool $preserveKeys <p>
6032
     *                           e.g.: A generator maybe return the same key more then once,
6033
     *                           so maybe you will ignore the keys.
6034
     *                           </p>
6035
     *
6036
     * @return bool
6037
     *
6038
     * @noinspection ReturnTypeCanBeDeclaredInspection
6039
     * @psalm-mutation-free :/
6040
     */
6041 1014
    protected function generatorToArray(bool $preserveKeys = true)
6042
    {
6043 1014
        if ($this->generator) {
6044 2
            $this->array = $this->toArray(false, $preserveKeys);
6045 2
            $this->generator = null;
6046
6047 2
            return true;
6048
        }
6049
6050 1014
        return false;
6051
    }
6052
6053
    /**
6054
     * Get correct PHP constant for direction.
6055
     *
6056
     * @param int|string $direction
6057
     *
6058
     * @return int
6059
     * @psalm-mutation-free
6060
     */
6061 43
    protected function getDirection($direction): int
6062
    {
6063 43
        if ((string) $direction === $direction) {
6064 10
            $direction = \strtolower($direction);
6065
6066 10
            if ($direction === 'desc') {
6067 2
                $direction = \SORT_DESC;
6068
            } else {
6069 8
                $direction = \SORT_ASC;
6070
            }
6071
        }
6072
6073
        if (
6074 43
            $direction !== \SORT_DESC
6075
            &&
6076 43
            $direction !== \SORT_ASC
6077
        ) {
6078
            $direction = \SORT_ASC;
6079
        }
6080
6081 43
        return $direction;
6082
    }
6083
6084
    /**
6085
     * @return TypeCheckPhpDoc[]
6086
     *
6087
     * @noinspection ReturnTypeCanBeDeclaredInspection
6088
     */
6089 16
    protected function getPropertiesFromPhpDoc()
6090
    {
6091 16
        static $PROPERTY_CACHE = [];
6092 16
        $cacheKey = 'Class::' . static::class;
6093
6094 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6095 15
            return $PROPERTY_CACHE[$cacheKey];
6096
        }
6097
6098
        // init
6099 2
        $properties = [];
6100
6101 2
        $reflector = new \ReflectionClass($this);
6102 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6103 2
        $docComment = $reflector->getDocComment();
6104 2
        if ($docComment) {
6105 2
            $docblock = $factory->create($docComment);
6106
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6107 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6108 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
6109
            }
6110
        }
6111
6112 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6113
    }
6114
6115
    /**
6116
     * @param mixed $glue
6117
     * @param mixed $pieces
6118
     * @param bool  $useKeys
6119
     *
6120
     * @return string
6121
     */
6122 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
6123
    {
6124 36
        if ($pieces instanceof self) {
6125 1
            $pieces = $pieces->toArray();
6126
        }
6127
6128 36
        if (\is_array($pieces) === true) {
6129 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6130 36
            $pieces_count_not_zero = $pieces_count > 0;
6131
6132 36
            return \implode(
6133 36
                $glue,
6134 36
                \array_map(
6135 36
                    [$this, 'implode_recursive'],
6136 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6137 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6138
                )
6139
            );
6140
        }
6141
6142
        if (
6143 36
            \is_scalar($pieces) === true
6144
            ||
6145 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6146
        ) {
6147 32
            return (string) $pieces;
6148
        }
6149
6150 8
        return '';
6151
    }
6152
6153
    /**
6154
     * @param mixed                 $needle   <p>
6155
     *                                        The searched value.
6156
     *                                        </p>
6157
     *                                        <p>
6158
     *                                        If needle is a string, the comparison is done
6159
     *                                        in a case-sensitive manner.
6160
     *                                        </p>
6161
     * @param array|\Generator|null $haystack <p>
6162
     *                                        The array.
6163
     *                                        </p>
6164
     * @param bool                  $strict   [optional] <p>
6165
     *                                        If the third parameter strict is set to true
6166
     *                                        then the in_array function will also check the
6167
     *                                        types of the
6168
     *                                        needle in the haystack.
6169
     *                                        </p>
6170
     *
6171
     * @return bool
6172
     *              <p>true if needle is found in the array, false otherwise</p>
6173
     *
6174
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6175
     * @psalm-mutation-free
6176
     */
6177 19
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6178
    {
6179 19
        if ($haystack === null) {
6180
            $haystack = $this->getGenerator();
6181
        }
6182
6183 19
        foreach ($haystack as $item) {
6184 15
            if (\is_array($item) === true) {
6185 4
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6186
            } else {
6187
                /** @noinspection NestedPositiveIfStatementsInspection */
6188 15
                if ($strict === true) {
6189 15
                    $returnTmp = $item === $needle;
6190
                } else {
6191
                    $returnTmp = $item == $needle;
6192
                }
6193
            }
6194
6195 15
            if ($returnTmp === true) {
6196 11
                return true;
6197
            }
6198
        }
6199
6200 8
        return false;
6201
    }
6202
6203
    /**
6204
     * @param mixed $data
6205
     *
6206
     * @return array|null
6207
     *
6208
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6209
     */
6210 1100
    protected function internalGetArray(&$data)
6211
    {
6212 1100
        if (\is_array($data) === true) {
6213 1095
            return $data;
6214
        }
6215
6216 54
        if (!$data) {
6217 6
            return [];
6218
        }
6219
6220 53
        if (\is_object($data) === true) {
6221 48
            if ($data instanceof \ArrayObject) {
6222 4
                return $data->getArrayCopy();
6223
            }
6224
6225 45
            if ($data instanceof \Generator) {
6226
                return static::createFromGeneratorImmutable($data)->toArray();
6227
            }
6228
6229 45
            if ($data instanceof \Traversable) {
6230
                return static::createFromObject($data)->toArray();
6231
            }
6232
6233 45
            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...
6234
                return (array) $data->jsonSerialize();
6235
            }
6236
6237 45
            if (\method_exists($data, '__toArray')) {
6238
                return (array) $data->__toArray();
6239
            }
6240
6241 45
            if (\method_exists($data, '__toString')) {
6242
                return [(string) $data];
6243
            }
6244
        }
6245
6246 50
        if (\is_callable($data)) {
6247
            /**
6248
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6249
             */
6250 43
            $this->generator = new ArrayyRewindableGenerator($data);
6251
6252 43
            return [];
6253
        }
6254
6255 9
        if (\is_scalar($data)) {
6256 7
            return [$data];
6257
        }
6258
6259 2
        return null;
6260
    }
6261
6262
    /**
6263
     * Internal mechanics of remove method.
6264
     *
6265
     * @param mixed $key
6266
     *
6267
     * @return bool
6268
     */
6269 18
    protected function internalRemove($key): bool
6270
    {
6271 18
        $this->generatorToArray();
6272
6273
        if (
6274 18
            $this->pathSeparator
6275
            &&
6276 18
            (string) $key === $key
6277
            &&
6278 18
            \strpos($key, $this->pathSeparator) !== false
6279
        ) {
6280
            $path = \explode($this->pathSeparator, (string) $key);
6281
6282
            if ($path !== false) {
6283
                // crawl though the keys
6284
                while (\count($path, \COUNT_NORMAL) > 1) {
6285
                    $key = \array_shift($path);
6286
6287
                    if (!$this->has($key)) {
6288
                        return false;
6289
                    }
6290
6291
                    $this->array = &$this->array[$key];
6292
                }
6293
6294
                $key = \array_shift($path);
6295
            }
6296
        }
6297
6298 18
        unset($this->array[$key]);
6299
6300 18
        return true;
6301
    }
6302
6303
    /**
6304
     * Internal mechanic of set method.
6305
     *
6306
     * @param int|string|null $key
6307
     * @param mixed           $value
6308
     * @param bool            $checkProperties
6309
     *
6310
     * @return bool
6311
     */
6312 962
    protected function internalSet(
6313
        $key,
6314
        &$value,
6315
        bool $checkProperties = true
6316
    ): bool {
6317
        if (
6318 962
            $checkProperties === true
6319
            &&
6320 962
            $this->properties !== []
6321
        ) {
6322 87
            $this->checkType($key, $value);
6323
        }
6324
6325 960
        if ($key === null) {
6326
            return false;
6327
        }
6328
6329 960
        $this->generatorToArray();
6330
6331
        /** @var array<int|string,mixed> $array */
6332 960
        $array = &$this->array;
6333
6334
        /**
6335
         * https://github.com/vimeo/psalm/issues/2536
6336
         *
6337
         * @psalm-suppress PossiblyInvalidArgument
6338
         * @psalm-suppress InvalidScalarArgument
6339
         */
6340
        if (
6341 960
            $this->pathSeparator
6342
            &&
6343 960
            (string) $key === $key
6344
            &&
6345 960
            \strpos($key, $this->pathSeparator) !== false
6346
        ) {
6347 3
            $path = \explode($this->pathSeparator, (string) $key);
6348
6349 3
            if ($path !== false) {
6350
                // crawl through the keys
6351 3
                while (\count($path, \COUNT_NORMAL) > 1) {
6352 3
                    $key = \array_shift($path);
6353
6354 3
                    $array = &$array[$key];
6355
                }
6356
6357 3
                $key = \array_shift($path);
6358
            }
6359
        }
6360
6361 960
        $array[$key] = $value;
6362
6363 960
        return true;
6364
    }
6365
6366
    /**
6367
     * Convert a object into an array.
6368
     *
6369
     * @param mixed|object $object
6370
     *
6371
     * @return array|mixed
6372
     *
6373
     * @psalm-mutation-free
6374
     */
6375 5
    protected static function objectToArray($object)
6376
    {
6377 5
        if (!\is_object($object)) {
6378 4
            return $object;
6379
        }
6380
6381 5
        $object = \get_object_vars($object);
6382
6383
        /**
6384
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6385
         */
6386 5
        return \array_map(['static', 'objectToArray'], $object);
6387
    }
6388
6389
    /**
6390
     * @param array $data
6391
     * @param bool  $checkPropertiesInConstructor
6392
     *
6393
     * @return void
6394
     *
6395
     * @psalm-param array<mixed,T> $data
6396
     */
6397 1098
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6398
    {
6399 1098
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6400
                                        &&
6401 1098
                                        $checkPropertiesInConstructor === true;
6402
6403 1098
        if ($this->properties !== []) {
6404 78
            foreach ($data as $key => &$valueInner) {
6405 78
                $this->internalSet(
6406 78
                    $key,
6407 78
                    $valueInner,
6408 78
                    $checkPropertiesInConstructor
6409
                );
6410
            }
6411
        } else {
6412
            if (
6413 1031
                $this->checkPropertyTypes === true
6414
                ||
6415 1031
                $checkPropertiesInConstructor === true
6416
            ) {
6417 16
                $this->properties = $this->getPropertiesFromPhpDoc();
6418
            }
6419
6420
            /** @var TypeCheckInterface[] $properties */
6421 1031
            $properties = $this->properties;
6422
6423
            if (
6424 1031
                $this->checkPropertiesMismatchInConstructor === true
6425
                &&
6426 1031
                \count($data) !== 0
6427
                &&
6428 1031
                \count(\array_diff_key($properties, $data)) > 0
6429
            ) {
6430 1
                throw new \TypeError('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($properties), true));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...eys($properties), true).

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

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

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

Loading history...
6431
            }
6432
6433 1030
            foreach ($data as $key => &$valueInner) {
6434 888
                $this->internalSet(
6435 888
                    $key,
6436 888
                    $valueInner,
6437 888
                    $checkPropertiesInConstructor
6438
                );
6439
            }
6440
        }
6441 1091
    }
6442
6443
    /**
6444
     * sorting keys
6445
     *
6446
     * @param array      $elements
6447
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6448
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6449
     *                              <strong>SORT_NATURAL</strong></p>
6450
     *
6451
     * @return $this
6452
     *               <p>(Mutable) Return this Arrayy object.</p>
6453
     *
6454
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6455
     * @psalm-return static<TKey,T>
6456
     */
6457 18
    protected function sorterKeys(
6458
        array &$elements,
6459
        $direction = \SORT_ASC,
6460
        int $strategy = \SORT_REGULAR
6461
    ): self {
6462 18
        $direction = $this->getDirection($direction);
6463
6464 18
        switch ($direction) {
6465 18
            case 'desc':
6466
            case \SORT_DESC:
6467 6
                \krsort($elements, $strategy);
6468
6469 6
                break;
6470 13
            case 'asc':
6471 13
            case \SORT_ASC:
6472
            default:
6473 13
                \ksort($elements, $strategy);
6474
        }
6475
6476 18
        return $this;
6477
    }
6478
6479
    /**
6480
     * @param array      $elements  <p>Warning: used as reference</p>
6481
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6482
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6483
     *                              <strong>SORT_NATURAL</strong></p>
6484
     * @param bool       $keepKeys
6485
     *
6486
     * @return $this
6487
     *               <p>(Mutable) Return this Arrayy object.</p>
6488
     *
6489
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6490
     * @psalm-return static<TKey,T>
6491
     */
6492 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6493
    {
6494 24
        $direction = $this->getDirection($direction);
6495
6496 24
        if (!$strategy) {
6497 24
            $strategy = \SORT_REGULAR;
6498
        }
6499
6500 24
        switch ($direction) {
6501 24
            case 'desc':
6502
            case \SORT_DESC:
6503 13
                if ($keepKeys) {
6504 9
                    \arsort($elements, $strategy);
6505
                } else {
6506 4
                    \rsort($elements, $strategy);
6507
                }
6508
6509 13
                break;
6510 11
            case 'asc':
6511 11
            case \SORT_ASC:
6512
            default:
6513 11
                if ($keepKeys) {
6514 4
                    \asort($elements, $strategy);
6515
                } else {
6516 7
                    \sort($elements, $strategy);
6517
                }
6518
        }
6519
6520 24
        return $this;
6521
    }
6522
6523
    /**
6524
     * @param int|string|null $key
6525
     * @param mixed           $value
6526
     *
6527
     * @return void
6528
     */
6529 87
    private function checkType($key, $value)
6530
    {
6531
        if (
6532 87
            $key !== null
6533
            &&
6534 87
            isset($this->properties[$key]) === false
6535
            &&
6536 87
            $this->checkPropertiesMismatch === true
6537
        ) {
6538
            throw new \TypeError('The key ' . $key . ' does not exists in "properties". Maybe because @property was not used for the class (' . \get_class($this) . ').');
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'The key ' . $key . ' do...get_class($this) . ').'.

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

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

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

Loading history...
6539
        }
6540
6541 87
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6542 76
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6543 17
        } elseif ($key !== null && isset($this->properties[$key])) {
6544 17
            $this->properties[$key]->checkType($value);
6545
        }
6546 85
    }
6547
}
6548