Completed
Push — master ( 787715...6d90b1 )
by Lars
02:34 queued 46s
created

Arrayy::pad()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 1
rs 10
c 0
b 0
f 0
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,mixed>|array<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<mixed,mixed>|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<mixed,mixed>|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 3
                    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 2
                    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
                foreach ($values as $value) {
916 1
                    $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 9
                $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 8
            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 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1202
    {
1203 23
        if ($recursive === true) {
1204 18
            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 7
                    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 22
                    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 11
                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 10
            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 3
                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 1
            'eq' => static function ($item, $prop, $value): bool {
2092 1
                return $item[$prop] === $value;
2093 1
            },
2094 1
            'gt' => static function ($item, $prop, $value): bool {
2095
                return $item[$prop] > $value;
2096 1
            },
2097 1
            'ge' => static function ($item, $prop, $value): bool {
2098
                return $item[$prop] >= $value;
2099 1
            },
2100 1
            'gte' => static function ($item, $prop, $value): bool {
2101
                return $item[$prop] >= $value;
2102 1
            },
2103 1
            'lt' => static function ($item, $prop, $value): bool {
2104 1
                return $item[$prop] < $value;
2105 1
            },
2106 1
            'le' => static function ($item, $prop, $value): bool {
2107
                return $item[$prop] <= $value;
2108 1
            },
2109 1
            'lte' => static function ($item, $prop, $value): bool {
2110
                return $item[$prop] <= $value;
2111 1
            },
2112 1
            'ne' => static function ($item, $prop, $value): bool {
2113
                return $item[$prop] !== $value;
2114 1
            },
2115 1
            'contains' => static function ($item, $prop, $value): bool {
2116 1
                return \in_array($item[$prop], (array) $value, true);
2117 1
            },
2118 1
            'notContains' => static function ($item, $prop, $value): bool {
2119
                return !\in_array($item[$prop], (array) $value, true);
2120 1
            },
2121 1
            'newer' => static function ($item, $prop, $value): bool {
2122
                return \strtotime($item[$prop]) > \strtotime($value);
2123 1
            },
2124 1
            '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 1
                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 6
                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 21
    public function first()
2199
    {
2200 21
        $key_first = $this->firstKey();
2201 21
        if ($key_first === null) {
2202 3
            return null;
2203
        }
2204
2205 18
        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 28
    public function firstKey()
2216
    {
2217 28
        $this->generatorToArray();
2218
2219 28
        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 11
                return static::create(
2364 11
                    $usedArray[$key],
2365 11
                    $this->iteratorClass,
2366 11
                    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 4
                    $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
     * @psalm-mutation-free
2767
     */
2768 28
    public function implode(string $glue = ''): string
2769
    {
2770 28
        return $this->implode_recursive($glue, $this->toArray(), false);
2771
    }
2772
2773
    /**
2774
     * Implodes the keys of this array.
2775
     *
2776
     * @param string $glue
2777
     *
2778
     * @return string
2779
     * @psalm-mutation-free
2780
     */
2781 8
    public function implodeKeys(string $glue = ''): string
2782
    {
2783 8
        return $this->implode_recursive($glue, $this->toArray(), true);
2784
    }
2785
2786
    /**
2787
     * Given a list and an iterate-function that returns
2788
     * a key for each element in the list (or a property name),
2789
     * returns an object with an index of each item.
2790
     *
2791
     * @param mixed $key
2792
     *
2793
     * @return static
2794
     *                <p>(Immutable)</p>
2795
     *
2796
     * @psalm-return static<TKey,T>
2797
     * @psalm-mutation-free
2798
     */
2799 4
    public function indexBy($key): self
2800
    {
2801
        // init
2802 4
        $results = [];
2803
2804 4
        foreach ($this->getGenerator() as $a) {
2805 4
            if (\array_key_exists($key, $a) === true) {
2806 4
                $results[$a[$key]] = $a;
2807
            }
2808
        }
2809
2810 4
        return static::create(
2811 4
            $results,
2812 4
            $this->iteratorClass,
2813 4
            false
2814
        );
2815
    }
2816
2817
    /**
2818
     * alias: for "Arrayy->searchIndex()"
2819
     *
2820
     * @param mixed $value <p>The value to search for.</p>
2821
     *
2822
     * @return false|mixed
2823
     *
2824
     * @see Arrayy::searchIndex()
2825
     */
2826 4
    public function indexOf($value)
2827
    {
2828 4
        return $this->searchIndex($value);
2829
    }
2830
2831
    /**
2832
     * Get everything but the last..$to items.
2833
     *
2834
     * @param int $to
2835
     *
2836
     * @return static
2837
     *                <p>(Immutable)</p>
2838
     *
2839
     * @psalm-return static<TKey,T>
2840
     * @psalm-mutation-free
2841
     */
2842 12
    public function initial(int $to = 1): self
2843
    {
2844 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
2845
    }
2846
2847
    /**
2848
     * Return an array with all elements found in input array.
2849
     *
2850
     * @param array $search
2851
     * @param bool  $keepKeys
2852
     *
2853
     * @return static
2854
     *                <p>(Immutable)</p>
2855
     *
2856
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
2857
     * @psalm-return static<TKey,T>
2858
     * @psalm-mutation-free
2859
     */
2860 4
    public function intersection(array $search, bool $keepKeys = false): self
2861
    {
2862 4
        if ($keepKeys) {
2863
            /**
2864
             * @psalm-suppress MissingClosureReturnType
2865
             * @psalm-suppress MissingClosureParamType
2866
             */
2867 1
            return static::create(
2868 1
                \array_uintersect(
2869 1
                    $this->toArray(),
2870 1
                    $search,
2871 1
                    static function ($a, $b) {
2872 1
                        return $a === $b ? 0 : -1;
2873 1
                    }
2874
                ),
2875 1
                $this->iteratorClass,
2876 1
                false
2877
            );
2878
        }
2879
2880 3
        return static::create(
2881 3
            \array_values(\array_intersect($this->toArray(), $search)),
2882 3
            $this->iteratorClass,
2883 3
            false
2884
        );
2885
    }
2886
2887
    /**
2888
     * Return an array with all elements found in input array.
2889
     *
2890
     * @param array ...$array
2891
     *
2892
     * @return static
2893
     *                <p>(Immutable)</p>
2894
     *
2895
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2896
     * @psalm-return static<TKey,T>
2897
     * @psalm-mutation-free
2898
     */
2899 1
    public function intersectionMulti(...$array): self
2900
    {
2901 1
        return static::create(
2902 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
2903 1
            $this->iteratorClass,
2904 1
            false
2905
        );
2906
    }
2907
2908
    /**
2909
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2910
     *
2911
     * @param array $search
2912
     *
2913
     * @return bool
2914
     *
2915
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
2916
     */
2917 1
    public function intersects(array $search): bool
2918
    {
2919 1
        return $this->intersection($search)->count() > 0;
2920
    }
2921
2922
    /**
2923
     * Invoke a function on all of an array's values.
2924
     *
2925
     * @param callable $callable
2926
     * @param mixed    $arguments
2927
     *
2928
     * @return static
2929
     *                <p>(Immutable)</p>
2930
     *
2931
     * @psalm-param  callable(T=,mixed):mixed $callable
2932
     * @psalm-return static<TKey,T>
2933
     * @psalm-mutation-free
2934
     */
2935 1
    public function invoke($callable, $arguments = []): self
2936
    {
2937
        // If one argument given for each iteration, create an array for it.
2938 1
        if (\is_array($arguments) === false) {
2939 1
            $arguments = \array_fill(
2940 1
                0,
2941 1
                $this->count(),
2942 1
                $arguments
2943
            );
2944
        }
2945
2946
        // If the callable has arguments, pass them.
2947 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...
2948 1
            $array = \array_map($callable, $this->toArray(), $arguments);
2949
        } else {
2950 1
            $array = $this->map($callable);
2951
        }
2952
2953 1
        return static::create(
2954 1
            $array,
2955 1
            $this->iteratorClass,
2956 1
            false
2957
        );
2958
    }
2959
2960
    /**
2961
     * Check whether array is associative or not.
2962
     *
2963
     * @param bool $recursive
2964
     *
2965
     * @return bool
2966
     *              <p>Returns true if associative, false otherwise.</p>
2967
     */
2968 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...
2969
    {
2970 15
        if ($this->isEmpty()) {
2971 3
            return false;
2972
        }
2973
2974 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2975 13
            if ((string) $key !== $key) {
2976 13
                return false;
2977
            }
2978
        }
2979
2980 3
        return true;
2981
    }
2982
2983
    /**
2984
     * Check if a given key or keys are empty.
2985
     *
2986
     * @param int|int[]|string|string[]|null $keys
2987
     *
2988
     * @return bool
2989
     *              <p>Returns true if empty, false otherwise.</p>
2990
     * @psalm-mutation-free
2991
     */
2992 38
    public function isEmpty($keys = null): bool
2993
    {
2994 38
        if ($this->generator) {
2995
            return $this->toArray() === [];
2996
        }
2997
2998 38
        if ($keys === null) {
2999 38
            return $this->array === [];
3000
        }
3001
3002
        foreach ((array) $keys as $key) {
3003
            if (!empty($this->get($key))) {
3004
                return false;
3005
            }
3006
        }
3007
3008
        return true;
3009
    }
3010
3011
    /**
3012
     * Check if the current array is equal to the given "$array" or not.
3013
     *
3014
     * @param array $array
3015
     *
3016
     * @return bool
3017
     *
3018
     * @psalm-param array<mixed,mixed> $array
3019
     */
3020 1
    public function isEqual(array $array): bool
3021
    {
3022 1
        return $this->toArray() === $array;
3023
    }
3024
3025
    /**
3026
     * Check if the current array is a multi-array.
3027
     *
3028
     * @return bool
3029
     */
3030 22
    public function isMultiArray(): bool
3031
    {
3032
        return !(
3033 22
            \count($this->toArray(), \COUNT_NORMAL)
3034
            ===
3035 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3036
        );
3037
    }
3038
3039
    /**
3040
     * Check whether array is numeric or not.
3041
     *
3042
     * @return bool
3043
     *              <p>Returns true if numeric, false otherwise.</p>
3044
     */
3045 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...
3046
    {
3047 5
        if ($this->isEmpty()) {
3048 2
            return false;
3049
        }
3050
3051 4
        foreach ($this->keys()->getGenerator() as $key) {
3052 4
            if ((int) $key !== $key) {
3053 4
                return false;
3054
            }
3055
        }
3056
3057 2
        return true;
3058
    }
3059
3060
    /**
3061
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3062
     *
3063
     * @param bool $recursive
3064
     *
3065
     * @return bool
3066
     * @psalm-mutation-free
3067
     */
3068 9
    public function isSequential(bool $recursive = false): bool
3069
    {
3070
3071
        // recursive
3072
3073 9
        if ($recursive === true) {
3074
            return $this->array_keys_recursive($this->toArray())
3075
                   ===
3076
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3077
        }
3078
3079
        // non recursive
3080
3081 9
        return \array_keys($this->toArray())
3082
               ===
3083 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3084
    }
3085
3086
    /**
3087
     * @return array
3088
     *
3089
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3090
     */
3091
    public function jsonSerialize(): array
3092
    {
3093
        return $this->toArray();
3094
    }
3095
3096
    /**
3097
     * Gets the key/index of the element at the current internal iterator position.
3098
     *
3099
     * @return int|string|null
3100
     */
3101
    public function key()
3102
    {
3103
        return \key($this->array);
3104
    }
3105
3106
    /**
3107
     * Checks if the given key exists in the provided array.
3108
     *
3109
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3110
     *       then you need to use "Arrayy->offsetExists()".
3111
     *
3112
     * @param int|string $key the key to look for
3113
     *
3114
     * @return bool
3115
     * @psalm-mutation-free
3116
     */
3117 127
    public function keyExists($key): bool
3118
    {
3119 127
        return \array_key_exists($key, $this->array);
3120
    }
3121
3122
    /**
3123
     * Get all keys from the current array.
3124
     *
3125
     * @param bool       $recursive     [optional] <p>
3126
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3127
     *                                  </p>
3128
     * @param mixed|null $search_values [optional] <p>
3129
     *                                  If specified, then only keys containing these values are returned.
3130
     *                                  </p>
3131
     * @param bool       $strict        [optional] <p>
3132
     *                                  Determines if strict comparison (===) should be used during the search.
3133
     *                                  </p>
3134
     *
3135
     * @return static
3136
     *                <p>(Immutable) An array of all the keys in input.</p>
3137
     *
3138
     * @psalm-return static<array-key,TKey>
3139
     * @psalm-mutation-free
3140
     */
3141 29
    public function keys(
3142
        bool $recursive = false,
3143
        $search_values = null,
3144
        bool $strict = true
3145
    ): self {
3146
3147
        // recursive
3148
3149 29
        if ($recursive === true) {
3150 4
            $array = $this->array_keys_recursive(
3151 4
                null,
3152 4
                $search_values,
3153 4
                $strict
3154
            );
3155
3156 4
            return static::create(
3157 4
                $array,
3158 4
                $this->iteratorClass,
3159 4
                false
3160
            );
3161
        }
3162
3163
        // non recursive
3164
3165 28
        if ($search_values === null) {
3166 28
            $arrayFunction = function (): \Generator {
3167 28
                foreach ($this->getGenerator() as $key => $value) {
3168 26
                    yield $key;
3169
                }
3170 28
            };
3171
        } else {
3172 1
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3173 1
                $is_array_tmp = \is_array($search_values);
3174
3175 1
                foreach ($this->getGenerator() as $key => $value) {
3176 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...
3177
                        (
3178 1
                            $is_array_tmp === false
3179
                            &&
3180 1
                            $strict === true
3181
                            &&
3182 1
                            $search_values === $value
3183
                        )
3184
                        ||
3185
                        (
3186 1
                            $is_array_tmp === false
3187
                            &&
3188 1
                            $strict === false
3189
                            &&
3190 1
                            $search_values == $value
3191
                        )
3192
                        ||
3193
                        (
3194 1
                            $is_array_tmp === true
3195
                            &&
3196 1
                            \in_array($value, $search_values, $strict)
3197
                        )
3198
                    ) {
3199 1
                        yield $key;
3200
                    }
3201
                }
3202 1
            };
3203
        }
3204
3205 28
        return static::create(
3206 28
            $arrayFunction,
3207 28
            $this->iteratorClass,
3208 28
            false
3209
        );
3210
    }
3211
3212
    /**
3213
     * Sort an array by key in reverse order.
3214
     *
3215
     * @param int $sort_flags [optional] <p>
3216
     *                        You may modify the behavior of the sort using the optional
3217
     *                        parameter sort_flags, for details
3218
     *                        see sort.
3219
     *                        </p>
3220
     *
3221
     * @return $this
3222
     *               <p>(Mutable) Return this Arrayy object.</p>
3223
     *
3224
     * @psalm-return static<TKey,T>
3225
     */
3226 4
    public function krsort(int $sort_flags = 0): self
3227
    {
3228 4
        $this->generatorToArray();
3229
3230 4
        \krsort($this->array, $sort_flags);
3231
3232 4
        return $this;
3233
    }
3234
3235
    /**
3236
     * Sort an array by key in reverse order.
3237
     *
3238
     * @param int $sort_flags [optional] <p>
3239
     *                        You may modify the behavior of the sort using the optional
3240
     *                        parameter sort_flags, for details
3241
     *                        see sort.
3242
     *                        </p>
3243
     *
3244
     * @return $this
3245
     *               <p>(Immutable) Return this Arrayy object.</p>
3246
     *
3247
     * @psalm-return static<TKey,T>
3248
     * @psalm-mutation-free
3249
     */
3250 4
    public function krsortImmutable(int $sort_flags = 0): self
3251
    {
3252 4
        $that = clone $this;
3253
3254
        /**
3255
         * @psalm-suppress ImpureMethodCall - object is already cloned
3256
         */
3257 4
        $that->krsort($sort_flags);
3258
3259 4
        return $that;
3260
    }
3261
3262
    /**
3263
     * Get the last value from the current array.
3264
     *
3265
     * @return mixed|null
3266
     *                    <p>Return null if there wasn't a element.</p>
3267
     * @psalm-mutation-free
3268
     */
3269 17
    public function last()
3270
    {
3271 17
        $key_last = $this->lastKey();
3272 17
        if ($key_last === null) {
3273 2
            return null;
3274
        }
3275
3276 15
        return $this->get($key_last);
3277
    }
3278
3279
    /**
3280
     * Get the last key from the current array.
3281
     *
3282
     * @return mixed|null
3283
     *                    <p>Return null if there wasn't a element.</p>
3284
     * @psalm-mutation-free
3285
     */
3286 21
    public function lastKey()
3287
    {
3288 21
        $this->generatorToArray();
3289
3290 21
        return \array_key_last($this->array);
3291
    }
3292
3293
    /**
3294
     * Get the last value(s) from the current array.
3295
     *
3296
     * @param int|null $number
3297
     *
3298
     * @return static
3299
     *                <p>(Immutable)</p>
3300
     *
3301
     * @psalm-return static<TKey,T>
3302
     * @psalm-mutation-free
3303
     */
3304 13
    public function lastsImmutable(int $number = null): self
3305
    {
3306 13
        if ($this->isEmpty()) {
3307 1
            return static::create(
3308 1
                [],
3309 1
                $this->iteratorClass,
3310 1
                false
3311
            );
3312
        }
3313
3314 12
        if ($number === null) {
3315 8
            $poppedValue = $this->last();
3316
3317 8
            if ($poppedValue === null) {
3318 1
                $poppedValue = [$poppedValue];
3319
            } else {
3320 7
                $poppedValue = (array) $poppedValue;
3321
            }
3322
3323 8
            $arrayy = static::create(
3324 8
                $poppedValue,
3325 8
                $this->iteratorClass,
3326 8
                false
3327
            );
3328
        } else {
3329 4
            $number = (int) $number;
3330 4
            $arrayy = $this->rest(-$number);
3331
        }
3332
3333 12
        return $arrayy;
3334
    }
3335
3336
    /**
3337
     * Get the last value(s) from the current array.
3338
     *
3339
     * @param int|null $number
3340
     *
3341
     * @return $this
3342
     *               <p>(Mutable)</p>
3343
     *
3344
     * @psalm-return static<TKey,T>
3345
     */
3346 13
    public function lastsMutable(int $number = null): self
3347
    {
3348 13
        if ($this->isEmpty()) {
3349 1
            return $this;
3350
        }
3351
3352 12
        if ($number === null) {
3353 8
            $poppedValue = $this->last();
3354
3355 8
            if ($poppedValue === null) {
3356 1
                $poppedValue = [$poppedValue];
3357
            } else {
3358 7
                $poppedValue = (array) $poppedValue;
3359
            }
3360
3361 8
            $this->array = static::create(
3362 8
                $poppedValue,
3363 8
                $this->iteratorClass,
3364 8
                false
3365 8
            )->toArray();
3366
        } else {
3367 4
            $number = (int) $number;
3368 4
            $this->array = $this->rest(-$number)->toArray();
3369
        }
3370
3371 12
        $this->generator = null;
3372
3373 12
        return $this;
3374
    }
3375
3376
    /**
3377
     * Count the values from the current array.
3378
     *
3379
     * alias: for "Arrayy->count()"
3380
     *
3381
     * @param int $mode
3382
     *
3383
     * @return int
3384
     *
3385
     * @see Arrayy::count()
3386
     */
3387 20
    public function length(int $mode = \COUNT_NORMAL): int
3388
    {
3389 20
        return $this->count($mode);
3390
    }
3391
3392
    /**
3393
     * Apply the given function to the every element of the array,
3394
     * collecting the results.
3395
     *
3396
     * @param callable $callable
3397
     * @param bool     $useKeyAsSecondParameter
3398
     * @param mixed    ...$arguments
3399
     *
3400
     * @return static
3401
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3402
     *
3403
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3404
     * @psalm-return static<TKey,T>
3405
     * @psalm-mutation-free
3406
     */
3407 5
    public function map(
3408
        callable $callable,
3409
        bool $useKeyAsSecondParameter = false,
3410
        ...$arguments
3411
    ) {
3412
        /**
3413
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3414
         */
3415 5
        $useArguments = \func_num_args() > 2;
3416
3417 5
        return static::create(
3418 5
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3419 5
                foreach ($this->getGenerator() as $key => $value) {
3420 4
                    if ($useArguments) {
3421 3
                        if ($useKeyAsSecondParameter) {
3422
                            yield $key => $callable($value, $key, ...$arguments);
3423
                        } else {
3424 3
                            yield $key => $callable($value, ...$arguments);
3425
                        }
3426
                    } else {
3427
                        /** @noinspection NestedPositiveIfStatementsInspection */
3428 4
                        if ($useKeyAsSecondParameter) {
3429
                            yield $key => $callable($value, $key);
3430
                        } else {
3431 4
                            yield $key => $callable($value);
3432
                        }
3433
                    }
3434
                }
3435 5
            },
3436 5
            $this->iteratorClass,
3437 5
            false
3438
        );
3439
    }
3440
3441
    /**
3442
     * Check if all items in current array match a truth test.
3443
     *
3444
     * @param \Closure $closure
3445
     *
3446
     * @return bool
3447
     */
3448 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...
3449
    {
3450 15
        if ($this->count() === 0) {
3451 2
            return false;
3452
        }
3453
3454 13
        foreach ($this->getGenerator() as $key => $value) {
3455 13
            $value = $closure($value, $key);
3456
3457 13
            if ($value === false) {
3458 13
                return false;
3459
            }
3460
        }
3461
3462 7
        return true;
3463
    }
3464
3465
    /**
3466
     * Check if any item in the current array matches a truth test.
3467
     *
3468
     * @param \Closure $closure
3469
     *
3470
     * @return bool
3471
     */
3472 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...
3473
    {
3474 14
        if ($this->count() === 0) {
3475 2
            return false;
3476
        }
3477
3478 12
        foreach ($this->getGenerator() as $key => $value) {
3479 12
            $value = $closure($value, $key);
3480
3481 12
            if ($value === true) {
3482 12
                return true;
3483
            }
3484
        }
3485
3486 4
        return false;
3487
    }
3488
3489
    /**
3490
     * Get the max value from an array.
3491
     *
3492
     * @return false|mixed
3493
     *                     <p>Will return false if there are no values.</p>
3494
     */
3495 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...
3496
    {
3497 10
        if ($this->count() === 0) {
3498 1
            return false;
3499
        }
3500
3501 9
        $max = false;
3502 9
        foreach ($this->getGenerator() as $value) {
3503
            if (
3504 9
                $max === false
3505
                ||
3506 9
                $value > $max
3507
            ) {
3508 9
                $max = $value;
3509
            }
3510
        }
3511
3512 9
        return $max;
3513
    }
3514
3515
    /**
3516
     * Merge the new $array into the current array.
3517
     *
3518
     * - keep key,value from the current array, also if the index is in the new $array
3519
     *
3520
     * @param array $array
3521
     * @param bool  $recursive
3522
     *
3523
     * @return static
3524
     *                <p>(Immutable)</p>
3525
     *
3526
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3527
     * @psalm-return static<int|TKey,T>
3528
     * @psalm-mutation-free
3529
     */
3530 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...
3531
    {
3532 25
        if ($recursive === true) {
3533 4
            $result = \array_replace_recursive($this->toArray(), $array);
3534
        } else {
3535 21
            $result = \array_replace($this->toArray(), $array);
3536
        }
3537
3538 25
        return static::create(
3539 25
            $result,
3540 25
            $this->iteratorClass,
3541 25
            false
3542
        );
3543
    }
3544
3545
    /**
3546
     * Merge the new $array into the current array.
3547
     *
3548
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3549
     * - create new indexes
3550
     *
3551
     * @param array $array
3552
     * @param bool  $recursive
3553
     *
3554
     * @return static
3555
     *                <p>(Immutable)</p>
3556
     *
3557
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3558
     * @psalm-return static<TKey,T>
3559
     * @psalm-mutation-free
3560
     */
3561 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...
3562
    {
3563 16
        if ($recursive === true) {
3564 4
            $result = \array_merge_recursive($this->toArray(), $array);
3565
        } else {
3566 12
            $result = \array_merge($this->toArray(), $array);
3567
        }
3568
3569 16
        return static::create(
3570 16
            $result,
3571 16
            $this->iteratorClass,
3572 16
            false
3573
        );
3574
    }
3575
3576
    /**
3577
     * Merge the the current array into the $array.
3578
     *
3579
     * - use key,value from the new $array, also if the index is in the current array
3580
     *
3581
     * @param array $array
3582
     * @param bool  $recursive
3583
     *
3584
     * @return static
3585
     *                <p>(Immutable)</p>
3586
     *
3587
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3588
     * @psalm-return static<TKey,T>
3589
     * @psalm-mutation-free
3590
     */
3591 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...
3592
    {
3593 16
        if ($recursive === true) {
3594 4
            $result = \array_replace_recursive($array, $this->toArray());
3595
        } else {
3596 12
            $result = \array_replace($array, $this->toArray());
3597
        }
3598
3599 16
        return static::create(
3600 16
            $result,
3601 16
            $this->iteratorClass,
3602 16
            false
3603
        );
3604
    }
3605
3606
    /**
3607
     * Merge the current array into the new $array.
3608
     *
3609
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3610
     * - create new indexes
3611
     *
3612
     * @param array $array
3613
     * @param bool  $recursive
3614
     *
3615
     * @return static
3616
     *                <p>(Immutable)</p>
3617
     *
3618
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3619
     * @psalm-return static<TKey,T>
3620
     * @psalm-mutation-free
3621
     */
3622 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...
3623
    {
3624 17
        if ($recursive === true) {
3625 4
            $result = \array_merge_recursive($array, $this->toArray());
3626
        } else {
3627 13
            $result = \array_merge($array, $this->toArray());
3628
        }
3629
3630 17
        return static::create(
3631 17
            $result,
3632 17
            $this->iteratorClass,
3633 17
            false
3634
        );
3635
    }
3636
3637
    /**
3638
     * @return ArrayyMeta|static
3639
     */
3640 15
    public static function meta()
3641
    {
3642 15
        return (new ArrayyMeta())->getMetaObject(static::class);
3643
    }
3644
3645
    /**
3646
     * Get the min value from an array.
3647
     *
3648
     * @return false|mixed
3649
     *                     <p>Will return false if there are no values.</p>
3650
     */
3651 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...
3652
    {
3653 10
        if ($this->count() === 0) {
3654 1
            return false;
3655
        }
3656
3657 9
        $min = false;
3658 9
        foreach ($this->getGenerator() as $value) {
3659
            if (
3660 9
                $min === false
3661
                ||
3662 9
                $value < $min
3663
            ) {
3664 9
                $min = $value;
3665
            }
3666
        }
3667
3668 9
        return $min;
3669
    }
3670
3671
    /**
3672
     * Get the most used value from the array.
3673
     *
3674
     * @return mixed|null
3675
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
3676
     * @psalm-mutation-free
3677
     */
3678 3
    public function mostUsedValue()
3679
    {
3680 3
        return $this->countValues()->arsortImmutable()->firstKey();
3681
    }
3682
3683
    /**
3684
     * Get the most used value from the array.
3685
     *
3686
     * @param int|null $number <p>How many values you will take?</p>
3687
     *
3688
     * @return static
3689
     *                <p>(Immutable)</p>
3690
     *
3691
     * @psalm-return static<TKey,T>
3692
     * @psalm-mutation-free
3693
     */
3694 3
    public function mostUsedValues(int $number = null): self
3695
    {
3696 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
3697
    }
3698
3699
    /**
3700
     * Move an array element to a new index.
3701
     *
3702
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3703
     *
3704
     * @param int|string $from
3705
     * @param int        $to
3706
     *
3707
     * @return static
3708
     *                <p>(Immutable)</p>
3709
     *
3710
     * @psalm-return static<TKey,T>
3711
     * @psalm-mutation-free
3712
     */
3713 1
    public function moveElement($from, $to): self
3714
    {
3715 1
        $array = $this->toArray();
3716
3717 1
        if ((int) $from === $from) {
3718 1
            $tmp = \array_splice($array, $from, 1);
3719 1
            \array_splice($array, (int) $to, 0, $tmp);
3720 1
            $output = $array;
3721 1
        } elseif ((string) $from === $from) {
3722 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3723 1
            $itemToMove = $array[$from];
3724 1
            if ($indexToMove !== false) {
3725 1
                \array_splice($array, $indexToMove, 1);
3726
            }
3727 1
            $i = 0;
3728 1
            $output = [];
3729 1
            foreach ($array as $key => $item) {
3730 1
                if ($i === $to) {
3731 1
                    $output[$from] = $itemToMove;
3732
                }
3733 1
                $output[$key] = $item;
3734 1
                ++$i;
3735
            }
3736
        } else {
3737
            $output = [];
3738
        }
3739
3740 1
        return static::create(
3741 1
            $output,
3742 1
            $this->iteratorClass,
3743 1
            false
3744
        );
3745
    }
3746
3747
    /**
3748
     * Move an array element to the first place.
3749
     *
3750
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3751
     *       loss the keys of an indexed array.
3752
     *
3753
     * @param int|string $key
3754
     *
3755
     * @return static
3756
     *                <p>(Immutable)</p>
3757
     *
3758
     * @psalm-return static<TKey,T>
3759
     * @psalm-mutation-free
3760
     */
3761 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...
3762
    {
3763 1
        $array = $this->toArray();
3764
3765 1
        if ($this->offsetExists($key)) {
3766 1
            $tmpValue = $this->get($key);
3767 1
            unset($array[$key]);
3768 1
            $array = [$key => $tmpValue] + $array;
3769
        }
3770
3771 1
        return static::create(
3772 1
            $array,
3773 1
            $this->iteratorClass,
3774 1
            false
3775
        );
3776
    }
3777
3778
    /**
3779
     * Move an array element to the last place.
3780
     *
3781
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3782
     *       loss the keys of an indexed array.
3783
     *
3784
     * @param int|string $key
3785
     *
3786
     * @return static
3787
     *                <p>(Immutable)</p>
3788
     *
3789
     * @psalm-return static<TKey,T>
3790
     * @psalm-mutation-free
3791
     */
3792 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...
3793
    {
3794 1
        $array = $this->toArray();
3795
3796 1
        if ($this->offsetExists($key)) {
3797 1
            $tmpValue = $this->get($key);
3798 1
            unset($array[$key]);
3799 1
            $array += [$key => $tmpValue];
3800
        }
3801
3802 1
        return static::create(
3803 1
            $array,
3804 1
            $this->iteratorClass,
3805 1
            false
3806
        );
3807
    }
3808
3809
    /**
3810
     * Moves the internal iterator position to the next element and returns this element.
3811
     *
3812
     * @return false|mixed
3813
     *                     <p>(Mutable) Will return false if there are no values.</p>
3814
     */
3815
    public function next()
3816
    {
3817
        return \next($this->array);
3818
    }
3819
3820
    /**
3821
     * Get the next nth keys and values from the array.
3822
     *
3823
     * @param int $step
3824
     * @param int $offset
3825
     *
3826
     * @return static
3827
     *                <p>(Immutable)</p>
3828
     *
3829
     * @psalm-return static<TKey,T>
3830
     * @psalm-mutation-free
3831
     */
3832
    public function nth(int $step, int $offset = 0): self
3833
    {
3834 1
        $arrayFunction = function () use ($step, $offset): \Generator {
3835 1
            $position = 0;
3836 1
            foreach ($this->getGenerator() as $key => $value) {
3837 1
                if ($position++ % $step !== $offset) {
3838 1
                    continue;
3839
                }
3840
3841 1
                yield $key => $value;
3842
            }
3843 1
        };
3844
3845 1
        return static::create(
3846 1
            $arrayFunction,
3847 1
            $this->iteratorClass,
3848 1
            false
3849
        );
3850
    }
3851
3852
    /**
3853
     * Get a subset of the items from the given array.
3854
     *
3855
     * @param mixed[] $keys
3856
     *
3857
     * @return static
3858
     *                <p>(Immutable)</p>
3859
     *
3860
     * @psalm-return static<TKey,T>
3861
     * @psalm-mutation-free
3862
     */
3863 1
    public function only(array $keys): self
3864
    {
3865 1
        $array = $this->toArray();
3866
3867 1
        return static::create(
3868 1
            \array_intersect_key($array, \array_flip($keys)),
3869 1
            $this->iteratorClass,
3870 1
            false
3871
        );
3872
    }
3873
3874
    /**
3875
     * Pad array to the specified size with a given value.
3876
     *
3877
     * @param int   $size  <p>Size of the result array.</p>
3878
     * @param mixed $value <p>Empty value by default.</p>
3879
     *
3880
     * @return static
3881
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3882
     *
3883
     * @psalm-return static<TKey,T>
3884
     * @psalm-mutation-free
3885
     */
3886 5
    public function pad(int $size, $value): self
3887
    {
3888 5
        return static::create(
3889 5
            \array_pad($this->toArray(), $size, $value),
3890 5
            $this->iteratorClass,
3891 5
            false
3892
        );
3893
    }
3894
3895
    /**
3896
     * Partitions this array in two array according to a predicate.
3897
     * Keys are preserved in the resulting array.
3898
     *
3899
     * @param \Closure $closure
3900
     *                          <p>The predicate on which to partition.</p>
3901
     *
3902
     * @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...
3903
     *                    <p>An array with two elements. The first element contains the array
3904
     *                    of elements where the predicate returned TRUE, the second element
3905
     *                    contains the array of elements where the predicate returned FALSE.</p>
3906
     *
3907
     * @psalm-return array<int, static<TKey,T>>
3908
     */
3909 1
    public function partition(\Closure $closure): array
3910
    {
3911
        // init
3912 1
        $matches = [];
3913 1
        $noMatches = [];
3914
3915 1
        foreach ($this->array as $key => $value) {
3916 1
            if ($closure($value, $key)) {
3917 1
                $matches[$key] = $value;
3918
            } else {
3919 1
                $noMatches[$key] = $value;
3920
            }
3921
        }
3922
3923 1
        return [self::create($matches), self::create($noMatches)];
3924
    }
3925
3926
    /**
3927
     * Pop a specified value off the end of the current array.
3928
     *
3929
     * @return mixed|null
3930
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
3931
     */
3932 5
    public function pop()
3933
    {
3934 5
        $this->generatorToArray();
3935
3936 5
        return \array_pop($this->array);
3937
    }
3938
3939
    /**
3940
     * Prepend a (key) + value to the current array.
3941
     *
3942
     * @param mixed $value
3943
     * @param mixed $key
3944
     *
3945
     * @return $this
3946
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3947
     *
3948
     * @psalm-return static<TKey,T>
3949
     */
3950 11
    public function prepend($value, $key = null)
3951
    {
3952 11
        $this->generatorToArray();
3953
3954 11
        if ($this->properties !== []) {
3955 3
            $this->checkType($key, $value);
3956
        }
3957
3958 9
        if ($key === null) {
3959 8
            \array_unshift($this->array, $value);
3960
        } else {
3961 2
            $this->array = [$key => $value] + $this->array;
3962
        }
3963
3964 9
        return $this;
3965
    }
3966
3967
    /**
3968
     * Add a suffix to each key.
3969
     *
3970
     * @param mixed $suffix
3971
     *
3972
     * @return static
3973
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3974
     *
3975
     * @psalm-return static<TKey,T>
3976
     * @psalm-mutation-free
3977
     */
3978 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...
3979
    {
3980
        // init
3981 10
        $result = [];
3982
3983 10
        foreach ($this->getGenerator() as $key => $item) {
3984 9
            if ($item instanceof self) {
3985
                $result[$key] = $item->prependToEachKey($suffix);
3986 9
            } elseif (\is_array($item) === true) {
3987
                $result[$key] = self::create(
3988
                    $item,
3989
                    $this->iteratorClass,
3990
                    false
3991
                )->prependToEachKey($suffix)
3992
                    ->toArray();
3993
            } else {
3994 9
                $result[$key . $suffix] = $item;
3995
            }
3996
        }
3997
3998 10
        return self::create(
3999 10
            $result,
4000 10
            $this->iteratorClass,
4001 10
            false
4002
        );
4003
    }
4004
4005
    /**
4006
     * Add a suffix to each value.
4007
     *
4008
     * @param mixed $suffix
4009
     *
4010
     * @return static
4011
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4012
     *
4013
     * @psalm-return static<TKey,T>
4014
     * @psalm-mutation-free
4015
     */
4016 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...
4017
    {
4018
        // init
4019 10
        $result = [];
4020
4021 10
        foreach ($this->getGenerator() as $key => $item) {
4022 9
            if ($item instanceof self) {
4023
                $result[$key] = $item->prependToEachValue($suffix);
4024 9
            } elseif (\is_array($item) === true) {
4025
                $result[$key] = self::create(
4026
                    $item,
4027
                    $this->iteratorClass,
4028
                    false
4029
                )->prependToEachValue($suffix)
4030
                    ->toArray();
4031 9
            } elseif (\is_object($item) === true) {
4032 1
                $result[$key] = $item;
4033
            } else {
4034 9
                $result[$key] = $item . $suffix;
4035
            }
4036
        }
4037
4038 10
        return self::create(
4039 10
            $result,
4040 10
            $this->iteratorClass,
4041 10
            false
4042
        );
4043
    }
4044
4045
    /**
4046
     * Return the value of a given key and
4047
     * delete the key.
4048
     *
4049
     * @param int|int[]|string|string[]|null $keyOrKeys
4050
     * @param mixed                          $fallback
4051
     *
4052
     * @return mixed
4053
     */
4054 1
    public function pull($keyOrKeys = null, $fallback = null)
4055
    {
4056 1
        if ($keyOrKeys === null) {
4057
            $array = $this->toArray();
4058
            $this->clear();
4059
4060
            return $array;
4061
        }
4062
4063 1
        if (\is_array($keyOrKeys) === true) {
4064 1
            $valueOrValues = [];
4065 1
            foreach ($keyOrKeys as $key) {
4066 1
                $valueOrValues[] = $this->get($key, $fallback);
4067 1
                $this->offsetUnset($key);
4068
            }
4069
        } else {
4070 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4071 1
            $this->offsetUnset($keyOrKeys);
4072
        }
4073
4074 1
        return $valueOrValues;
4075
    }
4076
4077
    /**
4078
     * Push one or more values onto the end of array at once.
4079
     *
4080
     * @param array ...$args
4081
     *
4082
     * @return $this
4083
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4084
     *
4085
     * @noinspection ReturnTypeCanBeDeclaredInspection
4086
     *
4087
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4088
     * @psalm-return static<TKey,T>
4089
     */
4090 5
    public function push(...$args)
4091
    {
4092 5
        $this->generatorToArray();
4093
4094
        if (
4095 5
            $this->checkPropertyTypes
4096
            &&
4097 5
            $this->properties !== []
4098
        ) {
4099 1
            foreach ($args as $key => $value) {
4100 1
                $this->checkType($key, $value);
4101
            }
4102
        }
4103
4104 5
        \array_push($this->array, ...$args);
4105
4106 5
        return $this;
4107
    }
4108
4109
    /**
4110
     * Get a random value from the current array.
4111
     *
4112
     * @param int|null $number <p>How many values you will take?</p>
4113
     *
4114
     * @return static
4115
     *                <p>(Immutable)</p>
4116
     *
4117
     * @psalm-return static<int|array-key,T>
4118
     */
4119 19
    public function randomImmutable(int $number = null): self
4120
    {
4121 19
        $this->generatorToArray();
4122
4123 19
        if ($this->count() === 0) {
4124 1
            return static::create(
4125 1
                [],
4126 1
                $this->iteratorClass,
4127 1
                false
4128
            );
4129
        }
4130
4131 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...
4132
            /** @noinspection NonSecureArrayRandUsageInspection */
4133 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4134
4135 13
            return static::create(
4136 13
                $arrayRandValue,
4137 13
                $this->iteratorClass,
4138 13
                false
4139
            );
4140
        }
4141
4142 6
        $arrayTmp = $this->array;
4143
        /** @noinspection NonSecureShuffleUsageInspection */
4144 6
        \shuffle($arrayTmp);
4145
4146 6
        return static::create(
4147 6
            $arrayTmp,
4148 6
            $this->iteratorClass,
4149 6
            false
4150 6
        )->firstsImmutable($number);
4151
    }
4152
4153
    /**
4154
     * Pick a random key/index from the keys of this array.
4155
     *
4156
     * @throws \RangeException If array is empty
4157
     *
4158
     * @return mixed
4159
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4160
     */
4161 4
    public function randomKey()
4162
    {
4163 4
        $result = $this->randomKeys(1);
4164
4165 4
        if (!isset($result[0])) {
4166
            $result[0] = null;
4167
        }
4168
4169 4
        return $result[0];
4170
    }
4171
4172
    /**
4173
     * Pick a given number of random keys/indexes out of this array.
4174
     *
4175
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4176
     *
4177
     * @throws \RangeException If array is empty
4178
     *
4179
     * @return static
4180
     *                <p>(Immutable)</p>
4181
     *
4182
     * @psalm-return static<TKey,T>
4183
     */
4184 13
    public function randomKeys(int $number): self
4185
    {
4186 13
        $this->generatorToArray();
4187
4188 13
        $count = $this->count();
4189
4190
        if (
4191 13
            $number === 0
4192
            ||
4193 13
            $number > $count
4194
        ) {
4195 2
            throw new \RangeException(
4196 2
                \sprintf(
4197 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4198 2
                    $number,
4199 2
                    $count
4200
                )
4201
            );
4202
        }
4203
4204 11
        $result = (array) \array_rand($this->array, $number);
4205
4206 11
        return static::create(
4207 11
            $result,
4208 11
            $this->iteratorClass,
4209 11
            false
4210
        );
4211
    }
4212
4213
    /**
4214
     * Get a random value from the current array.
4215
     *
4216
     * @param int|null $number <p>How many values you will take?</p>
4217
     *
4218
     * @return $this
4219
     *               <p>(Mutable) Return this Arrayy object.</p>
4220
     *
4221
     * @psalm-return static<TKey,T>
4222
     */
4223 17
    public function randomMutable(int $number = null): self
4224
    {
4225 17
        $this->generatorToArray();
4226
4227 17
        if ($this->count() === 0) {
4228
            return static::create(
4229
                [],
4230
                $this->iteratorClass,
4231
                false
4232
            );
4233
        }
4234
4235 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...
4236
            /** @noinspection NonSecureArrayRandUsageInspection */
4237 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4238 7
            $this->array = $arrayRandValue;
4239
4240 7
            return $this;
4241
        }
4242
4243
        /** @noinspection NonSecureShuffleUsageInspection */
4244 11
        \shuffle($this->array);
4245
4246 11
        return $this->firstsMutable($number);
4247
    }
4248
4249
    /**
4250
     * Pick a random value from the values of this array.
4251
     *
4252
     * @return mixed
4253
     *               <p>Get a random value or null if there wasn't a value.</p>
4254
     */
4255 4
    public function randomValue()
4256
    {
4257 4
        $result = $this->randomImmutable();
4258
4259 4
        if (!isset($result[0])) {
4260
            $result[0] = null;
4261
        }
4262
4263 4
        return $result[0];
4264
    }
4265
4266
    /**
4267
     * Pick a given number of random values out of this array.
4268
     *
4269
     * @param int $number
4270
     *
4271
     * @return static
4272
     *                <p>(Mutable)</p>
4273
     *
4274
     * @psalm-return static<TKey,T>
4275
     */
4276 7
    public function randomValues(int $number): self
4277
    {
4278 7
        return $this->randomMutable($number);
4279
    }
4280
4281
    /**
4282
     * Get a random value from an array, with the ability to skew the results.
4283
     *
4284
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4285
     *
4286
     * @param array    $array
4287
     * @param int|null $number <p>How many values you will take?</p>
4288
     *
4289
     * @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...
4290
     *                           <p>(Immutable)</p>
4291
     *
4292
     * @psalm-param  array<mixed,mixed> $array
4293
     * @psalm-return static<int|array-key,T>
4294
     */
4295 9
    public function randomWeighted(array $array, int $number = null): self
4296
    {
4297
        // init
4298 9
        $options = [];
4299
4300 9
        foreach ($array as $option => $weight) {
4301 9
            if ($this->searchIndex($option) !== false) {
4302 9
                for ($i = 0; $i < $weight; ++$i) {
4303 1
                    $options[] = $option;
4304
                }
4305
            }
4306
        }
4307
4308 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4309
    }
4310
4311
    /**
4312
     * Reduce the current array via callable e.g. anonymous-function.
4313
     *
4314
     * @param callable $callable
4315
     * @param mixed    $init
4316
     *
4317
     * @return static
4318
     *                <p>(Immutable)</p>
4319
     *
4320
     * @psalm-return static<TKey,T>
4321
     * @psalm-mutation-free
4322
     */
4323 18
    public function reduce($callable, $init = []): self
4324
    {
4325 18
        if ($this->generator) {
4326 1
            $result = $init;
4327
4328 1
            foreach ($this->getGenerator() as $value) {
4329 1
                $result = $callable($result, $value);
4330
            }
4331
4332 1
            return static::create(
4333 1
                $result,
4334 1
                $this->iteratorClass,
4335 1
                false
4336
            );
4337
        }
4338
4339 18
        $result = \array_reduce($this->array, $callable, $init);
4340
4341 18
        if ($result === null) {
4342
            $this->array = [];
4343
        } else {
4344 18
            $this->array = (array) $result;
4345
        }
4346
4347 18
        return static::create(
4348 18
            $this->array,
4349 18
            $this->iteratorClass,
4350 18
            false
4351
        );
4352
    }
4353
4354
    /**
4355
     * @param bool $unique
4356
     *
4357
     * @return static
4358
     *                <p>(Immutable)</p>
4359
     *
4360
     * @psalm-return static<TKey,T>
4361
     * @psalm-mutation-free
4362
     */
4363 14
    public function reduce_dimension(bool $unique = true): self
4364
    {
4365
        // init
4366 14
        $result = [];
4367
4368 14
        foreach ($this->getGenerator() as $val) {
4369 12
            if (\is_array($val) === true) {
4370 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4371
            } else {
4372 12
                $result[] = [$val];
4373
            }
4374
        }
4375
4376 14
        $result = $result === [] ? [] : \array_merge(...$result);
4377
4378 14
        $resultArrayy = new static($result);
4379
4380
        /**
4381
         * @psalm-suppress ImpureMethodCall - object is already re-created
4382
         * @psalm-suppress InvalidReturnStatement - why?
4383
         */
4384 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4385
    }
4386
4387
    /**
4388
     * Create a numerically re-indexed Arrayy object.
4389
     *
4390
     * @return $this
4391
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4392
     *
4393
     * @psalm-return static<TKey,T>
4394
     */
4395 9
    public function reindex(): self
4396
    {
4397 9
        $this->generatorToArray(false);
4398
4399 9
        $this->array = \array_values($this->array);
4400
4401 9
        return $this;
4402
    }
4403
4404
    /**
4405
     * Return all items that fail the truth test.
4406
     *
4407
     * @param \Closure $closure
4408
     *
4409
     * @return static
4410
     *                <p>(Immutable)</p>
4411
     *
4412
     * @psalm-return static<TKey,T>
4413
     * @psalm-mutation-free
4414
     */
4415 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...
4416
    {
4417
        // init
4418 1
        $filtered = [];
4419
4420 1
        foreach ($this->getGenerator() as $key => $value) {
4421 1
            if (!$closure($value, $key)) {
4422 1
                $filtered[$key] = $value;
4423
            }
4424
        }
4425
4426 1
        return static::create(
4427 1
            $filtered,
4428 1
            $this->iteratorClass,
4429 1
            false
4430
        );
4431
    }
4432
4433
    /**
4434
     * Remove a value from the current array (optional using dot-notation).
4435
     *
4436
     * @param mixed $key
4437
     *
4438
     * @return static
4439
     *                <p>(Mutable)</p>
4440
     *
4441
     * @psalm-param  TKey $key
4442
     * @psalm-return static<TKey,T>
4443
     */
4444 18
    public function remove($key)
4445
    {
4446
        // recursive call
4447 18
        if (\is_array($key) === true) {
4448
            foreach ($key as $k) {
4449
                $this->internalRemove($k);
4450
            }
4451
4452
            return static::create(
4453
                $this->toArray(),
4454
                $this->iteratorClass,
4455
                false
4456
            );
4457
        }
4458
4459 18
        $this->internalRemove($key);
4460
4461 18
        return static::create(
4462 18
            $this->toArray(),
4463 18
            $this->iteratorClass,
4464 18
            false
4465
        );
4466
    }
4467
4468
    /**
4469
     * alias: for "Arrayy->removeValue()"
4470
     *
4471
     * @param mixed $element
4472
     *
4473
     * @return static
4474
     *                <p>(Immutable)</p>
4475
     *
4476
     * @psalm-param  T $element
4477
     * @psalm-return static<TKey,T>
4478
     * @psalm-mutation-free
4479
     */
4480 8
    public function removeElement($element)
4481
    {
4482 8
        return $this->removeValue($element);
4483
    }
4484
4485
    /**
4486
     * Remove the first value from the current array.
4487
     *
4488
     * @return static
4489
     *                <p>(Immutable)</p>
4490
     *
4491
     * @psalm-return static<TKey,T>
4492
     * @psalm-mutation-free
4493
     */
4494 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...
4495
    {
4496 7
        $tmpArray = $this->toArray();
4497
4498 7
        \array_shift($tmpArray);
4499
4500 7
        return static::create(
4501 7
            $tmpArray,
4502 7
            $this->iteratorClass,
4503 7
            false
4504
        );
4505
    }
4506
4507
    /**
4508
     * Remove the last value from the current array.
4509
     *
4510
     * @return static
4511
     *                <p>(Immutable)</p>
4512
     *
4513
     * @psalm-return static<TKey,T>
4514
     * @psalm-mutation-free
4515
     */
4516 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...
4517
    {
4518 7
        $tmpArray = $this->toArray();
4519
4520 7
        \array_pop($tmpArray);
4521
4522 7
        return static::create(
4523 7
            $tmpArray,
4524 7
            $this->iteratorClass,
4525 7
            false
4526
        );
4527
    }
4528
4529
    /**
4530
     * Removes a particular value from an array (numeric or associative).
4531
     *
4532
     * @param mixed $value
4533
     *
4534
     * @return static
4535
     *                <p>(Immutable)</p>
4536
     *
4537
     * @psalm-param  T $value
4538
     * @psalm-return static<TKey,T>
4539
     * @psalm-mutation-free
4540
     */
4541 8
    public function removeValue($value): self
4542
    {
4543 8
        $this->generatorToArray();
4544
4545
        // init
4546 8
        $isSequentialArray = $this->isSequential();
4547
4548 8
        foreach ($this->array as $key => $item) {
4549 7
            if ($item === $value) {
4550 7
                unset($this->array[$key]);
4551
            }
4552
        }
4553
4554 8
        if ($isSequentialArray) {
4555 6
            $this->array = \array_values($this->array);
4556
        }
4557
4558 8
        return static::create(
4559 8
            $this->array,
4560 8
            $this->iteratorClass,
4561 8
            false
4562
        );
4563
    }
4564
4565
    /**
4566
     * Generate array of repeated arrays.
4567
     *
4568
     * @param int $times <p>How many times has to be repeated.</p>
4569
     *
4570
     * @return static
4571
     *                <p>(Immutable)</p>
4572
     *
4573
     * @psalm-return static<TKey,T>
4574
     * @psalm-mutation-free
4575
     */
4576 1
    public function repeat($times): self
4577
    {
4578 1
        if ($times === 0) {
4579 1
            return static::create([], $this->iteratorClass);
4580
        }
4581
4582 1
        return static::create(
4583 1
            \array_fill(0, (int) $times, $this->toArray()),
4584 1
            $this->iteratorClass,
4585 1
            false
4586
        );
4587
    }
4588
4589
    /**
4590
     * Replace a key with a new key/value pair.
4591
     *
4592
     * @param mixed $replace
4593
     * @param mixed $key
4594
     * @param mixed $value
4595
     *
4596
     * @return static
4597
     *                <p>(Immutable)</p>
4598
     *
4599
     * @psalm-return static<TKey,T>
4600
     * @psalm-mutation-free
4601
     */
4602 2
    public function replace($replace, $key, $value): self
4603
    {
4604 2
        $that = clone $this;
4605
4606
        /**
4607
         * @psalm-suppress ImpureMethodCall - object is already cloned
4608
         */
4609 2
        return $that->remove($replace)
4610 2
            ->set($key, $value);
4611
    }
4612
4613
    /**
4614
     * Create an array using the current array as values and the other array as keys.
4615
     *
4616
     * @param array $keys <p>An array of keys.</p>
4617
     *
4618
     * @return static
4619
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4620
     *
4621
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4622
     * @psalm-return static<TKey,T>
4623
     * @psalm-mutation-free
4624
     */
4625 2
    public function replaceAllKeys(array $keys): self
4626
    {
4627 2
        return static::create(
4628 2
            \array_combine($keys, $this->toArray()),
4629 2
            $this->iteratorClass,
4630 2
            false
4631
        );
4632
    }
4633
4634
    /**
4635
     * Create an array using the current array as keys and the other array as values.
4636
     *
4637
     * @param array $array <p>An array o values.</p>
4638
     *
4639
     * @return static
4640
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4641
     *
4642
     * @psalm-param  array<mixed,T> $array
4643
     * @psalm-return static<TKey,T>
4644
     * @psalm-mutation-free
4645
     */
4646 2
    public function replaceAllValues(array $array): self
4647
    {
4648 2
        return static::create(
4649 2
            \array_combine($this->array, $array),
4650 2
            $this->iteratorClass,
4651 2
            false
4652
        );
4653
    }
4654
4655
    /**
4656
     * Replace the keys in an array with another set.
4657
     *
4658
     * @param array $keys <p>An array of keys matching the array's size</p>
4659
     *
4660
     * @return static
4661
     *                <p>(Immutable)</p>
4662
     *
4663
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4664
     * @psalm-return static<TKey,T>
4665
     * @psalm-mutation-free
4666
     */
4667 1
    public function replaceKeys(array $keys): self
4668
    {
4669 1
        $values = \array_values($this->toArray());
4670 1
        $result = \array_combine($keys, $values);
4671
4672 1
        return static::create(
4673 1
            $result,
4674 1
            $this->iteratorClass,
4675 1
            false
4676
        );
4677
    }
4678
4679
    /**
4680
     * Replace the first matched value in an array.
4681
     *
4682
     * @param mixed $search      <p>The value to replace.</p>
4683
     * @param mixed $replacement <p>The value to replace.</p>
4684
     *
4685
     * @return static
4686
     *                <p>(Immutable)</p>
4687
     *
4688
     * @psalm-return static<TKey,T>
4689
     * @psalm-mutation-free
4690
     */
4691 3
    public function replaceOneValue($search, $replacement = ''): self
4692
    {
4693 3
        $array = $this->toArray();
4694 3
        $key = \array_search($search, $array, true);
4695
4696 3
        if ($key !== false) {
4697 3
            $array[$key] = $replacement;
4698
        }
4699
4700 3
        return static::create(
4701 3
            $array,
4702 3
            $this->iteratorClass,
4703 3
            false
4704
        );
4705
    }
4706
4707
    /**
4708
     * Replace values in the current array.
4709
     *
4710
     * @param mixed $search      <p>The value to replace.</p>
4711
     * @param mixed $replacement <p>What to replace it with.</p>
4712
     *
4713
     * @return static
4714
     *                <p>(Immutable)</p>
4715
     *
4716
     * @psalm-return static<TKey,T>
4717
     * @psalm-mutation-free
4718
     */
4719 1
    public function replaceValues($search, $replacement = ''): self
4720
    {
4721
        /**
4722
         * @psalm-suppress MissingClosureReturnType
4723
         * @psalm-suppress MissingClosureParamType
4724
         */
4725 1
        return $this->each(
4726 1
            static function ($value) use ($search, $replacement) {
4727 1
                return \str_replace($search, $replacement, $value);
4728 1
            }
4729
        );
4730
    }
4731
4732
    /**
4733
     * Get the last elements from index $from until the end of this array.
4734
     *
4735
     * @param int $from
4736
     *
4737
     * @return static
4738
     *                <p>(Immutable)</p>
4739
     *
4740
     * @psalm-return static<TKey,T>
4741
     * @psalm-mutation-free
4742
     */
4743 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...
4744
    {
4745 15
        $tmpArray = $this->toArray();
4746
4747 15
        return static::create(
4748 15
            \array_splice($tmpArray, $from),
4749 15
            $this->iteratorClass,
4750 15
            false
4751
        );
4752
    }
4753
4754
    /**
4755
     * Return the array in the reverse order.
4756
     *
4757
     * @return $this
4758
     *               <p>(Mutable) Return this Arrayy object.</p>
4759
     *
4760
     * @psalm-return static<TKey,T>
4761
     */
4762 9
    public function reverse(): self
4763
    {
4764 9
        $this->generatorToArray();
4765
4766 9
        $this->array = \array_reverse($this->array);
4767
4768 9
        return $this;
4769
    }
4770
4771
    /**
4772
     * Sort an array in reverse order.
4773
     *
4774
     * @param int $sort_flags [optional] <p>
4775
     *                        You may modify the behavior of the sort using the optional
4776
     *                        parameter sort_flags, for details
4777
     *                        see sort.
4778
     *                        </p>
4779
     *
4780
     * @return $this
4781
     *               <p>(Mutable) Return this Arrayy object.</p>
4782
     *
4783
     * @psalm-return static<TKey,T>
4784
     */
4785 4
    public function rsort(int $sort_flags = 0): self
4786
    {
4787 4
        $this->generatorToArray();
4788
4789 4
        \rsort($this->array, $sort_flags);
4790
4791 4
        return $this;
4792
    }
4793
4794
    /**
4795
     * Sort an array in reverse order.
4796
     *
4797
     * @param int $sort_flags [optional] <p>
4798
     *                        You may modify the behavior of the sort using the optional
4799
     *                        parameter sort_flags, for details
4800
     *                        see sort.
4801
     *                        </p>
4802
     *
4803
     * @return $this
4804
     *               <p>(Immutable) Return this Arrayy object.</p>
4805
     *
4806
     * @psalm-return static<TKey,T>
4807
     * @psalm-mutation-free
4808
     */
4809 4
    public function rsortImmutable(int $sort_flags = 0): self
4810
    {
4811 4
        $that = clone $this;
4812
4813
        /**
4814
         * @psalm-suppress ImpureMethodCall - object is already cloned
4815
         */
4816 4
        $that->rsort($sort_flags);
4817
4818 4
        return $that;
4819
    }
4820
4821
    /**
4822
     * Search for the first index of the current array via $value.
4823
     *
4824
     * @param mixed $value
4825
     *
4826
     * @return false|float|int|string
4827
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4828
     * @psalm-mutation-free
4829
     */
4830 21
    public function searchIndex($value)
4831
    {
4832 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
4833 20
            if ($value === $valueFromArray) {
4834 20
                return $keyFromArray;
4835
            }
4836
        }
4837
4838 11
        return false;
4839
    }
4840
4841
    /**
4842
     * Search for the value of the current array via $index.
4843
     *
4844
     * @param mixed $index
4845
     *
4846
     * @return static
4847
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
4848
     *
4849
     * @psalm-return static<TKey,T>
4850
     * @psalm-mutation-free
4851
     */
4852 9
    public function searchValue($index): self
4853
    {
4854 9
        $this->generatorToArray();
4855
4856
        // init
4857 9
        $return = [];
4858
4859 9
        if ($this->array === []) {
4860
            return static::create(
4861
                [],
4862
                $this->iteratorClass,
4863
                false
4864
            );
4865
        }
4866
4867
        // php cast "bool"-index into "int"-index
4868 9
        if ((bool) $index === $index) {
4869 1
            $index = (int) $index;
4870
        }
4871
4872 9
        if ($this->offsetExists($index)) {
4873 7
            $return = [$this->array[$index]];
4874
        }
4875
4876 9
        return static::create(
4877 9
            $return,
4878 9
            $this->iteratorClass,
4879 9
            false
4880
        );
4881
    }
4882
4883
    /**
4884
     * Set a value for the current array (optional using dot-notation).
4885
     *
4886
     * @param string $key   <p>The key to set.</p>
4887
     * @param mixed  $value <p>Its value.</p>
4888
     *
4889
     * @return $this
4890
     *               <p>(Mutable) Return this Arrayy object.</p>
4891
     *
4892
     * @psalm-param  TKey $key
4893
     * @psalm-param  T $value
4894
     * @psalm-return static<TKey,T>
4895
     */
4896 18
    public function set($key, $value): self
4897
    {
4898 18
        $this->internalSet($key, $value);
4899
4900 18
        return $this;
4901
    }
4902
4903
    /**
4904
     * Get a value from a array and set it if it was not.
4905
     *
4906
     * WARNING: this method only set the value, if the $key is not already set
4907
     *
4908
     * @param mixed $key      <p>The key</p>
4909
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
4910
     *
4911
     * @return mixed
4912
     *               <p>(Mutable)</p>
4913
     */
4914 11
    public function setAndGet($key, $fallback = null)
4915
    {
4916 11
        $this->generatorToArray();
4917
4918
        // If the key doesn't exist, set it.
4919 11
        if (!$this->has($key)) {
4920 4
            $this->array = $this->set($key, $fallback)->toArray();
4921
        }
4922
4923 11
        return $this->get($key);
4924
    }
4925
4926
    /**
4927
     * Shifts a specified value off the beginning of array.
4928
     *
4929
     * @return mixed
4930
     *               <p>(Mutable) A shifted element from the current array.</p>
4931
     */
4932 5
    public function shift()
4933
    {
4934 5
        $this->generatorToArray();
4935
4936 5
        return \array_shift($this->array);
4937
    }
4938
4939
    /**
4940
     * Shuffle the current array.
4941
     *
4942
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
4943
     * @param array $array  [optional]
4944
     *
4945
     * @return static
4946
     *                <p>(Immutable)</p>
4947
     *
4948
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4949
     * @psalm-return static<TKey,T>
4950
     *
4951
     * @noinspection BadExceptionsProcessingInspection
4952
     * @noinspection RandomApiMigrationInspection
4953
     * @noinspection NonSecureShuffleUsageInspection
4954
     */
4955 2
    public function shuffle(bool $secure = false, array $array = null): self
4956
    {
4957 2
        if ($array === null) {
4958 2
            $array = $this->toArray(false);
4959
        }
4960
4961 2
        if ($secure !== true) {
4962 2
            \shuffle($array);
4963
        } else {
4964 1
            $size = \count($array, \COUNT_NORMAL);
4965 1
            $keys = \array_keys($array);
4966 1
            for ($i = $size - 1; $i > 0; --$i) {
4967
                try {
4968 1
                    $r = \random_int(0, $i);
4969
                } catch (\Exception $e) {
4970
                    $r = \mt_rand(0, $i);
4971
                }
4972 1
                if ($r !== $i) {
4973 1
                    $temp = $array[$keys[$r]];
4974 1
                    $array[$keys[$r]] = $array[$keys[$i]];
4975 1
                    $array[$keys[$i]] = $temp;
4976
                }
4977
            }
4978
        }
4979
4980 2
        foreach ($array as $key => $value) {
4981
            // check if recursive is needed
4982 2
            if (\is_array($value) === true) {
4983 2
                $array[$key] = $this->shuffle($secure, $value);
4984
            }
4985
        }
4986
4987 2
        return static::create(
4988 2
            $array,
4989 2
            $this->iteratorClass,
4990 2
            false
4991
        );
4992
    }
4993
4994
    /**
4995
     * Count the values from the current array.
4996
     *
4997
     * alias: for "Arrayy->count()"
4998
     *
4999
     * @param int $mode
5000
     *
5001
     * @return int
5002
     */
5003 20
    public function size(int $mode = \COUNT_NORMAL): int
5004
    {
5005 20
        return $this->count($mode);
5006
    }
5007
5008
    /**
5009
     * Checks whether array has exactly $size items.
5010
     *
5011
     * @param int $size
5012
     *
5013
     * @return bool
5014
     */
5015 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...
5016
    {
5017
        // init
5018 1
        $itemsTempCount = 0;
5019
5020 1
        foreach ($this->getGenerator() as $key => $value) {
5021 1
            ++$itemsTempCount;
5022 1
            if ($itemsTempCount > $size) {
5023 1
                return false;
5024
            }
5025
        }
5026
5027 1
        return $itemsTempCount === $size;
5028
    }
5029
5030
    /**
5031
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5032
     * smaller than $fromSize.
5033
     *
5034
     * @param int $fromSize
5035
     * @param int $toSize
5036
     *
5037
     * @return bool
5038
     */
5039 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5040
    {
5041 1
        if ($fromSize > $toSize) {
5042 1
            $tmp = $toSize;
5043 1
            $toSize = $fromSize;
5044 1
            $fromSize = $tmp;
5045
        }
5046
5047
        // init
5048 1
        $itemsTempCount = 0;
5049
5050 1
        foreach ($this->getGenerator() as $key => $value) {
5051 1
            ++$itemsTempCount;
5052 1
            if ($itemsTempCount > $toSize) {
5053 1
                return false;
5054
            }
5055
        }
5056
5057 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5058
    }
5059
5060
    /**
5061
     * Checks whether array has more than $size items.
5062
     *
5063
     * @param int $size
5064
     *
5065
     * @return bool
5066
     */
5067 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...
5068
    {
5069
        // init
5070 1
        $itemsTempCount = 0;
5071
5072 1
        foreach ($this->getGenerator() as $key => $value) {
5073 1
            ++$itemsTempCount;
5074 1
            if ($itemsTempCount > $size) {
5075 1
                return true;
5076
            }
5077
        }
5078
5079 1
        return $itemsTempCount > $size;
5080
    }
5081
5082
    /**
5083
     * Checks whether array has less than $size items.
5084
     *
5085
     * @param int $size
5086
     *
5087
     * @return bool
5088
     */
5089 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...
5090
    {
5091
        // init
5092 1
        $itemsTempCount = 0;
5093
5094 1
        foreach ($this->getGenerator() as $key => $value) {
5095 1
            ++$itemsTempCount;
5096 1
            if ($itemsTempCount > $size) {
5097 1
                return false;
5098
            }
5099
        }
5100
5101 1
        return $itemsTempCount < $size;
5102
    }
5103
5104
    /**
5105
     * Counts all elements in an array, or something in an object.
5106
     *
5107
     * <p>
5108
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5109
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5110
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5111
     * implemented and used in PHP.
5112
     * </p>
5113
     *
5114
     * @return int
5115
     *             <p>
5116
     *             The number of elements in var, which is
5117
     *             typically an array, since anything else will have one
5118
     *             element.
5119
     *             </p>
5120
     *             <p>
5121
     *             If var is not an array or an object with
5122
     *             implemented Countable interface,
5123
     *             1 will be returned.
5124
     *             There is one exception, if var is &null;,
5125
     *             0 will be returned.
5126
     *             </p>
5127
     *             <p>
5128
     *             Caution: count may return 0 for a variable that isn't set,
5129
     *             but it may also return 0 for a variable that has been initialized with an
5130
     *             empty array. Use isset to test if a variable is set.
5131
     *             </p>
5132
     */
5133 10
    public function sizeRecursive(): int
5134
    {
5135 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5136
    }
5137
5138
    /**
5139
     * Extract a slice of the array.
5140
     *
5141
     * @param int      $offset       <p>Slice begin index.</p>
5142
     * @param int|null $length       <p>Length of the slice.</p>
5143
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5144
     *
5145
     * @return static
5146
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5147
     *
5148
     * @psalm-return static<TKey,T>
5149
     * @psalm-mutation-free
5150
     */
5151 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5152
    {
5153 5
        return static::create(
5154 5
            \array_slice(
5155 5
                $this->toArray(),
5156 5
                $offset,
5157 5
                $length,
5158 5
                $preserveKeys
5159
            ),
5160 5
            $this->iteratorClass,
5161 5
            false
5162
        );
5163
    }
5164
5165
    /**
5166
     * Sort the current array and optional you can keep the keys.
5167
     *
5168
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5169
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5170
     *                              <strong>SORT_NATURAL</strong></p>
5171
     * @param bool       $keepKeys
5172
     *
5173
     * @return static
5174
     *                <p>(Mutable) Return this Arrayy object.</p>
5175
     *
5176
     * @psalm-return static<TKey,T>
5177
     */
5178 20
    public function sort(
5179
        $direction = \SORT_ASC,
5180
        int $strategy = \SORT_REGULAR,
5181
        bool $keepKeys = false
5182
    ): self {
5183 20
        $this->generatorToArray();
5184
5185 20
        return $this->sorting(
5186 20
            $this->array,
5187 20
            $direction,
5188 20
            $strategy,
5189 20
            $keepKeys
5190
        );
5191
    }
5192
5193
    /**
5194
     * Sort the current array and optional you can keep the keys.
5195
     *
5196
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5197
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5198
     *                              <strong>SORT_NATURAL</strong></p>
5199
     * @param bool       $keepKeys
5200
     *
5201
     * @return static
5202
     *                <p>(Immutable) Return this Arrayy object.</p>
5203
     *
5204
     * @psalm-return static<TKey,T>
5205
     */
5206 12
    public function sortImmutable(
5207
        $direction = \SORT_ASC,
5208
        int $strategy = \SORT_REGULAR,
5209
        bool $keepKeys = false
5210
    ): self {
5211 12
        $that = clone $this;
5212
5213 12
        $that->generatorToArray();
5214
5215 12
        return $that->sorting(
5216 12
            $that->array,
5217 12
            $direction,
5218 12
            $strategy,
5219 12
            $keepKeys
5220
        );
5221
    }
5222
5223
    /**
5224
     * Sort the current array by key.
5225
     *
5226
     * @see          http://php.net/manual/en/function.ksort.php
5227
     * @see          http://php.net/manual/en/function.krsort.php
5228
     *
5229
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5230
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5231
     *                              <strong>SORT_NATURAL</strong></p>
5232
     *
5233
     * @return $this
5234
     *               <p>(Mutable) Return this Arrayy object.</p>
5235
     *
5236
     * @psalm-return static<TKey,T>
5237
     */
5238 18
    public function sortKeys(
5239
        $direction = \SORT_ASC,
5240
        int $strategy = \SORT_REGULAR
5241
    ): self {
5242 18
        $this->generatorToArray();
5243
5244 18
        $this->sorterKeys($this->array, $direction, $strategy);
5245
5246 18
        return $this;
5247
    }
5248
5249
    /**
5250
     * Sort the current array by key.
5251
     *
5252
     * @see          http://php.net/manual/en/function.ksort.php
5253
     * @see          http://php.net/manual/en/function.krsort.php
5254
     *
5255
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5256
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5257
     *                              <strong>SORT_NATURAL</strong></p>
5258
     *
5259
     * @return $this
5260
     *               <p>(Immutable) Return this Arrayy object.</p>
5261
     *
5262
     * @psalm-return static<TKey,T>
5263
     * @psalm-mutation-free
5264
     */
5265 8
    public function sortKeysImmutable(
5266
        $direction = \SORT_ASC,
5267
        int $strategy = \SORT_REGULAR
5268
    ): self {
5269 8
        $that = clone $this;
5270
5271
        /**
5272
         * @psalm-suppress ImpureMethodCall - object is already cloned
5273
         */
5274 8
        $that->sortKeys($direction, $strategy);
5275
5276 8
        return $that;
5277
    }
5278
5279
    /**
5280
     * Sort the current array by value.
5281
     *
5282
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5283
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5284
     *                              <strong>SORT_NATURAL</strong></p>
5285
     *
5286
     * @return static
5287
     *                <p>(Mutable)</p>
5288
     *
5289
     * @psalm-return static<TKey,T>
5290
     */
5291 1
    public function sortValueKeepIndex(
5292
        $direction = \SORT_ASC,
5293
        int $strategy = \SORT_REGULAR
5294
    ): self {
5295 1
        return $this->sort($direction, $strategy, true);
5296
    }
5297
5298
    /**
5299
     * Sort the current array by value.
5300
     *
5301
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5302
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5303
     *                              <strong>SORT_NATURAL</strong></p>
5304
     *
5305
     * @return static
5306
     *                <p>(Mutable)</p>
5307
     *
5308
     * @psalm-return static<TKey,T>
5309
     */
5310 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5311
    {
5312 1
        return $this->sort($direction, $strategy, false);
5313
    }
5314
5315
    /**
5316
     * Sort a array by value, by a closure or by a property.
5317
     *
5318
     * - If the sorter is null, the array is sorted naturally.
5319
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5320
     *
5321
     * @param callable|string|null $sorter
5322
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5323
     *                                        <strong>SORT_DESC</strong></p>
5324
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5325
     *                                        <strong>SORT_NATURAL</strong></p>
5326
     *
5327
     * @return static
5328
     *                <p>(Immutable)</p>
5329
     *
5330
     * @psalm-return static<TKey,T>
5331
     * @psalm-mutation-free
5332
     */
5333 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5334
    {
5335 1
        $array = $this->toArray();
5336 1
        $direction = $this->getDirection($direction);
5337
5338
        // Transform all values into their results.
5339 1
        if ($sorter) {
5340 1
            $arrayy = static::create(
5341 1
                $array,
5342 1
                $this->iteratorClass,
5343 1
                false
5344
            );
5345
5346
            /**
5347
             * @psalm-suppress MissingClosureReturnType
5348
             * @psalm-suppress MissingClosureParamType
5349
             */
5350 1
            $results = $arrayy->each(
5351 1
                function ($value) use ($sorter) {
5352 1
                    if (\is_callable($sorter) === true) {
5353 1
                        return $sorter($value);
5354
                    }
5355
5356 1
                    return $this->get($sorter);
5357 1
                }
5358
            );
5359
5360 1
            $results = $results->toArray();
5361
        } else {
5362 1
            $results = $array;
5363
        }
5364
5365
        // Sort by the results and replace by original values
5366 1
        \array_multisort($results, $direction, $strategy, $array);
5367
5368 1
        return static::create(
5369 1
            $array,
5370 1
            $this->iteratorClass,
5371 1
            false
5372
        );
5373
    }
5374
5375
    /**
5376
     * @param int      $offset
5377
     * @param int|null $length
5378
     * @param array    $replacement
5379
     *
5380
     * @return static
5381
     *                <p>(Immutable)</p>
5382
     *
5383
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5384
     * @psalm-return static<TKey,T>
5385
     * @psalm-mutation-free
5386
     */
5387 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5388
    {
5389 1
        $tmpArray = $this->toArray();
5390
5391 1
        \array_splice(
5392 1
            $tmpArray,
5393 1
            $offset,
5394 1
            $length ?? $this->count(),
5395 1
            $replacement
5396
        );
5397
5398 1
        return static::create(
5399 1
            $tmpArray,
5400 1
            $this->iteratorClass,
5401 1
            false
5402
        );
5403
    }
5404
5405
    /**
5406
     * Split an array in the given amount of pieces.
5407
     *
5408
     * @param int  $numberOfPieces
5409
     * @param bool $keepKeys
5410
     *
5411
     * @return static
5412
     *                <p>(Immutable)</p>
5413
     *
5414
     * @psalm-return static<TKey,T>
5415
     * @psalm-mutation-free
5416
     */
5417 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5418
    {
5419 1
        $this->generatorToArray();
5420
5421 1
        $count = $this->count();
5422
5423 1
        if ($count === 0) {
5424 1
            $result = [];
5425
        } else {
5426 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5427 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5428
        }
5429
5430 1
        return static::create(
5431 1
            $result,
5432 1
            $this->iteratorClass,
5433 1
            false
5434
        );
5435
    }
5436
5437
    /**
5438
     * Stripe all empty items.
5439
     *
5440
     * @return static
5441
     *                <p>(Immutable)</p>
5442
     *
5443
     * @psalm-return static<TKey,T>
5444
     * @psalm-mutation-free
5445
     */
5446 1
    public function stripEmpty(): self
5447
    {
5448 1
        return $this->filter(
5449 1
            static function ($item) {
5450 1
                if ($item === null) {
5451 1
                    return false;
5452
                }
5453
5454 1
                return (bool) \trim((string) $item);
5455 1
            }
5456
        );
5457
    }
5458
5459
    /**
5460
     * Swap two values between positions by key.
5461
     *
5462
     * @param int|string $swapA <p>a key in the array</p>
5463
     * @param int|string $swapB <p>a key in the array</p>
5464
     *
5465
     * @return static
5466
     *                <p>(Immutable)</p>
5467
     *
5468
     * @psalm-return static<TKey,T>
5469
     * @psalm-mutation-free
5470
     */
5471 1
    public function swap($swapA, $swapB): self
5472
    {
5473 1
        $array = $this->toArray();
5474
5475 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5476
5477 1
        return static::create(
5478 1
            $array,
5479 1
            $this->iteratorClass,
5480 1
            false
5481
        );
5482
    }
5483
5484
    /**
5485
     * Get the current array from the "Arrayy"-object.
5486
     * alias for "getArray()"
5487
     *
5488
     * @param bool $convertAllArrayyElements <p>
5489
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5490
     *                                       </p>
5491
     * @param bool $preserveKeys             <p>
5492
     *                                       e.g.: A generator maybe return the same key more then once,
5493
     *                                       so maybe you will ignore the keys.
5494
     *                                       </p>
5495
     *
5496
     * @return array
5497
     *
5498
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5499
     * @psalm-mutation-free
5500
     */
5501 893
    public function toArray(
5502
        bool $convertAllArrayyElements = false,
5503
        bool $preserveKeys = true
5504
    ): array {
5505
        // init
5506 893
        $array = [];
5507
5508 893
        if ($convertAllArrayyElements) {
5509 2
            foreach ($this->getGenerator() as $key => $value) {
5510 2
                if ($value instanceof self) {
5511 1
                    $value = $value->toArray(true);
5512
                }
5513
5514 2
                if ($preserveKeys) {
5515 1
                    $array[$key] = $value;
5516
                } else {
5517 2
                    $array[] = $value;
5518
                }
5519
            }
5520
        } else {
5521 893
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5522
        }
5523
5524 893
        return $array;
5525
    }
5526
5527
    /**
5528
     * Get the current array from the "Arrayy"-object as list.
5529
     *
5530
     * @param bool $convertAllArrayyElements <p>
5531
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5532
     *                                       </p>
5533
     *
5534
     * @return array
5535
     *
5536
     * @psalm-return array<int,mixed>|array<int,T>
5537
     * @psalm-mutation-free
5538
     */
5539 1
    public function toList(bool $convertAllArrayyElements = false): array
5540
    {
5541 1
        return $this->toArray(
5542 1
            $convertAllArrayyElements,
5543 1
            false
5544
        );
5545
    }
5546
5547
    /**
5548
     * Convert the current array to JSON.
5549
     *
5550
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
5551
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
5552
     *
5553
     * @return string
5554
     */
5555 7
    public function toJson(int $options = 0, int $depth = 512): string
5556
    {
5557 7
        $return = \json_encode($this->toArray(), $options, $depth);
5558 7
        if ($return === false) {
5559
            return '';
5560
        }
5561
5562 7
        return $return;
5563
    }
5564
5565
    /**
5566
     * @param string[]|null $items  [optional]
5567
     * @param string[]      $helper [optional]
5568
     *
5569
     * @return static[]|static
5570
     *
5571
     * @psalm-return static<int, static<TKey,T>>
5572
     */
5573 1
    public function toPermutation(array $items = null, array $helper = []): self
5574
    {
5575
        // init
5576 1
        $return = [];
5577
5578 1
        if ($items === null) {
5579 1
            $items = $this->toArray();
5580
        }
5581
5582 1
        if (empty($items)) {
5583 1
            $return[] = $helper;
5584
        } else {
5585 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5586 1
                $new_items = $items;
5587 1
                $new_helper = $helper;
5588 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5589
                /** @noinspection PhpSillyAssignmentInspection */
5590
                /** @var string[] $new_items */
5591 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...
5592 1
                \array_unshift($new_helper, $tmp_helper);
5593
                /** @noinspection SlowArrayOperationsInLoopInspection */
5594 1
                $return = \array_merge(
5595 1
                    $return,
5596 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5597
                );
5598
            }
5599
        }
5600
5601 1
        return static::create(
5602 1
            $return,
5603 1
            $this->iteratorClass,
5604 1
            false
5605
        );
5606
    }
5607
5608
    /**
5609
     * Implodes array to a string with specified separator.
5610
     *
5611
     * @param string $separator [optional] <p>The element's separator.</p>
5612
     *
5613
     * @return string
5614
     *                <p>The string representation of array, separated by ",".</p>
5615
     */
5616 19
    public function toString(string $separator = ','): string
5617
    {
5618 19
        return $this->implode($separator);
5619
    }
5620
5621
    /**
5622
     * Return a duplicate free copy of the current array.
5623
     *
5624
     * @return $this
5625
     *               <p>(Mutable)</p>
5626
     *
5627
     * @psalm-return static<TKey,T>
5628
     */
5629 13
    public function unique(): self
5630
    {
5631
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5632
5633
        /**
5634
         * @psalm-suppress MissingClosureReturnType
5635
         * @psalm-suppress MissingClosureParamType
5636
         */
5637 13
        $this->array = $this->reduce(
5638 13
            static function ($resultArray, $value) {
5639 12
                if (!\in_array($value, $resultArray, true)) {
5640 12
                    $resultArray[] = $value;
5641
                }
5642
5643 12
                return $resultArray;
5644 13
            },
5645 13
            []
5646 13
        )->toArray();
5647 13
        $this->generator = null;
5648
5649 13
        return $this;
5650
    }
5651
5652
    /**
5653
     * Return a duplicate free copy of the current array. (with the old keys)
5654
     *
5655
     * @return $this
5656
     *               <p>(Mutable)</p>
5657
     *
5658
     * @psalm-return static<TKey,T>
5659
     */
5660 11
    public function uniqueKeepIndex(): self
5661
    {
5662
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5663
5664
        // init
5665 11
        $array = $this->toArray();
5666
5667
        /**
5668
         * @psalm-suppress MissingClosureReturnType
5669
         * @psalm-suppress MissingClosureParamType
5670
         */
5671 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...
5672 11
            \array_keys($array),
5673 11
            static function ($resultArray, $key) use ($array) {
5674 10
                if (!\in_array($array[$key], $resultArray, true)) {
5675 10
                    $resultArray[$key] = $array[$key];
5676
                }
5677
5678 10
                return $resultArray;
5679 11
            },
5680 11
            []
5681
        );
5682 11
        $this->generator = null;
5683
5684 11
        return $this;
5685
    }
5686
5687
    /**
5688
     * alias: for "Arrayy->unique()"
5689
     *
5690
     * @return static
5691
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
5692
     *
5693
     * @see          Arrayy::unique()
5694
     *
5695
     * @psalm-return static<TKey,T>
5696
     */
5697 10
    public function uniqueNewIndex(): self
5698
    {
5699 10
        return $this->unique();
5700
    }
5701
5702
    /**
5703
     * Prepends one or more values to the beginning of array at once.
5704
     *
5705
     * @param array ...$args
5706
     *
5707
     * @return $this
5708
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
5709
     *
5710
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
5711
     * @psalm-return static<TKey,T>
5712
     */
5713 4
    public function unshift(...$args): self
5714
    {
5715 4
        $this->generatorToArray();
5716
5717 4
        \array_unshift($this->array, ...$args);
5718
5719 4
        return $this;
5720
    }
5721
5722
    /**
5723
     * Tests whether the given closure return something valid for all elements of this array.
5724
     *
5725
     * @param \Closure $closure the predicate
5726
     *
5727
     * @return bool
5728
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
5729
     */
5730 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...
5731
    {
5732 1
        foreach ($this->getGenerator() as $key => $value) {
5733 1
            if (!$closure($value, $key)) {
5734 1
                return false;
5735
            }
5736
        }
5737
5738 1
        return true;
5739
    }
5740
5741
    /**
5742
     * Get all values from a array.
5743
     *
5744
     * @return static
5745
     *                <p>(Immutable)</p>
5746
     *
5747
     * @psalm-return static<TKey,T>
5748
     * @psalm-mutation-free
5749
     */
5750 2
    public function values(): self
5751
    {
5752 2
        return static::create(
5753 2
            function () {
5754
                /** @noinspection YieldFromCanBeUsedInspection */
5755 2
                foreach ($this->getGenerator() as $value) {
5756 2
                    yield $value;
5757
                }
5758 2
            },
5759 2
            $this->iteratorClass,
5760 2
            false
5761
        );
5762
    }
5763
5764
    /**
5765
     * Apply the given function to every element in the array, discarding the results.
5766
     *
5767
     * @param callable $callable
5768
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
5769
     *
5770
     * @return $this
5771
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
5772
     *
5773
     * @psalm-return static<TKey,T>
5774
     */
5775 12
    public function walk($callable, bool $recursive = false): self
5776
    {
5777 12
        $this->generatorToArray();
5778
5779 12
        if ($recursive === true) {
5780 6
            \array_walk_recursive($this->array, $callable);
5781
        } else {
5782 6
            \array_walk($this->array, $callable);
5783
        }
5784
5785 12
        return $this;
5786
    }
5787
5788
    /**
5789
     * Returns a collection of matching items.
5790
     *
5791
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
5792
     * @param mixed  $value                 the value to match
5793
     *
5794
     * @throws \InvalidArgumentException if property or method is not defined
5795
     *
5796
     * @return static
5797
     *
5798
     * @psalm-param  T $value
5799
     * @psalm-return static<TKey,T>
5800
     */
5801 1
    public function where(string $keyOrPropertyOrMethod, $value): self
5802
    {
5803 1
        return $this->filter(
5804 1
            function ($item) use ($keyOrPropertyOrMethod, $value) {
5805 1
                $accessorValue = $this->extractValue(
5806 1
                    $item,
5807 1
                    $keyOrPropertyOrMethod
5808
                );
5809
5810 1
                return $accessorValue === $value;
5811 1
            }
5812
        );
5813
    }
5814
5815
    /**
5816
     * Convert an array into a object.
5817
     *
5818
     * @param array $array
5819
     *
5820
     * @return \stdClass
5821
     *
5822
     * @psalm-param array<mixed,mixed> $array
5823
     */
5824 4
    final protected static function arrayToObject(array $array = []): \stdClass
5825
    {
5826
        // init
5827 4
        $object = new \stdClass();
5828
5829 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
5830 1
            return $object;
5831
        }
5832
5833 3
        foreach ($array as $name => $value) {
5834 3
            if (\is_array($value) === true) {
5835 1
                $object->{$name} = static::arrayToObject($value);
5836
            } else {
5837 3
                $object->{$name} = $value;
5838
            }
5839
        }
5840
5841 3
        return $object;
5842
    }
5843
5844
    /**
5845
     * @param array|\Generator|null $input         <p>
5846
     *                                             An array containing keys to return.
5847
     *                                             </p>
5848
     * @param mixed|null            $search_values [optional] <p>
5849
     *                                             If specified, then only keys containing these values are returned.
5850
     *                                             </p>
5851
     * @param bool                  $strict        [optional] <p>
5852
     *                                             Determines if strict comparison (===) should be used during the
5853
     *                                             search.
5854
     *                                             </p>
5855
     *
5856
     * @return array
5857
     *               <p>an array of all the keys in input</p>
5858
     *
5859
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
5860
     * @psalm-return array<TKey|mixed>
5861
     * @psalm-mutation-free
5862
     */
5863 11
    protected function array_keys_recursive(
5864
        $input = null,
5865
        $search_values = null,
5866
        bool $strict = true
5867
    ): array {
5868
        // init
5869 11
        $keys = [];
5870 11
        $keysTmp = [];
5871
5872 11
        if ($input === null) {
5873 4
            $input = $this->getGenerator();
5874
        }
5875
5876 11
        if ($search_values === null) {
5877 11
            foreach ($input as $key => $value) {
5878 11
                $keys[] = $key;
5879
5880
                // check if recursive is needed
5881 11
                if (\is_array($value) === true) {
5882 11
                    $keysTmp[] = $this->array_keys_recursive($value);
5883
                }
5884
            }
5885
        } else {
5886 1
            $is_array_tmp = \is_array($search_values);
5887
5888 1
            foreach ($input as $key => $value) {
5889 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...
5890
                    (
5891 1
                        $is_array_tmp === false
5892
                        &&
5893 1
                        $strict === true
5894
                        &&
5895 1
                        $search_values === $value
5896
                    )
5897
                    ||
5898
                    (
5899 1
                        $is_array_tmp === false
5900
                        &&
5901 1
                        $strict === false
5902
                        &&
5903 1
                        $search_values == $value
5904
                    )
5905
                    ||
5906
                    (
5907 1
                        $is_array_tmp === true
5908
                        &&
5909 1
                        \in_array($value, $search_values, $strict)
5910
                    )
5911
                ) {
5912 1
                    $keys[] = $key;
5913
                }
5914
5915
                // check if recursive is needed
5916 1
                if (\is_array($value) === true) {
5917 1
                    $keysTmp[] = $this->array_keys_recursive($value);
5918
                }
5919
            }
5920
        }
5921
5922 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
5923
    }
5924
5925
    /**
5926
     * @param mixed      $path
5927
     * @param callable   $callable
5928
     * @param array|null $currentOffset
5929
     *
5930
     * @return void
5931
     *
5932
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
5933
     * @psalm-mutation-free
5934
     */
5935 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
5936
    {
5937 4
        $this->generatorToArray();
5938
5939 4
        if ($currentOffset === null) {
5940 4
            $currentOffset = &$this->array;
5941
        }
5942
5943 4
        $explodedPath = \explode($this->pathSeparator, $path);
5944 4
        if ($explodedPath === false) {
5945
            return;
5946
        }
5947
5948 4
        $nextPath = \array_shift($explodedPath);
5949
5950 4
        if (!isset($currentOffset[$nextPath])) {
5951
            return;
5952
        }
5953
5954 4
        if (!empty($explodedPath)) {
5955 1
            $this->callAtPath(
5956 1
                \implode($this->pathSeparator, $explodedPath),
5957 1
                $callable,
5958 1
                $currentOffset[$nextPath]
5959
            );
5960
        } else {
5961 4
            $callable($currentOffset[$nextPath]);
5962
        }
5963 4
    }
5964
5965
    /**
5966
     * Extracts the value of the given property or method from the object.
5967
     *
5968
     * @param static $object                <p>The object to extract the value from.</p>
5969
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
5970
     *                                      value should be extracted.</p>
5971
     *
5972
     * @throws \InvalidArgumentException if the method or property is not defined
5973
     *
5974
     * @return mixed
5975
     *               <p>The value extracted from the specified property or method.</p>
5976
     *
5977
     * @psalm-param self<TKey,T> $object
5978
     */
5979 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
5980
    {
5981 2
        if (isset($object[$keyOrPropertyOrMethod])) {
5982 2
            $return = $object->get($keyOrPropertyOrMethod);
5983
5984 2
            if ($return instanceof self) {
5985 1
                return $return->toArray();
5986
            }
5987
5988 1
            return $return;
5989
        }
5990
5991
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
5992
            return $object->{$keyOrPropertyOrMethod};
5993
        }
5994
5995
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
5996
            return $object->{$keyOrPropertyOrMethod}();
5997
        }
5998
5999
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6000
    }
6001
6002
    /**
6003
     * create a fallback for array
6004
     *
6005
     * 1. use the current array, if it's a array
6006
     * 2. fallback to empty array, if there is nothing
6007
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6008
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6009
     * 5. call "__toArray()" on object, if the method exists
6010
     * 6. cast a string or object with "__toString()" into an array
6011
     * 7. throw a "InvalidArgumentException"-Exception
6012
     *
6013
     * @param mixed $data
6014
     *
6015
     * @throws \InvalidArgumentException
6016
     *
6017
     * @return array
6018
     *
6019
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6020
     */
6021 1100
    protected function fallbackForArray(&$data): array
6022
    {
6023 1100
        $data = $this->internalGetArray($data);
6024
6025 1100
        if ($data === null) {
6026 2
            throw new \InvalidArgumentException('Passed value should be a array');
6027
        }
6028
6029 1098
        return $data;
6030
    }
6031
6032
    /**
6033
     * @param bool $preserveKeys <p>
6034
     *                           e.g.: A generator maybe return the same key more then once,
6035
     *                           so maybe you will ignore the keys.
6036
     *                           </p>
6037
     *
6038
     * @return bool
6039
     *
6040
     * @noinspection ReturnTypeCanBeDeclaredInspection
6041
     * @psalm-mutation-free :/
6042
     */
6043 1014
    protected function generatorToArray(bool $preserveKeys = true)
6044
    {
6045 1014
        if ($this->generator) {
6046 2
            $this->array = $this->toArray(false, $preserveKeys);
6047 2
            $this->generator = null;
6048
6049 2
            return true;
6050
        }
6051
6052 1014
        return false;
6053
    }
6054
6055
    /**
6056
     * Get correct PHP constant for direction.
6057
     *
6058
     * @param int|string $direction
6059
     *
6060
     * @return int
6061
     * @psalm-mutation-free
6062
     */
6063 43
    protected function getDirection($direction): int
6064
    {
6065 43
        if ((string) $direction === $direction) {
6066 10
            $direction = \strtolower($direction);
6067
6068 10
            if ($direction === 'desc') {
6069 2
                $direction = \SORT_DESC;
6070
            } else {
6071 8
                $direction = \SORT_ASC;
6072
            }
6073
        }
6074
6075
        if (
6076 43
            $direction !== \SORT_DESC
6077
            &&
6078 43
            $direction !== \SORT_ASC
6079
        ) {
6080
            $direction = \SORT_ASC;
6081
        }
6082
6083 43
        return $direction;
6084
    }
6085
6086
    /**
6087
     * @return TypeCheckPhpDoc[]
6088
     *
6089
     * @noinspection ReturnTypeCanBeDeclaredInspection
6090
     */
6091 16
    protected function getPropertiesFromPhpDoc()
6092
    {
6093 16
        static $PROPERTY_CACHE = [];
6094 16
        $cacheKey = 'Class::' . static::class;
6095
6096 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6097 15
            return $PROPERTY_CACHE[$cacheKey];
6098
        }
6099
6100
        // init
6101 2
        $properties = [];
6102
6103 2
        $reflector = new \ReflectionClass($this);
6104 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6105 2
        $docComment = $reflector->getDocComment();
6106 2
        if ($docComment) {
6107 2
            $docblock = $factory->create($docComment);
6108
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6109 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6110 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
6111
            }
6112
        }
6113
6114 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6115
    }
6116
6117
    /**
6118
     * @param mixed $glue
6119
     * @param mixed $pieces
6120
     * @param bool  $useKeys
6121
     *
6122
     * @return string
6123
     * @psalm-mutation-free
6124
     */
6125 36
    protected function implode_recursive(
6126
        $glue = '',
6127
        $pieces = [],
6128
        bool $useKeys = false
6129
    ): string {
6130 36
        if ($pieces instanceof self) {
6131 1
            $pieces = $pieces->toArray();
6132
        }
6133
6134 36
        if (\is_array($pieces) === true) {
6135 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6136 36
            $pieces_count_not_zero = $pieces_count > 0;
6137
6138 36
            return \implode(
6139 36
                $glue,
6140 36
                \array_map(
6141 36
                    [$this, 'implode_recursive'],
6142 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6143 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6144
                )
6145
            );
6146
        }
6147
6148
        if (
6149 36
            \is_scalar($pieces) === true
6150
            ||
6151 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6152
        ) {
6153 32
            return (string) $pieces;
6154
        }
6155
6156 8
        return '';
6157
    }
6158
6159
    /**
6160
     * @param mixed                 $needle   <p>
6161
     *                                        The searched value.
6162
     *                                        </p>
6163
     *                                        <p>
6164
     *                                        If needle is a string, the comparison is done
6165
     *                                        in a case-sensitive manner.
6166
     *                                        </p>
6167
     * @param array|\Generator|null $haystack <p>
6168
     *                                        The array.
6169
     *                                        </p>
6170
     * @param bool                  $strict   [optional] <p>
6171
     *                                        If the third parameter strict is set to true
6172
     *                                        then the in_array function will also check the
6173
     *                                        types of the
6174
     *                                        needle in the haystack.
6175
     *                                        </p>
6176
     *
6177
     * @return bool
6178
     *              <p>true if needle is found in the array, false otherwise</p>
6179
     *
6180
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6181
     * @psalm-mutation-free
6182
     */
6183 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6184
    {
6185 18
        if ($haystack === null) {
6186
            $haystack = $this->getGenerator();
6187
        }
6188
6189 18
        foreach ($haystack as $item) {
6190 14
            if (\is_array($item) === true) {
6191 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6192
            } else {
6193
                /** @noinspection NestedPositiveIfStatementsInspection */
6194 14
                if ($strict === true) {
6195 14
                    $returnTmp = $item === $needle;
6196
                } else {
6197
                    $returnTmp = $item == $needle;
6198
                }
6199
            }
6200
6201 14
            if ($returnTmp === true) {
6202 14
                return true;
6203
            }
6204
        }
6205
6206 8
        return false;
6207
    }
6208
6209
    /**
6210
     * @param mixed $data
6211
     *
6212
     * @return array|null
6213
     *
6214
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6215
     */
6216 1100
    protected function internalGetArray(&$data)
6217
    {
6218 1100
        if (\is_array($data) === true) {
6219 1095
            return $data;
6220
        }
6221
6222 54
        if (!$data) {
6223 6
            return [];
6224
        }
6225
6226 53
        if (\is_object($data) === true) {
6227 48
            if ($data instanceof \ArrayObject) {
6228 4
                return $data->getArrayCopy();
6229
            }
6230
6231 45
            if ($data instanceof \Generator) {
6232
                return static::createFromGeneratorImmutable($data)->toArray();
6233
            }
6234
6235 45
            if ($data instanceof \Traversable) {
6236
                return static::createFromObject($data)->toArray();
6237
            }
6238
6239 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...
6240
                return (array) $data->jsonSerialize();
6241
            }
6242
6243 45
            if (\method_exists($data, '__toArray')) {
6244
                return (array) $data->__toArray();
6245
            }
6246
6247 45
            if (\method_exists($data, '__toString')) {
6248
                return [(string) $data];
6249
            }
6250
        }
6251
6252 50
        if (\is_callable($data)) {
6253
            /**
6254
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6255
             */
6256 43
            $this->generator = new ArrayyRewindableGenerator($data);
6257
6258 43
            return [];
6259
        }
6260
6261 9
        if (\is_scalar($data)) {
6262 7
            return [$data];
6263
        }
6264
6265 2
        return null;
6266
    }
6267
6268
    /**
6269
     * Internal mechanics of remove method.
6270
     *
6271
     * @param mixed $key
6272
     *
6273
     * @return bool
6274
     */
6275 18
    protected function internalRemove($key): bool
6276
    {
6277 18
        $this->generatorToArray();
6278
6279
        if (
6280 18
            $this->pathSeparator
6281
            &&
6282 18
            (string) $key === $key
6283
            &&
6284 18
            \strpos($key, $this->pathSeparator) !== false
6285
        ) {
6286
            $path = \explode($this->pathSeparator, (string) $key);
6287
6288
            if ($path !== false) {
6289
                // crawl though the keys
6290
                while (\count($path, \COUNT_NORMAL) > 1) {
6291
                    $key = \array_shift($path);
6292
6293
                    if (!$this->has($key)) {
6294
                        return false;
6295
                    }
6296
6297
                    $this->array = &$this->array[$key];
6298
                }
6299
6300
                $key = \array_shift($path);
6301
            }
6302
        }
6303
6304 18
        unset($this->array[$key]);
6305
6306 18
        return true;
6307
    }
6308
6309
    /**
6310
     * Internal mechanic of set method.
6311
     *
6312
     * @param int|string|null $key
6313
     * @param mixed           $value
6314
     * @param bool            $checkProperties
6315
     *
6316
     * @return bool
6317
     */
6318 962
    protected function internalSet(
6319
        $key,
6320
        &$value,
6321
        bool $checkProperties = true
6322
    ): bool {
6323
        if (
6324 962
            $checkProperties === true
6325
            &&
6326 962
            $this->properties !== []
6327
        ) {
6328 87
            $this->checkType($key, $value);
6329
        }
6330
6331 960
        if ($key === null) {
6332
            return false;
6333
        }
6334
6335 960
        $this->generatorToArray();
6336
6337
        /** @var array<int|string,mixed> $array */
6338 960
        $array = &$this->array;
6339
6340
        /**
6341
         * https://github.com/vimeo/psalm/issues/2536
6342
         *
6343
         * @psalm-suppress PossiblyInvalidArgument
6344
         * @psalm-suppress InvalidScalarArgument
6345
         */
6346
        if (
6347 960
            $this->pathSeparator
6348
            &&
6349 960
            (string) $key === $key
6350
            &&
6351 960
            \strpos($key, $this->pathSeparator) !== false
6352
        ) {
6353 3
            $path = \explode($this->pathSeparator, (string) $key);
6354
6355 3
            if ($path !== false) {
6356
                // crawl through the keys
6357 3
                while (\count($path, \COUNT_NORMAL) > 1) {
6358 3
                    $key = \array_shift($path);
6359
6360 3
                    $array = &$array[$key];
6361
                }
6362
6363 3
                $key = \array_shift($path);
6364
            }
6365
        }
6366
6367 960
        $array[$key] = $value;
6368
6369 960
        return true;
6370
    }
6371
6372
    /**
6373
     * Convert a object into an array.
6374
     *
6375
     * @param mixed|object $object
6376
     *
6377
     * @return array|mixed
6378
     *
6379
     * @psalm-mutation-free
6380
     */
6381 5
    protected static function objectToArray($object)
6382
    {
6383 5
        if (!\is_object($object)) {
6384 4
            return $object;
6385
        }
6386
6387 5
        $object = \get_object_vars($object);
6388
6389
        /**
6390
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6391
         */
6392 5
        return \array_map(['static', 'objectToArray'], $object);
6393
    }
6394
6395
    /**
6396
     * @param array $data
6397
     * @param bool  $checkPropertiesInConstructor
6398
     *
6399
     * @return void
6400
     *
6401
     * @psalm-param array<mixed,T> $data
6402
     */
6403 1098
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6404
    {
6405 1098
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6406
                                        &&
6407 1098
                                        $checkPropertiesInConstructor === true;
6408
6409 1098
        if ($this->properties !== []) {
6410 78
            foreach ($data as $key => &$valueInner) {
6411 78
                $this->internalSet(
6412 78
                    $key,
6413 78
                    $valueInner,
6414 78
                    $checkPropertiesInConstructor
6415
                );
6416
            }
6417
        } else {
6418
            if (
6419 1031
                $this->checkPropertyTypes === true
6420
                ||
6421 1031
                $checkPropertiesInConstructor === true
6422
            ) {
6423 16
                $this->properties = $this->getPropertiesFromPhpDoc();
6424
            }
6425
6426
            /** @var TypeCheckInterface[] $properties */
6427 1031
            $properties = $this->properties;
6428
6429
            if (
6430 1031
                $this->checkPropertiesMismatchInConstructor === true
6431
                &&
6432 1031
                \count($data) !== 0
6433
                &&
6434 1031
                \count(\array_diff_key($properties, $data)) > 0
6435
            ) {
6436 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...
6437
            }
6438
6439 1030
            foreach ($data as $key => &$valueInner) {
6440 888
                $this->internalSet(
6441 888
                    $key,
6442 888
                    $valueInner,
6443 888
                    $checkPropertiesInConstructor
6444
                );
6445
            }
6446
        }
6447 1091
    }
6448
6449
    /**
6450
     * sorting keys
6451
     *
6452
     * @param array      $elements
6453
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6454
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6455
     *                              <strong>SORT_NATURAL</strong></p>
6456
     *
6457
     * @return $this
6458
     *               <p>(Mutable) Return this Arrayy object.</p>
6459
     *
6460
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6461
     * @psalm-return static<TKey,T>
6462
     */
6463 18
    protected function sorterKeys(
6464
        array &$elements,
6465
        $direction = \SORT_ASC,
6466
        int $strategy = \SORT_REGULAR
6467
    ): self {
6468 18
        $direction = $this->getDirection($direction);
6469
6470
        switch ($direction) {
6471 18
            case 'desc':
6472 18
            case \SORT_DESC:
6473 6
                \krsort($elements, $strategy);
6474
6475 6
                break;
6476 13
            case 'asc':
6477 13
            case \SORT_ASC:
6478
            default:
6479 13
                \ksort($elements, $strategy);
6480
        }
6481
6482 18
        return $this;
6483
    }
6484
6485
    /**
6486
     * @param array      $elements  <p>Warning: used as reference</p>
6487
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6488
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6489
     *                              <strong>SORT_NATURAL</strong></p>
6490
     * @param bool       $keepKeys
6491
     *
6492
     * @return $this
6493
     *               <p>(Mutable) Return this Arrayy object.</p>
6494
     *
6495
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6496
     * @psalm-return static<TKey,T>
6497
     */
6498 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6499
    {
6500 24
        $direction = $this->getDirection($direction);
6501
6502 24
        if (!$strategy) {
6503 24
            $strategy = \SORT_REGULAR;
6504
        }
6505
6506
        switch ($direction) {
6507 24
            case 'desc':
6508 24
            case \SORT_DESC:
6509 13
                if ($keepKeys) {
6510 9
                    \arsort($elements, $strategy);
6511
                } else {
6512 4
                    \rsort($elements, $strategy);
6513
                }
6514
6515 13
                break;
6516 11
            case 'asc':
6517 11
            case \SORT_ASC:
6518
            default:
6519 11
                if ($keepKeys) {
6520 4
                    \asort($elements, $strategy);
6521
                } else {
6522 7
                    \sort($elements, $strategy);
6523
                }
6524
        }
6525
6526 24
        return $this;
6527
    }
6528
6529
    /**
6530
     * @param int|string|null $key
6531
     * @param mixed           $value
6532
     *
6533
     * @return void
6534
     */
6535 87
    private function checkType($key, $value)
6536
    {
6537
        if (
6538 87
            $key !== null
6539
            &&
6540 87
            isset($this->properties[$key]) === false
6541
            &&
6542 87
            $this->checkPropertiesMismatch === true
6543
        ) {
6544
            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...
6545
        }
6546
6547 87
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6548 76
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6549 17
        } elseif ($key !== null && isset($this->properties[$key])) {
6550 17
            $this->properties[$key]->checkType($value);
6551
        }
6552 85
    }
6553
}
6554