Completed
Push — master ( cfbff3...bc22cd )
by Lars
01:31
created

Arrayy::flip()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/** @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 1167
    public function __construct(
103
        $data = [],
104
        string $iteratorClass = ArrayyIterator::class,
105
        bool $checkPropertiesInConstructor = true
106
    ) {
107 1167
        $data = $this->fallbackForArray($data);
108
109
        // used only for serialize + unserialize, all other methods are overwritten
110
        /**
111
         * @psalm-suppress InvalidArgument - why?
112
         */
113 1165
        parent::__construct([], 0, $iteratorClass);
114
115 1165
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
116
117 1158
        $this->setIteratorClass($iteratorClass);
118 1158
    }
119
120
    /**
121
     * @return void
122
     */
123 50
    public function __clone()
124
    {
125 50
        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 50
        if ($this->generator !== null) {
130
            $this->generator = clone $this->generator;
131
        }
132 50
    }
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 4
    public function add($value)
231
    {
232 4
        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 16
    public function append($value, $key = null): self
247
    {
248 16
        $this->generatorToArray();
249
250 16
        if ($this->properties !== []) {
251 4
            $this->checkType($key, $value);
252
        }
253
254 15
        if ($key !== null) {
255
            if (
256 2
                isset($this->array[$key])
257
                &&
258 2
                \is_array($this->array[$key]) === true
259
            ) {
260
                $this->array[$key][] = $value;
261
            } else {
262 2
                $this->array[$key] = $value;
263
            }
264
        } else {
265 13
            $this->array[] = $value;
266
        }
267
268 15
        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 147
    public function count(int $mode = \COUNT_NORMAL): int
359
    {
360
        if (
361 147
            $this->generator
362
            &&
363 147
            $mode === \COUNT_NORMAL
364
        ) {
365 4
            return \iterator_count($this->generator);
366
        }
367
368 143
        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 6
    public function getArrayCopy(): array
396
    {
397 6
        $this->generatorToArray();
398
399 6
        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 26
    public function getIterator(): \Iterator
410
    {
411 26
        if ($this->generator instanceof ArrayyRewindableGenerator) {
412 1
            return $this->generator;
413
        }
414
415 25
        $iterator = $this->getIteratorClass();
416
417 25
        if ($iterator === ArrayyIterator::class) {
418 25
            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 25
    public function getIteratorClass(): string
435
    {
436 25
        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 135
    public function offsetExists($offset): bool
576
    {
577 135
        $this->generatorToArray();
578
579 135
        if ($this->array === []) {
580 7
            return false;
581
        }
582
583
        // php cast "bool"-index into "int"-index
584 129
        if ((bool) $offset === $offset) {
585 1
            $offset = (int) $offset;
586
        }
587
588
        /** @var int|string $offset - hint for phpstan */
589 129
        $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 129
        $tmpReturn = $this->keyExists($offset);
592
593
        if (
594 129
            $tmpReturn === true
595
            ||
596 129
            \strpos((string) $offset, $this->pathSeparator) === false
597
        ) {
598 126
            return $tmpReturn;
599
        }
600
601 4
        $offsetExists = false;
602
603
        /**
604
         * https://github.com/vimeo/psalm/issues/2536
605
         *
606
         * @psalm-suppress PossiblyInvalidArgument
607
         * @psalm-suppress InvalidScalarArgument
608
         */
609 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...
610 4
            $this->pathSeparator
611
            &&
612 4
            (string) $offset === $offset
613
            &&
614 4
            \strpos($offset, $this->pathSeparator) !== false
615
        ) {
616 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
617 4
            if ($explodedPath !== false) {
618
                /** @var string $lastOffset - helper for phpstan */
619 4
                $lastOffset = \array_pop($explodedPath);
620 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
621
622
                /**
623
                 * @psalm-suppress MissingClosureReturnType
624
                 * @psalm-suppress MissingClosureParamType
625
                 */
626 4
                $this->callAtPath(
627 4
                    $containerPath,
628
                    static function ($container) use ($lastOffset, &$offsetExists) {
629 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
630 4
                    }
631
                );
632
            }
633
        }
634
635 4
        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 104
    public function offsetGet($offset)
647
    {
648 104
        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 20
    public function offsetSet($offset, $value)
660
    {
661 20
        $this->generatorToArray();
662
663 20
        if ($offset === null) {
664 6
            if ($this->properties !== []) {
665 1
                $this->checkType(null, $value);
666
            }
667
668 5
            $this->array[] = $value;
669
        } else {
670 14
            $this->internalSet(
671 14
                $offset,
672 14
                $value,
673 14
                true
674
            );
675
        }
676 19
    }
677
678
    /**
679
     * Unset an offset.
680
     *
681
     * @param int|string $offset
682
     *
683
     * @return void
684
     *              <p>(Mutable) Return nothing.</p>
685
     */
686 25
    public function offsetUnset($offset)
687
    {
688 25
        $this->generatorToArray();
689
690 25
        if ($this->array === []) {
691 6
            return;
692
        }
693
694 20
        if ($this->keyExists($offset)) {
695 13
            unset($this->array[$offset]);
696
697 13
            return;
698
        }
699
700
        /**
701
         * https://github.com/vimeo/psalm/issues/2536
702
         *
703
         * @psalm-suppress PossiblyInvalidArgument
704
         * @psalm-suppress InvalidScalarArgument
705
         */
706 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...
707 10
            $this->pathSeparator
708
            &&
709 10
            (string) $offset === $offset
710
            &&
711 10
            \strpos($offset, $this->pathSeparator) !== false
712
        ) {
713 7
            $path = \explode($this->pathSeparator, (string) $offset);
714
715 7
            if ($path !== false) {
716 7
                $pathToUnset = \array_pop($path);
717
718
                /**
719
                 * @psalm-suppress MissingClosureReturnType
720
                 * @psalm-suppress MissingClosureParamType
721
                 */
722 7
                $this->callAtPath(
723 7
                    \implode($this->pathSeparator, $path),
724
                    static function (&$offset) use ($pathToUnset) {
725 6
                        if (\is_array($offset)) {
726 5
                            unset($offset[$pathToUnset]);
727
                        } else {
728 1
                            $offset = null;
729
                        }
730 7
                    }
731
                );
732
            }
733
        }
734
735 10
        unset($this->array[$offset]);
736 10
    }
737
738
    /**
739
     * Serialize the current "Arrayy"-object.
740
     *
741
     * @return string
742
     */
743 2
    public function serialize(): string
744
    {
745 2
        $this->generatorToArray();
746
747 2
        if (\PHP_VERSION_ID < 70400) {
748 2
            return parent::serialize();
749
        }
750
751
        return \serialize($this);
752
    }
753
754
    /**
755
     * Sets the iterator classname for the current "Arrayy"-object.
756
     *
757
     * @param string $iteratorClass
758
     *
759
     * @throws \InvalidArgumentException
760
     *
761
     * @return void
762
     *
763
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
764
     */
765 1158
    public function setIteratorClass($iteratorClass)
766
    {
767 1158
        if (\class_exists($iteratorClass)) {
768 1158
            $this->iteratorClass = $iteratorClass;
769
770 1158
            return;
771
        }
772
773
        if (\strpos($iteratorClass, '\\') === 0) {
774
            $iteratorClass = '\\' . $iteratorClass;
775
            if (\class_exists($iteratorClass)) {
776
                /**
777
                 * @psalm-suppress PropertyTypeCoercion
778
                 */
779
                $this->iteratorClass = $iteratorClass;
780
781
                return;
782
            }
783
        }
784
785
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
786
    }
787
788
    /**
789
     * Sort the entries with a user-defined comparison function and maintain key association.
790
     *
791
     * @param callable $function
792
     *
793
     * @throws \InvalidArgumentException
794
     *
795
     * @return $this
796
     *               <p>(Mutable) Return this Arrayy object.</p>
797
     *
798
     * @psalm-return static<TKey,T>
799
     */
800 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...
801
    {
802 8
        if (!\is_callable($function)) {
803
            throw new \InvalidArgumentException('Passed function must be callable');
804
        }
805
806 8
        $this->generatorToArray();
807
808 8
        \uasort($this->array, $function);
809
810 8
        return $this;
811
    }
812
813
    /**
814
     * Sort the entries with a user-defined comparison function and maintain key association.
815
     *
816
     * @param callable $function
817
     *
818
     * @throws \InvalidArgumentException
819
     *
820
     * @return $this
821
     *               <p>(Immutable) Return this Arrayy object.</p>
822
     *
823
     * @psalm-return static<TKey,T>
824
     * @psalm-mutation-free
825
     */
826 4
    public function uasortImmutable($function): self
827
    {
828 4
        $that = clone $this;
829
830
        /**
831
         * @psalm-suppress ImpureMethodCall - object is already cloned
832
         */
833 4
        $that->uasort($function);
834
835 4
        return $that;
836
    }
837
838
    /**
839
     * Sort the entries by keys using a user-defined comparison function.
840
     *
841
     * @param callable $function
842
     *
843
     * @throws \InvalidArgumentException
844
     *
845
     * @return static
846
     *                <p>(Mutable) Return this Arrayy object.</p>
847
     *
848
     * @psalm-return static<TKey,T>
849
     */
850 5
    public function uksort($function): self
851
    {
852 5
        return $this->customSortKeys($function);
853
    }
854
855
    /**
856
     * Sort the entries by keys using a user-defined comparison function.
857
     *
858
     * @param callable $function
859
     *
860
     * @throws \InvalidArgumentException
861
     *
862
     * @return static
863
     *                <p>(Immutable) Return this Arrayy object.</p>
864
     *
865
     * @psalm-return static<TKey,T>
866
     * @psalm-mutation-free
867
     */
868 1
    public function uksortImmutable($function): self
869
    {
870 1
        return $this->customSortKeysImmutable($function);
871
    }
872
873
    /**
874
     * Unserialize an string and return the instance of the "Arrayy"-class.
875
     *
876
     * @param string $string
877
     *
878
     * @return $this
879
     *
880
     * @psalm-return static<TKey,T>
881
     */
882 2
    public function unserialize($string): self
883
    {
884 2
        if (\PHP_VERSION_ID < 70400) {
885 2
            parent::unserialize($string);
886
887 2
            return $this;
888
        }
889
890
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
891
    }
892
893
    /**
894
     * Append a (key) + values to the current array.
895
     *
896
     * @param array $values
897
     * @param mixed $key
898
     *
899
     * @return $this
900
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
901
     *
902
     * @psalm-param  array<mixed,T> $values
903
     * @psalm-param  TKey|null $key
904
     * @psalm-return static<TKey,T>
905
     */
906 1
    public function appendArrayValues(array $values, $key = null)
907
    {
908 1
        $this->generatorToArray();
909
910 1
        if ($key !== null) {
911
            if (
912 1
                isset($this->array[$key])
913
                &&
914 1
                \is_array($this->array[$key]) === true
915
            ) {
916 1
                foreach ($values as $value) {
917 1
                    $this->array[$key][] = $value;
918
                }
919
            } else {
920 1
                foreach ($values as $value) {
921
                    $this->array[$key] = $value;
922
                }
923
            }
924
        } else {
925
            foreach ($values as $value) {
926
                $this->array[] = $value;
927
            }
928
        }
929
930 1
        return $this;
931
    }
932
933
    /**
934
     * Add a suffix to each key.
935
     *
936
     * @param mixed $prefix
937
     *
938
     * @return static
939
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
940
     *
941
     * @psalm-return static<TKey,T>
942
     * @psalm-mutation-free
943
     */
944 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...
945
    {
946
        // init
947 10
        $result = [];
948
949 10
        foreach ($this->getGenerator() as $key => $item) {
950 9
            if ($item instanceof self) {
951
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
952 9
            } elseif (\is_array($item) === true) {
953
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
954
                    ->appendToEachKey($prefix)
955
                    ->toArray();
956
            } else {
957 9
                $result[$prefix . $key] = $item;
958
            }
959
        }
960
961 10
        return self::create($result, $this->iteratorClass, false);
962
    }
963
964
    /**
965
     * Add a prefix to each value.
966
     *
967
     * @param mixed $prefix
968
     *
969
     * @return static
970
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
971
     *
972
     * @psalm-return static<TKey,T>
973
     * @psalm-mutation-free
974
     */
975 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...
976
    {
977
        // init
978 10
        $result = [];
979
980 10
        foreach ($this->getGenerator() as $key => $item) {
981 9
            if ($item instanceof self) {
982
                $result[$key] = $item->appendToEachValue($prefix);
983 9
            } elseif (\is_array($item) === true) {
984
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
985 9
            } elseif (\is_object($item) === true) {
986 1
                $result[$key] = $item;
987
            } else {
988 8
                $result[$key] = $prefix . $item;
989
            }
990
        }
991
992 10
        return self::create($result, $this->iteratorClass, false);
993
    }
994
995
    /**
996
     * Sort an array in reverse order and maintain index association.
997
     *
998
     * @return $this
999
     *               <p>(Mutable) Return this Arrayy object.</p>
1000
     *
1001
     * @psalm-return static<TKey,T>
1002
     */
1003 4
    public function arsort(): self
1004
    {
1005 4
        $this->generatorToArray();
1006
1007 4
        \arsort($this->array);
1008
1009 4
        return $this;
1010
    }
1011
1012
    /**
1013
     * Sort an array in reverse order and maintain index association.
1014
     *
1015
     * @return $this
1016
     *               <p>(Immutable) Return this Arrayy object.</p>
1017
     *
1018
     * @psalm-return static<TKey,T>
1019
     * @psalm-mutation-free
1020
     */
1021 10
    public function arsortImmutable(): self
1022
    {
1023 10
        $that = clone $this;
1024
1025 10
        $that->generatorToArray();
1026
1027 10
        \arsort($that->array);
1028
1029 10
        return $that;
1030
    }
1031
1032
    /**
1033
     * Iterate over the current array and execute a callback for each loop.
1034
     *
1035
     * @param \Closure $closure
1036
     *
1037
     * @return static
1038
     *                <p>(Immutable)</p>
1039
     *
1040
     * @psalm-return static<TKey,T>
1041
     * @psalm-mutation-free
1042
     */
1043 2
    public function at(\Closure $closure): self
1044
    {
1045 2
        $that = clone $this;
1046
1047 2
        foreach ($that->getGenerator() as $key => $value) {
1048 2
            $closure($value, $key);
1049
        }
1050
1051 2
        return static::create(
1052 2
            $that->toArray(),
1053 2
            $this->iteratorClass,
1054 2
            false
1055
        );
1056
    }
1057
1058
    /**
1059
     * Returns the average value of the current array.
1060
     *
1061
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1062
     *
1063
     * @return float|int
1064
     *                   <p>The average value.</p>
1065
     * @psalm-mutation-free
1066
     */
1067 10
    public function average($decimals = 0)
1068
    {
1069 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1070
1071 10
        if (!$count) {
1072 2
            return 0;
1073
        }
1074
1075 8
        if ((int) $decimals !== $decimals) {
1076 3
            $decimals = 0;
1077
        }
1078
1079 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1080
    }
1081
1082
    /**
1083
     * Changes all keys in an array.
1084
     *
1085
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1086
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1087
     *
1088
     * @return static
1089
     *                <p>(Immutable)</p>
1090
     *
1091
     * @psalm-return static<TKey,T>
1092
     * @psalm-mutation-free
1093
     */
1094 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1095
    {
1096
        if (
1097 1
            $case !== \CASE_LOWER
1098
            &&
1099 1
            $case !== \CASE_UPPER
1100
        ) {
1101
            $case = \CASE_LOWER;
1102
        }
1103
1104 1
        $return = [];
1105 1
        foreach ($this->getGenerator() as $key => $value) {
1106 1
            if ($case === \CASE_LOWER) {
1107 1
                $key = \mb_strtolower((string) $key);
1108
            } else {
1109 1
                $key = \mb_strtoupper((string) $key);
1110
            }
1111
1112 1
            $return[$key] = $value;
1113
        }
1114
1115 1
        return static::create(
1116 1
            $return,
1117 1
            $this->iteratorClass,
1118 1
            false
1119
        );
1120
    }
1121
1122
    /**
1123
     * Change the path separator of the array wrapper.
1124
     *
1125
     * By default, the separator is: "."
1126
     *
1127
     * @param string $separator <p>Separator to set.</p>
1128
     *
1129
     * @return $this
1130
     *               <p>(Mutable) Return this Arrayy object.</p>
1131
     *
1132
     * @psalm-return static<TKey,T>
1133
     */
1134 11
    public function changeSeparator($separator): self
1135
    {
1136 11
        $this->pathSeparator = $separator;
1137
1138 11
        return $this;
1139
    }
1140
1141
    /**
1142
     * Create a chunked version of the current array.
1143
     *
1144
     * @param int  $size         <p>Size of each chunk.</p>
1145
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1146
     *
1147
     * @return static
1148
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1149
     *
1150
     * @psalm-return static<TKey,T>
1151
     * @psalm-mutation-free
1152
     */
1153 5
    public function chunk($size, $preserveKeys = false): self
1154
    {
1155 5
        return static::create(
1156 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1157 5
            $this->iteratorClass,
1158 5
            false
1159
        );
1160
    }
1161
1162
    /**
1163
     * Clean all falsy values from the current array.
1164
     *
1165
     * @return static
1166
     *                <p>(Immutable)</p>
1167
     *
1168
     * @psalm-return static<TKey,T>
1169
     * @psalm-mutation-free
1170
     */
1171 8
    public function clean(): self
1172
    {
1173 8
        return $this->filter(
1174
            static function ($value) {
1175 7
                return (bool) $value;
1176 8
            }
1177
        );
1178
    }
1179
1180
    /**
1181
     * WARNING!!! -> Clear the current full array or a $key of it.
1182
     *
1183
     * @param int|int[]|string|string[]|null $key
1184
     *
1185
     * @return $this
1186
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1187
     *
1188
     * @psalm-return static<TKey,T>
1189
     */
1190 10
    public function clear($key = null): self
1191
    {
1192 10
        if ($key !== null) {
1193 3
            if (\is_array($key)) {
1194 1
                foreach ($key as $keyTmp) {
1195 1
                    $this->offsetUnset($keyTmp);
1196
                }
1197
            } else {
1198 2
                $this->offsetUnset($key);
1199
            }
1200
1201 3
            return $this;
1202
        }
1203
1204 7
        $this->array = [];
1205 7
        $this->generator = null;
1206
1207 7
        return $this;
1208
    }
1209
1210
    /**
1211
     * Check if an item is in the current array.
1212
     *
1213
     * @param float|int|string $value
1214
     * @param bool             $recursive
1215
     * @param bool             $strict
1216
     *
1217
     * @return bool
1218
     * @psalm-mutation-free
1219
     */
1220 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1221
    {
1222 23
        if ($recursive === true) {
1223 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1224
        }
1225
1226 14
        foreach ($this->getGenerator() as $valueFromArray) {
1227 11
            if ($strict) {
1228 11
                if ($value === $valueFromArray) {
1229 11
                    return true;
1230
                }
1231
            } else {
1232
                /** @noinspection NestedPositiveIfStatementsInspection */
1233
                if ($value == $valueFromArray) {
1234
                    return true;
1235
                }
1236
            }
1237
        }
1238
1239 7
        return false;
1240
    }
1241
1242
    /**
1243
     * Check if an (case-insensitive) string is in the current array.
1244
     *
1245
     * @param mixed $value
1246
     * @param bool  $recursive
1247
     *
1248
     * @return bool
1249
     * @psalm-mutation-free
1250
     *
1251
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1252
     */
1253 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1254
    {
1255 26
        if ($value === null) {
1256 2
            return false;
1257
        }
1258
1259 24
        if ($recursive === true) {
1260 24
            foreach ($this->getGenerator() as $key => $valueTmp) {
1261 22
                if (\is_array($valueTmp) === true) {
1262 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1263 5
                    if ($return === true) {
1264 5
                        return $return;
1265
                    }
1266 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1267 16
                    return true;
1268
                }
1269
            }
1270
1271 8
            return false;
1272
        }
1273
1274 12
        foreach ($this->getGenerator() as $key => $valueTmp) {
1275 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1276 8
                return true;
1277
            }
1278
        }
1279
1280 4
        return false;
1281
    }
1282
1283
    /**
1284
     * Check if the given key/index exists in the array.
1285
     *
1286
     * @param int|string $key <p>key/index to search for</p>
1287
     *
1288
     * @return bool
1289
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1290
     *
1291
     * @psalm-mutation-free
1292
     */
1293 4
    public function containsKey($key): bool
1294
    {
1295 4
        return $this->offsetExists($key);
1296
    }
1297
1298
    /**
1299
     * Check if all given needles are present in the array as key/index.
1300
     *
1301
     * @param array $needles   <p>The keys you are searching for.</p>
1302
     * @param bool  $recursive
1303
     *
1304
     * @return bool
1305
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1306
     *
1307
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1308
     * @psalm-mutation-free
1309
     */
1310 2
    public function containsKeys(array $needles, $recursive = false): bool
1311
    {
1312 2
        if ($recursive === true) {
1313
            return
1314 2
                \count(
1315 2
                    \array_intersect(
1316 2
                        $needles,
1317 2
                        $this->keys(true)->toArray()
1318
                    ),
1319 2
                    \COUNT_RECURSIVE
1320
                )
1321
                ===
1322 2
                \count(
1323 2
                    $needles,
1324 2
                    \COUNT_RECURSIVE
1325
                );
1326
        }
1327
1328 1
        return \count(
1329 1
            \array_intersect($needles, $this->keys()->toArray()),
1330 1
            \COUNT_NORMAL
1331
        )
1332
               ===
1333 1
               \count(
1334 1
                   $needles,
1335 1
                   \COUNT_NORMAL
1336
               );
1337
    }
1338
1339
    /**
1340
     * Check if all given needles are present in the array as key/index.
1341
     *
1342
     * @param array $needles <p>The keys you are searching for.</p>
1343
     *
1344
     * @return bool
1345
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1346
     *
1347
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1348
     * @psalm-mutation-free
1349
     */
1350 1
    public function containsKeysRecursive(array $needles): bool
1351
    {
1352 1
        return $this->containsKeys($needles, true);
1353
    }
1354
1355
    /**
1356
     * alias: for "Arrayy->contains()"
1357
     *
1358
     * @param float|int|string $value
1359
     *
1360
     * @return bool
1361
     *
1362
     * @see Arrayy::contains()
1363
     * @psalm-mutation-free
1364
     */
1365 9
    public function containsValue($value): bool
1366
    {
1367 9
        return $this->contains($value);
1368
    }
1369
1370
    /**
1371
     * alias: for "Arrayy->contains($value, true)"
1372
     *
1373
     * @param float|int|string $value
1374
     *
1375
     * @return bool
1376
     *
1377
     * @see Arrayy::contains()
1378
     * @psalm-mutation-free
1379
     */
1380 18
    public function containsValueRecursive($value): bool
1381
    {
1382 18
        return $this->contains($value, true);
1383
    }
1384
1385
    /**
1386
     * Check if all given needles are present in the array.
1387
     *
1388
     * @param array $needles
1389
     *
1390
     * @return bool
1391
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1392
     *
1393
     * @psalm-param array<mixed>|array<T> $needles
1394
     * @psalm-mutation-free
1395
     */
1396 1
    public function containsValues(array $needles): bool
1397
    {
1398 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1399
               ===
1400 1
               \count($needles, \COUNT_NORMAL);
1401
    }
1402
1403
    /**
1404
     * Counts all the values of an array
1405
     *
1406
     * @see          http://php.net/manual/en/function.array-count-values.php
1407
     *
1408
     * @return static
1409
     *                <p>
1410
     *                (Immutable)
1411
     *                An associative Arrayy-object of values from input as
1412
     *                keys and their count as value.
1413
     *                </p>
1414
     *
1415
     * @psalm-return static<TKey,T>
1416
     * @psalm-mutation-free
1417
     */
1418 7
    public function countValues(): self
1419
    {
1420 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1421
    }
1422
1423
    /**
1424
     * Creates an Arrayy object.
1425
     *
1426
     * @param mixed  $data
1427
     * @param string $iteratorClass
1428
     * @param bool   $checkPropertiesInConstructor
1429
     *
1430
     * @return static
1431
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1432
     *
1433
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1434
     *
1435
     * @psalm-mutation-free
1436
     */
1437 690
    public static function create(
1438
        $data = [],
1439
        string $iteratorClass = ArrayyIterator::class,
1440
        bool $checkPropertiesInConstructor = true
1441
    ) {
1442 690
        return new static(
1443 690
            $data,
1444 690
            $iteratorClass,
1445 690
            $checkPropertiesInConstructor
1446
        );
1447
    }
1448
1449
    /**
1450
     * Flatten an array with the given character as a key delimiter
1451
     *
1452
     * @param string     $delimiter
1453
     * @param string     $prepend
1454
     * @param array|null $items
1455
     *
1456
     * @return array
1457
     */
1458 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1459
    {
1460
        // init
1461 2
        $flatten = [];
1462
1463 2
        if ($items === null) {
1464 2
            $items = $this->array;
1465
        }
1466
1467 2
        foreach ($items as $key => $value) {
1468 2
            if (\is_array($value) && !empty($value)) {
1469 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1470
            } else {
1471 2
                $flatten[] = [$prepend . $key => $value];
1472
            }
1473
        }
1474
1475 2
        if (\count($flatten) === 0) {
1476
            return [];
1477
        }
1478
1479 2
        return \array_merge_recursive([], ...$flatten);
1480
    }
1481
1482
    /**
1483
     * WARNING: Creates an Arrayy object by reference.
1484
     *
1485
     * @param array $array
1486
     *
1487
     * @return $this
1488
     *               <p>(Mutable) Return this Arrayy object.</p>
1489
     *
1490
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1491
     */
1492 2
    public function createByReference(array &$array = []): self
1493
    {
1494 2
        $array = $this->fallbackForArray($array);
1495
1496 2
        $this->array = &$array;
1497
1498 2
        return $this;
1499
    }
1500
1501
    /**
1502
     * Create an new instance from a callable function which will return an Generator.
1503
     *
1504
     * @param callable $generatorFunction
1505
     *
1506
     * @return static
1507
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1508
     *
1509
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1510
     *
1511
     * @psalm-mutation-free
1512
     */
1513 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1514
    {
1515 7
        return self::create($generatorFunction);
1516
    }
1517
1518
    /**
1519
     * Create an new instance filled with a copy of values from a "Generator"-object.
1520
     *
1521
     * @param \Generator $generator
1522
     *
1523
     * @return static
1524
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1525
     *
1526
     * @psalm-param \Generator<array-key,mixed> $generator
1527
     *
1528
     * @psalm-mutation-free
1529
     */
1530 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1531
    {
1532 4
        return self::create(\iterator_to_array($generator, true));
1533
    }
1534
1535
    /**
1536
     * Create an new Arrayy object via JSON.
1537
     *
1538
     * @param string $json
1539
     *
1540
     * @return static
1541
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1542
     *
1543
     * @psalm-mutation-free
1544
     */
1545 5
    public static function createFromJson(string $json): self
1546
    {
1547 5
        return static::create(\json_decode($json, true));
1548
    }
1549
1550
    /**
1551
     * Create an new Arrayy object via JSON.
1552
     *
1553
     * @param array $array
1554
     *
1555
     * @return static
1556
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1557
     *
1558
     * @psalm-mutation-free
1559
     */
1560 1
    public static function createFromArray(array $array): self
1561
    {
1562 1
        return static::create($array);
1563
    }
1564
1565
    /**
1566
     * Create an new instance filled with values from an object that is iterable.
1567
     *
1568
     * @param \Traversable $object <p>iterable object</p>
1569
     *
1570
     * @return static
1571
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1572
     *
1573
     * @psalm-param \Traversable<array-key,mixed> $object
1574
     *
1575
     * @psalm-mutation-free
1576
     */
1577 4
    public static function createFromObject(\Traversable $object): self
1578
    {
1579
        // init
1580 4
        $arrayy = new static();
1581
1582 4
        if ($object instanceof self) {
1583 4
            $objectArray = $object->getGenerator();
1584
        } else {
1585
            $objectArray = $object;
1586
        }
1587
1588 4
        foreach ($objectArray as $key => $value) {
1589
            /**
1590
             * @psalm-suppress ImpureMethodCall - object is already re-created
1591
             */
1592 3
            $arrayy->internalSet($key, $value);
1593
        }
1594
1595 4
        return $arrayy;
1596
    }
1597
1598
    /**
1599
     * Create an new instance filled with values from an object.
1600
     *
1601
     * @param object $object
1602
     *
1603
     * @return static
1604
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1605
     *
1606
     * @psalm-mutation-free
1607
     */
1608 5
    public static function createFromObjectVars($object): self
1609
    {
1610 5
        return self::create(self::objectToArray($object));
1611
    }
1612
1613
    /**
1614
     * Create an new Arrayy object via string.
1615
     *
1616
     * @param string      $str       <p>The input string.</p>
1617
     * @param string|null $delimiter <p>The boundary string.</p>
1618
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1619
     *                               used.</p>
1620
     *
1621
     * @return static
1622
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1623
     *
1624
     * @psalm-mutation-free
1625
     */
1626 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1627
    {
1628 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...
1629 1
            \preg_match_all($regEx, $str, $array);
1630
1631 1
            if (!empty($array)) {
1632 1
                $array = $array[0];
1633
            }
1634
        } else {
1635
            /** @noinspection NestedPositiveIfStatementsInspection */
1636 9
            if ($delimiter !== null) {
1637 7
                $array = \explode($delimiter, $str);
1638
            } else {
1639 2
                $array = [$str];
1640
            }
1641
        }
1642
1643
        // trim all string in the array
1644
        /**
1645
         * @psalm-suppress MissingClosureParamType
1646
         */
1647 10
        \array_walk(
1648 10
            $array,
1649
            static function (&$val) {
1650 10
                if ((string) $val === $val) {
1651 10
                    $val = \trim($val);
1652
                }
1653 10
            }
1654
        );
1655
1656 10
        return static::create($array);
1657
    }
1658
1659
    /**
1660
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1661
     *
1662
     * @param \Traversable $traversable
1663
     *
1664
     * @return static
1665
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1666
     *
1667
     * @psalm-param \Traversable<array-key,mixed> $traversable
1668
     *
1669
     * @psalm-mutation-free
1670
     */
1671 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1672
    {
1673 1
        return self::create(\iterator_to_array($traversable, true));
1674
    }
1675
1676
    /**
1677
     * Create an new instance containing a range of elements.
1678
     *
1679
     * @param float|int|string $low  <p>First value of the sequence.</p>
1680
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1681
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1682
     *
1683
     * @return static
1684
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1685
     *
1686
     * @psalm-mutation-free
1687
     */
1688 2
    public static function createWithRange($low, $high, $step = 1): self
1689
    {
1690 2
        return static::create(\range($low, $high, $step));
1691
    }
1692
1693
    /**
1694
     * Gets the element of the array at the current internal iterator position.
1695
     *
1696
     * @return false|mixed
1697
     */
1698
    public function current()
1699
    {
1700
        return \current($this->array);
1701
    }
1702
1703
    /**
1704
     * Custom sort by index via "uksort".
1705
     *
1706
     * @see          http://php.net/manual/en/function.uksort.php
1707
     *
1708
     * @param callable $function
1709
     *
1710
     * @throws \InvalidArgumentException
1711
     *
1712
     * @return $this
1713
     *               <p>(Mutable) Return this Arrayy object.</p>
1714
     *
1715
     * @psalm-return static<TKey,T>
1716
     */
1717 5
    public function customSortKeys(callable $function): self
1718
    {
1719 5
        $this->generatorToArray();
1720
1721 5
        \uksort($this->array, $function);
1722
1723 5
        return $this;
1724
    }
1725
1726
    /**
1727
     * Custom sort by index via "uksort".
1728
     *
1729
     * @see          http://php.net/manual/en/function.uksort.php
1730
     *
1731
     * @param callable $function
1732
     *
1733
     * @throws \InvalidArgumentException
1734
     *
1735
     * @return $this
1736
     *               <p>(Immutable) Return this Arrayy object.</p>
1737
     *
1738
     * @psalm-return static<TKey,T>
1739
     * @psalm-mutation-free
1740
     */
1741 1
    public function customSortKeysImmutable(callable $function): self
1742
    {
1743 1
        $that = clone $this;
1744
1745 1
        $that->generatorToArray();
1746
1747
        /**
1748
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1749
         */
1750 1
        \uksort($that->array, $function);
1751
1752 1
        return $that;
1753
    }
1754
1755
    /**
1756
     * Custom sort by value via "usort".
1757
     *
1758
     * @see          http://php.net/manual/en/function.usort.php
1759
     *
1760
     * @param callable $function
1761
     *
1762
     * @throws \InvalidArgumentException
1763
     *
1764
     * @return $this
1765
     *               <p>(Mutable) Return this Arrayy object.</p>
1766
     *
1767
     * @psalm-return static<TKey,T>
1768
     */
1769 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...
1770
    {
1771 10
        if (\is_callable($function) === false) {
1772
            throw new \InvalidArgumentException('Passed function must be callable');
1773
        }
1774
1775 10
        $this->generatorToArray();
1776
1777 10
        \usort($this->array, $function);
1778
1779 10
        return $this;
1780
    }
1781
1782
    /**
1783
     * Custom sort by value via "usort".
1784
     *
1785
     * @see          http://php.net/manual/en/function.usort.php
1786
     *
1787
     * @param callable $function
1788
     *
1789
     * @throws \InvalidArgumentException
1790
     *
1791
     * @return $this
1792
     *               <p>(Immutable) Return this Arrayy object.</p>
1793
     *
1794
     * @psalm-return static<TKey,T>
1795
     * @psalm-mutation-free
1796
     */
1797 4
    public function customSortValuesImmutable($function): self
1798
    {
1799 4
        $that = clone $this;
1800
1801
        /**
1802
         * @psalm-suppress ImpureMethodCall - object is already cloned
1803
         */
1804 4
        $that->customSortValues($function);
1805
1806 4
        return $that;
1807
    }
1808
1809
    /**
1810
     * Delete the given key or keys.
1811
     *
1812
     * @param int|int[]|string|string[] $keyOrKeys
1813
     *
1814
     * @return void
1815
     */
1816 9
    public function delete($keyOrKeys)
1817
    {
1818 9
        $keyOrKeys = (array) $keyOrKeys;
1819
1820 9
        foreach ($keyOrKeys as $key) {
1821 9
            $this->offsetUnset($key);
1822
        }
1823 9
    }
1824
1825
    /**
1826
     * Return values that are only in the current array.
1827
     *
1828
     * @param array ...$array
1829
     *
1830
     * @return static
1831
     *                <p>(Immutable)</p>
1832
     *
1833
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1834
     * @psalm-return static<TKey,T>
1835
     * @psalm-mutation-free
1836
     */
1837 13
    public function diff(...$array): self
1838
    {
1839 13
        return static::create(
1840 13
            \array_diff($this->toArray(), ...$array),
1841 13
            $this->iteratorClass,
1842 13
            false
1843
        );
1844
    }
1845
1846
    /**
1847
     * Return values that are only in the current array.
1848
     *
1849
     * @param array ...$array
1850
     *
1851
     * @return static
1852
     *                <p>(Immutable)</p>
1853
     *
1854
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1855
     * @psalm-return static<TKey,T>
1856
     * @psalm-mutation-free
1857
     */
1858 8
    public function diffKey(...$array): self
1859
    {
1860 8
        return static::create(
1861 8
            \array_diff_key($this->toArray(), ...$array),
1862 8
            $this->iteratorClass,
1863 8
            false
1864
        );
1865
    }
1866
1867
    /**
1868
     * Return values and Keys that are only in the current array.
1869
     *
1870
     * @param array $array
1871
     *
1872
     * @return static
1873
     *                <p>(Immutable)</p>
1874
     *
1875
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1876
     * @psalm-return static<TKey,T>
1877
     * @psalm-mutation-free
1878
     */
1879 8
    public function diffKeyAndValue(array $array = []): self
1880
    {
1881 8
        return static::create(
1882 8
            \array_diff_assoc($this->toArray(), $array),
1883 8
            $this->iteratorClass,
1884 8
            false
1885
        );
1886
    }
1887
1888
    /**
1889
     * Return values that are only in the current multi-dimensional array.
1890
     *
1891
     * @param array                 $array
1892
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
1893
     *
1894
     * @return static
1895
     *                <p>(Immutable)</p>
1896
     *
1897
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1898
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
1899
     * @psalm-return static<TKey,T>
1900
     * @psalm-mutation-free
1901
     */
1902 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1903
    {
1904
        // init
1905 1
        $result = [];
1906
1907
        if (
1908 1
            $helperVariableForRecursion !== null
1909
            &&
1910 1
            \is_array($helperVariableForRecursion) === true
1911
        ) {
1912
            $arrayForTheLoop = $helperVariableForRecursion;
1913
        } else {
1914 1
            $arrayForTheLoop = $this->getGenerator();
1915
        }
1916
1917 1
        foreach ($arrayForTheLoop as $key => $value) {
1918 1
            if ($value instanceof self) {
1919
                $value = $value->toArray();
1920
            }
1921
1922 1
            if (\array_key_exists($key, $array)) {
1923 1
                if ($value !== $array[$key]) {
1924 1
                    $result[$key] = $value;
1925
                }
1926
            } else {
1927 1
                $result[$key] = $value;
1928
            }
1929
        }
1930
1931 1
        return static::create(
1932 1
            $result,
1933 1
            $this->iteratorClass,
1934 1
            false
1935
        );
1936
    }
1937
1938
    /**
1939
     * Return values that are only in the new $array.
1940
     *
1941
     * @param array $array
1942
     *
1943
     * @return static
1944
     *                <p>(Immutable)</p>
1945
     *
1946
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1947
     * @psalm-return static<TKey,T>
1948
     * @psalm-mutation-free
1949
     */
1950 8
    public function diffReverse(array $array = []): self
1951
    {
1952 8
        return static::create(
1953 8
            \array_diff($array, $this->toArray()),
1954 8
            $this->iteratorClass,
1955 8
            false
1956
        );
1957
    }
1958
1959
    /**
1960
     * Divide an array into two arrays. One with keys and the other with values.
1961
     *
1962
     * @return static
1963
     *                <p>(Immutable)</p>
1964
     *
1965
     * @psalm-return static<TKey,T>
1966
     * @psalm-mutation-free
1967
     */
1968 1
    public function divide(): self
1969
    {
1970 1
        return static::create(
1971
            [
1972 1
                $this->keys(),
1973 1
                $this->values(),
1974
            ],
1975 1
            $this->iteratorClass,
1976 1
            false
1977
        );
1978
    }
1979
1980
    /**
1981
     * Iterate over the current array and modify the array's value.
1982
     *
1983
     * @param \Closure $closure
1984
     *
1985
     * @return static
1986
     *                <p>(Immutable)</p>
1987
     *
1988
     * @psalm-return static<TKey,T>
1989
     * @psalm-mutation-free
1990
     */
1991 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...
1992
    {
1993
        // init
1994 5
        $array = [];
1995
1996 5
        foreach ($this->getGenerator() as $key => $value) {
1997 5
            $array[$key] = $closure($value, $key);
1998
        }
1999
2000 5
        return static::create(
2001 5
            $array,
2002 5
            $this->iteratorClass,
2003 5
            false
2004
        );
2005
    }
2006
2007
    /**
2008
     * Sets the internal iterator to the last element in the array and returns this element.
2009
     *
2010
     * @return mixed
2011
     */
2012
    public function end()
2013
    {
2014
        return \end($this->array);
2015
    }
2016
2017
    /**
2018
     * Check if a value is in the current array using a closure.
2019
     *
2020
     * @param \Closure $closure
2021
     *
2022
     * @return bool
2023
     *              <p>Returns true if the given value is found, false otherwise.</p>
2024
     */
2025 4
    public function exists(\Closure $closure): bool
2026
    {
2027
        // init
2028 4
        $isExists = false;
2029
2030 4
        foreach ($this->getGenerator() as $key => $value) {
2031 3
            if ($closure($value, $key)) {
2032 1
                $isExists = true;
2033
2034 1
                break;
2035
            }
2036
        }
2037
2038 4
        return $isExists;
2039
    }
2040
2041
    /**
2042
     * Fill the array until "$num" with "$default" values.
2043
     *
2044
     * @param int   $num
2045
     * @param mixed $default
2046
     *
2047
     * @return static
2048
     *                <p>(Immutable)</p>
2049
     *
2050
     * @psalm-return static<TKey,T>
2051
     * @psalm-mutation-free
2052
     */
2053 8
    public function fillWithDefaults(int $num, $default = null): self
2054
    {
2055 8
        if ($num < 0) {
2056 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2057
        }
2058
2059 7
        $this->generatorToArray();
2060
2061 7
        $tmpArray = $this->array;
2062
2063 7
        $count = \count($tmpArray);
2064
2065 7
        while ($count < $num) {
2066 4
            $tmpArray[] = $default;
2067 4
            ++$count;
2068
        }
2069
2070 7
        return static::create(
2071 7
            $tmpArray,
2072 7
            $this->iteratorClass,
2073 7
            false
2074
        );
2075
    }
2076
2077
    /**
2078
     * Find all items in an array that pass the truth test.
2079
     *
2080
     * @param \Closure|null $closure [optional] <p>
2081
     *                               The callback function to use
2082
     *                               </p>
2083
     *                               <p>
2084
     *                               If no callback is supplied, all entries of
2085
     *                               input equal to false (see
2086
     *                               converting to
2087
     *                               boolean) will be removed.
2088
     *                               </p>
2089
     * @param int           $flag    [optional] <p>
2090
     *                               Flag determining what arguments are sent to <i>callback</i>:
2091
     *                               </p><ul>
2092
     *                               <li>
2093
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
2094
     *                               to <i>callback</i> instead of the value</span>
2095
     *                               </li>
2096
     *                               <li>
2097
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
2098
     *                               arguments to <i>callback</i> instead of the value</span>
2099
     *                               </li>
2100
     *                               </ul>
2101
     *
2102
     * @return static
2103
     *                <p>(Immutable)</p>
2104
     *
2105
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
2106
     * @psalm-return static<TKey,T>
2107
     * @psalm-mutation-free
2108
     */
2109 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2110
    {
2111 12
        if (!$closure) {
2112 1
            return $this->clean();
2113
        }
2114
2115 12
        return static::create(
2116 12
            \array_filter($this->toArray(), $closure, $flag),
2117 12
            $this->iteratorClass,
2118 12
            false
2119
        );
2120
    }
2121
2122
    /**
2123
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2124
     * property within that.
2125
     *
2126
     * @param string          $property
2127
     * @param string|string[] $value
2128
     * @param string          $comparisonOp
2129
     *                                      <p>
2130
     *                                      'eq' (equals),<br />
2131
     *                                      'gt' (greater),<br />
2132
     *                                      'gte' || 'ge' (greater or equals),<br />
2133
     *                                      'lt' (less),<br />
2134
     *                                      'lte' || 'le' (less or equals),<br />
2135
     *                                      'ne' (not equals),<br />
2136
     *                                      'contains',<br />
2137
     *                                      'notContains',<br />
2138
     *                                      'newer' (via strtotime),<br />
2139
     *                                      'older' (via strtotime),<br />
2140
     *                                      </p>
2141
     *
2142
     * @return static
2143
     *                <p>(Immutable)</p>
2144
     *
2145
     * @psalm-return static<TKey,T>
2146
     * @psalm-mutation-free
2147
     *
2148
     * @psalm-suppress MissingClosureReturnType
2149
     * @psalm-suppress MissingClosureParamType
2150
     */
2151 1
    public function filterBy(
2152
        string $property,
2153
        $value,
2154
        string $comparisonOp = null
2155
    ): self {
2156 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...
2157 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2158
        }
2159
2160
        $ops = [
2161
            'eq' => static function ($item, $prop, $value): bool {
2162 1
                return $item[$prop] === $value;
2163 1
            },
2164
            'gt' => static function ($item, $prop, $value): bool {
2165
                return $item[$prop] > $value;
2166 1
            },
2167
            'ge' => static function ($item, $prop, $value): bool {
2168
                return $item[$prop] >= $value;
2169 1
            },
2170
            'gte' => static function ($item, $prop, $value): bool {
2171
                return $item[$prop] >= $value;
2172 1
            },
2173
            'lt' => static function ($item, $prop, $value): bool {
2174 1
                return $item[$prop] < $value;
2175 1
            },
2176
            'le' => static function ($item, $prop, $value): bool {
2177
                return $item[$prop] <= $value;
2178 1
            },
2179
            'lte' => static function ($item, $prop, $value): bool {
2180
                return $item[$prop] <= $value;
2181 1
            },
2182
            'ne' => static function ($item, $prop, $value): bool {
2183
                return $item[$prop] !== $value;
2184 1
            },
2185
            'contains' => static function ($item, $prop, $value): bool {
2186 1
                return \in_array($item[$prop], (array) $value, true);
2187 1
            },
2188
            'notContains' => static function ($item, $prop, $value): bool {
2189
                return !\in_array($item[$prop], (array) $value, true);
2190 1
            },
2191
            'newer' => static function ($item, $prop, $value): bool {
2192
                return \strtotime($item[$prop]) > \strtotime($value);
2193 1
            },
2194
            'older' => static function ($item, $prop, $value): bool {
2195
                return \strtotime($item[$prop]) < \strtotime($value);
2196 1
            },
2197
        ];
2198
2199 1
        $result = \array_values(
2200 1
            \array_filter(
2201 1
                $this->toArray(false, true),
2202
                static function ($item) use (
2203 1
                    $property,
2204 1
                    $value,
2205 1
                    $ops,
2206 1
                    $comparisonOp
2207
                ) {
2208 1
                    $item = (array) $item;
2209 1
                    $itemArrayy = static::create($item);
2210 1
                    $item[$property] = $itemArrayy->get($property, []);
2211
2212 1
                    return $ops[$comparisonOp]($item, $property, $value);
2213 1
                }
2214
            )
2215
        );
2216
2217 1
        return static::create(
2218 1
            $result,
2219 1
            $this->iteratorClass,
2220 1
            false
2221
        );
2222
    }
2223
2224
    /**
2225
     * Find the first item in an array that passes the truth test,
2226
     *  otherwise return false
2227
     *
2228
     * @param \Closure $closure
2229
     *
2230
     * @return false|mixed
2231
     *                     <p>Return false if we did not find the value.</p>
2232
     */
2233 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...
2234
    {
2235 8
        foreach ($this->getGenerator() as $key => $value) {
2236 6
            if ($closure($value, $key)) {
2237 5
                return $value;
2238
            }
2239
        }
2240
2241 3
        return false;
2242
    }
2243
2244
    /**
2245
     * find by ...
2246
     *
2247
     * @param string          $property
2248
     * @param string|string[] $value
2249
     * @param string          $comparisonOp
2250
     *
2251
     * @return static
2252
     *                <p>(Immutable)</p>
2253
     *
2254
     * @psalm-return static<TKey,T>
2255
     * @psalm-mutation-free
2256
     */
2257 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2258
    {
2259 1
        return $this->filterBy($property, $value, $comparisonOp);
2260
    }
2261
2262
    /**
2263
     * Get the first value from the current array.
2264
     *
2265
     * @return mixed
2266
     *               <p>Return null if there wasn't a element.</p>
2267
     */
2268 21
    public function first()
2269
    {
2270 21
        $key_first = $this->firstKey();
2271 21
        if ($key_first === null) {
2272 3
            return null;
2273
        }
2274
2275 18
        return $this->get($key_first);
2276
    }
2277
2278
    /**
2279
     * Get the first key from the current array.
2280
     *
2281
     * @return mixed
2282
     *               <p>Return null if there wasn't a element.</p>
2283
     * @psalm-mutation-free
2284
     */
2285 28
    public function firstKey()
2286
    {
2287 28
        $this->generatorToArray();
2288
2289 28
        return \array_key_first($this->array);
2290
    }
2291
2292
    /**
2293
     * Get the first value(s) from the current array.
2294
     * And will return an empty array if there was no first entry.
2295
     *
2296
     * @param int|null $number <p>How many values you will take?</p>
2297
     *
2298
     * @return static
2299
     *                <p>(Immutable)</p>
2300
     *
2301
     * @psalm-return static<TKey,T>
2302
     * @psalm-mutation-free
2303
     */
2304 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...
2305
    {
2306 37
        $arrayTmp = $this->toArray();
2307
2308 37
        if ($number === null) {
2309 14
            $array = (array) \array_shift($arrayTmp);
2310
        } else {
2311 23
            $number = (int) $number;
2312 23
            $array = \array_splice($arrayTmp, 0, $number);
2313
        }
2314
2315 37
        return static::create(
2316 37
            $array,
2317 37
            $this->iteratorClass,
2318 37
            false
2319
        );
2320
    }
2321
2322
    /**
2323
     * Get the first value(s) from the current array.
2324
     * And will return an empty array if there was no first entry.
2325
     *
2326
     * @param int|null $number <p>How many values you will take?</p>
2327
     *
2328
     * @return static
2329
     *                <p>(Immutable)</p>
2330
     *
2331
     * @psalm-return static<TKey,T>
2332
     * @psalm-mutation-free
2333
     */
2334 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...
2335
    {
2336 3
        $arrayTmp = $this->keys()->toArray();
2337
2338 3
        if ($number === null) {
2339
            $array = (array) \array_shift($arrayTmp);
2340
        } else {
2341 3
            $number = (int) $number;
2342 3
            $array = \array_splice($arrayTmp, 0, $number);
2343
        }
2344
2345 3
        return static::create(
2346 3
            $array,
2347 3
            $this->iteratorClass,
2348 3
            false
2349
        );
2350
    }
2351
2352
    /**
2353
     * Get and rmove the first value(s) from the current array.
2354
     * And will return an empty array if there was no first entry.
2355
     *
2356
     * @param int|null $number <p>How many values you will take?</p>
2357
     *
2358
     * @return $this
2359
     *               <p>(Mutable)</p>
2360
     *
2361
     * @psalm-return static<TKey,T>
2362
     */
2363 34
    public function firstsMutable(int $number = null): self
2364
    {
2365 34
        $this->generatorToArray();
2366
2367 34
        if ($number === null) {
2368 19
            $this->array = (array) \array_shift($this->array);
2369
        } else {
2370 15
            $number = (int) $number;
2371 15
            $this->array = \array_splice($this->array, 0, $number);
2372
        }
2373
2374 34
        return $this;
2375
    }
2376
2377
    /**
2378
     * Exchanges all keys with their associated values in an array.
2379
     *
2380
     * @return static
2381
     *                <p>(Immutable)</p>
2382
     *
2383
     * @psalm-return static<TKey,T>
2384
     * @psalm-mutation-free
2385
     */
2386 1
    public function flip(): self
2387
    {
2388 1
        return static::create(
2389 1
            \array_flip($this->toArray()),
2390 1
            $this->iteratorClass,
2391 1
            false
2392
        );
2393
    }
2394
2395
    /**
2396
     * Get a value from an array (optional using dot-notation).
2397
     *
2398
     * @param mixed $key      <p>The key to look for.</p>
2399
     * @param mixed $fallback <p>Value to fallback to.</p>
2400
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
2401
     *                        class.</p>
2402
     *
2403
     * @return mixed|static
2404
     *
2405
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2406
     * @psalm-mutation-free
2407
     */
2408 215
    public function get($key, $fallback = null, array $array = null)
2409
    {
2410 215
        if ($array !== null) {
2411 4
            $usedArray = $array;
2412
        } else {
2413 212
            $this->generatorToArray();
2414
2415 212
            $usedArray = $this->array;
2416
        }
2417
2418 215
        if ($key === null) {
2419 1
            return static::create(
2420 1
                $usedArray,
2421 1
                $this->iteratorClass,
2422 1
                false
2423
            );
2424
        }
2425
2426
        // php cast "bool"-index into "int"-index
2427 215
        if ((bool) $key === $key) {
2428 3
            $key = (int) $key;
2429
        }
2430
2431 215
        if (\array_key_exists($key, $usedArray) === true) {
2432 181
            if (\is_array($usedArray[$key]) === true) {
2433 17
                return static::create(
2434 17
                    $usedArray[$key],
2435 17
                    $this->iteratorClass,
2436 17
                    false
2437
                );
2438
            }
2439
2440 167
            return $usedArray[$key];
2441
        }
2442
2443
        // crawl through array, get key according to object or not
2444 52
        $usePath = false;
2445
        if (
2446 52
            $this->pathSeparator
2447
            &&
2448 52
            (string) $key === $key
2449
            &&
2450 52
            \strpos($key, $this->pathSeparator) !== false
2451
        ) {
2452 26
            $segments = \explode($this->pathSeparator, (string) $key);
2453 26
            if ($segments !== false) {
2454 26
                $usePath = true;
2455
2456 26
                foreach ($segments as $segment) {
2457
                    if (
2458
                        (
2459 26
                            \is_array($usedArray) === true
2460
                            ||
2461 26
                            $usedArray instanceof \ArrayAccess
2462
                        )
2463
                        &&
2464 26
                        isset($usedArray[$segment])
2465
                    ) {
2466 26
                        $usedArray = $usedArray[$segment];
2467
2468 26
                        continue;
2469
                    }
2470
2471
                    if (
2472 10
                        \is_object($usedArray) === true
2473
                        &&
2474 10
                        \property_exists($usedArray, $segment)
2475
                    ) {
2476 1
                        $usedArray = $usedArray->{$segment};
2477
2478 1
                        continue;
2479
                    }
2480
2481 9
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2482
                }
2483
            }
2484
        }
2485
2486 50
        if (!$usePath && !isset($usedArray[$key])) {
2487 26
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2488
        }
2489
2490 24
        if (\is_array($usedArray) === true) {
2491 4
            return static::create(
2492 4
                $usedArray,
2493 4
                $this->iteratorClass,
2494 4
                false
2495
            );
2496
        }
2497
2498 24
        return $usedArray;
2499
    }
2500
2501
    /**
2502
     * alias: for "Arrayy->toArray()"
2503
     *
2504
     * @return array
2505
     *
2506
     * @see          Arrayy::getArray()
2507
     *
2508
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2509
     */
2510 12
    public function getAll(): array
2511
    {
2512 12
        return $this->toArray();
2513
    }
2514
2515
    /**
2516
     * Get the current array from the "Arrayy"-object.
2517
     *
2518
     * alias for "toArray()"
2519
     *
2520
     * @param bool $convertAllArrayyElements <p>
2521
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2522
     *                                       </p>
2523
     * @param bool $preserveKeys             <p>
2524
     *                                       e.g.: A generator maybe return the same key more then once,
2525
     *                                       so maybe you will ignore the keys.
2526
     *                                       </p>
2527
     *
2528
     * @return array
2529
     *
2530
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2531
     * @psalm-mutation-free
2532
     *
2533
     * @see Arrayy::toArray()
2534
     */
2535 492
    public function getArray(
2536
        bool $convertAllArrayyElements = false,
2537
        bool $preserveKeys = true
2538
    ): array {
2539 492
        return $this->toArray(
2540 492
            $convertAllArrayyElements,
2541 492
            $preserveKeys
2542
        );
2543
    }
2544
2545
    /**
2546
     * Get the current array from the "Arrayy"-object as list.
2547
     *
2548
     * alias for "toList()"
2549
     *
2550
     * @param bool $convertAllArrayyElements <p>
2551
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2552
     *                                       </p>
2553
     *
2554
     * @return array
2555
     *
2556
     * @psalm-return array<int,mixed>|array<int,T>
2557
     * @psalm-mutation-free
2558
     *
2559
     * @see Arrayy::toList()
2560
     */
2561 1
    public function getList(bool $convertAllArrayyElements = false): array
2562
    {
2563 1
        return $this->toList($convertAllArrayyElements);
2564
    }
2565
2566
    /**
2567
     * Returns the values from a single column of the input array, identified by
2568
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2569
     *
2570
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2571
     * array by the values from the $indexKey column in the input array.
2572
     *
2573
     * @param mixed $columnKey
2574
     * @param mixed $indexKey
2575
     *
2576
     * @return static
2577
     *                <p>(Immutable)</p>
2578
     *
2579
     * @psalm-return static<TKey,T>
2580
     * @psalm-mutation-free
2581
     */
2582 1
    public function getColumn($columnKey = null, $indexKey = null): self
2583
    {
2584 1
        return static::create(
2585 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2586 1
            $this->iteratorClass,
2587 1
            false
2588
        );
2589
    }
2590
2591
    /**
2592
     * Get the current array from the "Arrayy"-object as generator.
2593
     *
2594
     * @return \Generator
2595
     *
2596
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2597
     * @psalm-mutation-free
2598
     */
2599 1010
    public function getGenerator(): \Generator
2600
    {
2601 1010
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2602 42
            yield from $this->generator;
2603
        }
2604
2605 1010
        yield from $this->array;
2606 966
    }
2607
2608
    /**
2609
     * alias: for "Arrayy->keys()"
2610
     *
2611
     * @return static
2612
     *                <p>(Immutable)</p>
2613
     *
2614
     * @see          Arrayy::keys()
2615
     *
2616
     * @psalm-return static<array-key,TKey>
2617
     * @psalm-mutation-free
2618
     */
2619 2
    public function getKeys()
2620
    {
2621 2
        return $this->keys();
2622
    }
2623
2624
    /**
2625
     * Get the current array from the "Arrayy"-object as object.
2626
     *
2627
     * @return \stdClass
2628
     */
2629 4
    public function getObject(): \stdClass
2630
    {
2631 4
        return self::arrayToObject($this->toArray());
2632
    }
2633
2634
    /**
2635
     * alias: for "Arrayy->randomImmutable()"
2636
     *
2637
     * @return static
2638
     *                <p>(Immutable)</p>
2639
     *
2640
     * @see          Arrayy::randomImmutable()
2641
     *
2642
     * @psalm-return static<int|array-key,T>
2643
     */
2644 4
    public function getRandom(): self
2645
    {
2646 4
        return $this->randomImmutable();
2647
    }
2648
2649
    /**
2650
     * alias: for "Arrayy->randomKey()"
2651
     *
2652
     * @return mixed
2653
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2654
     *
2655
     * @see Arrayy::randomKey()
2656
     */
2657 3
    public function getRandomKey()
2658
    {
2659 3
        return $this->randomKey();
2660
    }
2661
2662
    /**
2663
     * alias: for "Arrayy->randomKeys()"
2664
     *
2665
     * @param int $number
2666
     *
2667
     * @return static
2668
     *                <p>(Immutable)</p>
2669
     *
2670
     * @see          Arrayy::randomKeys()
2671
     *
2672
     * @psalm-return static<TKey,T>
2673
     */
2674 8
    public function getRandomKeys(int $number): self
2675
    {
2676 8
        return $this->randomKeys($number);
2677
    }
2678
2679
    /**
2680
     * alias: for "Arrayy->randomValue()"
2681
     *
2682
     * @return mixed
2683
     *               <p>Get a random value or null if there wasn't a value.</p>
2684
     *
2685
     * @see Arrayy::randomValue()
2686
     */
2687 3
    public function getRandomValue()
2688
    {
2689 3
        return $this->randomValue();
2690
    }
2691
2692
    /**
2693
     * alias: for "Arrayy->randomValues()"
2694
     *
2695
     * @param int $number
2696
     *
2697
     * @return static
2698
     *                <p>(Immutable)</p>
2699
     *
2700
     * @see          Arrayy::randomValues()
2701
     *
2702
     * @psalm-return static<TKey,T>
2703
     */
2704 6
    public function getRandomValues(int $number): self
2705
    {
2706 6
        return $this->randomValues($number);
2707
    }
2708
2709
    /**
2710
     * Gets all values.
2711
     *
2712
     * @return static
2713
     *                <p>The values of all elements in this array, in the order they
2714
     *                appear in the array.</p>
2715
     *
2716
     * @psalm-return static<TKey,T>
2717
     */
2718 4
    public function getValues()
2719
    {
2720 4
        $this->generatorToArray(false);
2721
2722 4
        return static::create(
2723 4
            \array_values($this->array),
2724 4
            $this->iteratorClass,
2725 4
            false
2726
        );
2727
    }
2728
2729
    /**
2730
     * Gets all values via Generator.
2731
     *
2732
     * @return \Generator
2733
     *                    <p>The values of all elements in this array, in the order they
2734
     *                    appear in the array as Generator.</p>
2735
     *
2736
     * @psalm-return \Generator<TKey,T>
2737
     */
2738 4
    public function getValuesYield(): \Generator
2739
    {
2740 4
        yield from $this->getGenerator();
2741 4
    }
2742
2743
    /**
2744
     * Group values from a array according to the results of a closure.
2745
     *
2746
     * @param callable|string $grouper  <p>A callable function name.</p>
2747
     * @param bool            $saveKeys
2748
     *
2749
     * @return static
2750
     *                <p>(Immutable)</p>
2751
     *
2752
     * @psalm-return static<TKey,T>
2753
     * @psalm-mutation-free
2754
     */
2755 4
    public function group($grouper, bool $saveKeys = false): self
2756
    {
2757
        // init
2758 4
        $result = [];
2759
2760
        // Iterate over values, group by property/results from closure.
2761 4
        foreach ($this->getGenerator() as $key => $value) {
2762 4
            if (\is_callable($grouper) === true) {
2763 3
                $groupKey = $grouper($value, $key);
2764
            } else {
2765 1
                $groupKey = $this->get($grouper);
2766
            }
2767
2768 4
            $newValue = $this->get($groupKey, null, $result);
2769
2770 4
            if ($groupKey instanceof self) {
2771
                $groupKey = $groupKey->toArray();
2772
            }
2773
2774 4
            if ($newValue instanceof self) {
2775 4
                $newValue = $newValue->toArray();
2776
            }
2777
2778
            // Add to results.
2779 4
            if ($groupKey !== null) {
2780 3
                if ($saveKeys) {
2781 2
                    $result[$groupKey] = $newValue;
2782 2
                    $result[$groupKey][$key] = $value;
2783
                } else {
2784 1
                    $result[$groupKey] = $newValue;
2785 1
                    $result[$groupKey][] = $value;
2786
                }
2787
            }
2788
        }
2789
2790 4
        return static::create(
2791 4
            $result,
2792 4
            $this->iteratorClass,
2793 4
            false
2794
        );
2795
    }
2796
2797
    /**
2798
     * Check if an array has a given key.
2799
     *
2800
     * @param mixed $key
2801
     *
2802
     * @return bool
2803
     */
2804 28
    public function has($key): bool
2805
    {
2806 28
        static $UN_FOUND = null;
2807
2808 28
        if ($UN_FOUND === null) {
2809
            // Generate unique string to use as marker.
2810 1
            $UN_FOUND = \uniqid('arrayy', true);
2811
        }
2812
2813 28
        if (\is_array($key)) {
2814 1
            if ($key === []) {
2815
                return false;
2816
            }
2817
2818 1
            foreach ($key as $keyTmp) {
2819 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
2820 1
                if ($found === false) {
2821 1
                    return false;
2822
                }
2823
            }
2824
2825 1
            return true;
2826
        }
2827
2828 27
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2829
    }
2830
2831
    /**
2832
     * Check if an array has a given value.
2833
     *
2834
     * INFO: if you need to search recursive please use ```contains()```
2835
     *
2836
     * @param mixed $value
2837
     *
2838
     * @return bool
2839
     */
2840 1
    public function hasValue($value): bool
2841
    {
2842 1
        return $this->contains($value);
2843
    }
2844
2845
    /**
2846
     * Implodes the values of this array.
2847
     *
2848
     * @param string $glue
2849
     *
2850
     * @return string
2851
     * @psalm-mutation-free
2852
     */
2853 28
    public function implode(string $glue = ''): string
2854
    {
2855 28
        return $this->implode_recursive($glue, $this->toArray(), false);
2856
    }
2857
2858
    /**
2859
     * Implodes the keys of this array.
2860
     *
2861
     * @param string $glue
2862
     *
2863
     * @return string
2864
     * @psalm-mutation-free
2865
     */
2866 8
    public function implodeKeys(string $glue = ''): string
2867
    {
2868 8
        return $this->implode_recursive($glue, $this->toArray(), true);
2869
    }
2870
2871
    /**
2872
     * Given a list and an iterate-function that returns
2873
     * a key for each element in the list (or a property name),
2874
     * returns an object with an index of each item.
2875
     *
2876
     * @param mixed $key
2877
     *
2878
     * @return static
2879
     *                <p>(Immutable)</p>
2880
     *
2881
     * @psalm-return static<TKey,T>
2882
     * @psalm-mutation-free
2883
     */
2884 4
    public function indexBy($key): self
2885
    {
2886
        // init
2887 4
        $results = [];
2888
2889 4
        foreach ($this->getGenerator() as $a) {
2890 4
            if (\array_key_exists($key, $a) === true) {
2891 3
                $results[$a[$key]] = $a;
2892
            }
2893
        }
2894
2895 4
        return static::create(
2896 4
            $results,
2897 4
            $this->iteratorClass,
2898 4
            false
2899
        );
2900
    }
2901
2902
    /**
2903
     * alias: for "Arrayy->searchIndex()"
2904
     *
2905
     * @param mixed $value <p>The value to search for.</p>
2906
     *
2907
     * @return false|mixed
2908
     *
2909
     * @see Arrayy::searchIndex()
2910
     */
2911 4
    public function indexOf($value)
2912
    {
2913 4
        return $this->searchIndex($value);
2914
    }
2915
2916
    /**
2917
     * Get everything but the last..$to items.
2918
     *
2919
     * @param int $to
2920
     *
2921
     * @return static
2922
     *                <p>(Immutable)</p>
2923
     *
2924
     * @psalm-return static<TKey,T>
2925
     * @psalm-mutation-free
2926
     */
2927 12
    public function initial(int $to = 1): self
2928
    {
2929 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
2930
    }
2931
2932
    /**
2933
     * Return an array with all elements found in input array.
2934
     *
2935
     * @param array $search
2936
     * @param bool  $keepKeys
2937
     *
2938
     * @return static
2939
     *                <p>(Immutable)</p>
2940
     *
2941
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
2942
     * @psalm-return static<TKey,T>
2943
     * @psalm-mutation-free
2944
     */
2945 4
    public function intersection(array $search, bool $keepKeys = false): self
2946
    {
2947 4
        if ($keepKeys) {
2948
            /**
2949
             * @psalm-suppress MissingClosureReturnType
2950
             * @psalm-suppress MissingClosureParamType
2951
             */
2952 1
            return static::create(
2953 1
                \array_uintersect(
2954 1
                    $this->toArray(),
2955 1
                    $search,
2956
                    static function ($a, $b) {
2957 1
                        return $a === $b ? 0 : -1;
2958 1
                    }
2959
                ),
2960 1
                $this->iteratorClass,
2961 1
                false
2962
            );
2963
        }
2964
2965 3
        return static::create(
2966 3
            \array_values(\array_intersect($this->toArray(), $search)),
2967 3
            $this->iteratorClass,
2968 3
            false
2969
        );
2970
    }
2971
2972
    /**
2973
     * Return an array with all elements found in input array.
2974
     *
2975
     * @param array ...$array
2976
     *
2977
     * @return static
2978
     *                <p>(Immutable)</p>
2979
     *
2980
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2981
     * @psalm-return static<TKey,T>
2982
     * @psalm-mutation-free
2983
     */
2984 1
    public function intersectionMulti(...$array): self
2985
    {
2986 1
        return static::create(
2987 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
2988 1
            $this->iteratorClass,
2989 1
            false
2990
        );
2991
    }
2992
2993
    /**
2994
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2995
     *
2996
     * @param array $search
2997
     *
2998
     * @return bool
2999
     *
3000
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3001
     */
3002 1
    public function intersects(array $search): bool
3003
    {
3004 1
        return $this->intersection($search)->count() > 0;
3005
    }
3006
3007
    /**
3008
     * Invoke a function on all of an array's values.
3009
     *
3010
     * @param callable $callable
3011
     * @param mixed    $arguments
3012
     *
3013
     * @return static
3014
     *                <p>(Immutable)</p>
3015
     *
3016
     * @psalm-param  callable(T=,mixed):mixed $callable
3017
     * @psalm-return static<TKey,T>
3018
     * @psalm-mutation-free
3019
     */
3020 1 View Code Duplication
    public function invoke($callable, $arguments = []): 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...
3021
    {
3022
        // If one argument given for each iteration, create an array for it.
3023 1
        if (\is_array($arguments) === false) {
3024 1
            $arguments = \array_fill(
3025 1
                0,
3026 1
                $this->count(),
3027 1
                $arguments
3028
            );
3029
        }
3030
3031
        // If the callable has arguments, pass them.
3032 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...
3033 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3034
        } else {
3035 1
            $array = $this->map($callable);
3036
        }
3037
3038 1
        return static::create(
3039 1
            $array,
3040 1
            $this->iteratorClass,
3041 1
            false
3042
        );
3043
    }
3044
3045
    /**
3046
     * Check whether array is associative or not.
3047
     *
3048
     * @param bool $recursive
3049
     *
3050
     * @return bool
3051
     *              <p>Returns true if associative, false otherwise.</p>
3052
     */
3053 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...
3054
    {
3055 15
        if ($this->isEmpty()) {
3056 3
            return false;
3057
        }
3058
3059 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
3060 13
            if ((string) $key !== $key) {
3061 11
                return false;
3062
            }
3063
        }
3064
3065 3
        return true;
3066
    }
3067
3068
    /**
3069
     * Check if a given key or keys are empty.
3070
     *
3071
     * @param int|int[]|string|string[]|null $keys
3072
     *
3073
     * @return bool
3074
     *              <p>Returns true if empty, false otherwise.</p>
3075
     * @psalm-mutation-free
3076
     */
3077 41
    public function isEmpty($keys = null): bool
3078
    {
3079 41
        if ($this->generator) {
3080
            return $this->toArray() === [];
3081
        }
3082
3083 41
        if ($keys === null) {
3084 39
            return $this->array === [];
3085
        }
3086
3087 2
        foreach ((array) $keys as $key) {
3088 2
            if (!empty($this->get($key))) {
3089 2
                return false;
3090
            }
3091
        }
3092
3093 2
        return true;
3094
    }
3095
3096
    /**
3097
     * Check if the current array is equal to the given "$array" or not.
3098
     *
3099
     * @param array $array
3100
     *
3101
     * @return bool
3102
     *
3103
     * @psalm-param array<mixed,mixed> $array
3104
     */
3105 1
    public function isEqual(array $array): bool
3106
    {
3107 1
        return $this->toArray() === $array;
3108
    }
3109
3110
    /**
3111
     * Check if the current array is a multi-array.
3112
     *
3113
     * @return bool
3114
     */
3115 22
    public function isMultiArray(): bool
3116
    {
3117
        return !(
3118 22
            \count($this->toArray(), \COUNT_NORMAL)
3119
            ===
3120 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3121
        );
3122
    }
3123
3124
    /**
3125
     * Check whether array is numeric or not.
3126
     *
3127
     * @return bool
3128
     *              <p>Returns true if numeric, false otherwise.</p>
3129
     */
3130 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...
3131
    {
3132 5
        if ($this->isEmpty()) {
3133 2
            return false;
3134
        }
3135
3136 4
        foreach ($this->keys()->getGenerator() as $key) {
3137 4
            if ((int) $key !== $key) {
3138 2
                return false;
3139
            }
3140
        }
3141
3142 2
        return true;
3143
    }
3144
3145
    /**
3146
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3147
     *
3148
     * @param bool $recursive
3149
     *
3150
     * @return bool
3151
     * @psalm-mutation-free
3152
     */
3153 9
    public function isSequential(bool $recursive = false): bool
3154
    {
3155
3156
        // recursive
3157
3158 9
        if ($recursive === true) {
3159
            return $this->array_keys_recursive($this->toArray())
3160
                   ===
3161
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3162
        }
3163
3164
        // non recursive
3165
3166 9
        return \array_keys($this->toArray())
3167
               ===
3168 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3169
    }
3170
3171
    /**
3172
     * @return array
3173
     *
3174
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3175
     */
3176 1
    public function jsonSerialize(): array
3177
    {
3178 1
        return $this->toArray();
3179
    }
3180
3181
    /**
3182
     * Gets the key/index of the element at the current internal iterator position.
3183
     *
3184
     * @return int|string|null
3185
     */
3186
    public function key()
3187
    {
3188
        return \key($this->array);
3189
    }
3190
3191
    /**
3192
     * Checks if the given key exists in the provided array.
3193
     *
3194
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3195
     *       then you need to use "Arrayy->offsetExists()".
3196
     *
3197
     * @param int|string $key the key to look for
3198
     *
3199
     * @return bool
3200
     * @psalm-mutation-free
3201
     */
3202 140
    public function keyExists($key): bool
3203
    {
3204 140
        return \array_key_exists($key, $this->array);
3205
    }
3206
3207
    /**
3208
     * Get all keys from the current array.
3209
     *
3210
     * @param bool       $recursive     [optional] <p>
3211
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3212
     *                                  </p>
3213
     * @param mixed|null $search_values [optional] <p>
3214
     *                                  If specified, then only keys containing these values are returned.
3215
     *                                  </p>
3216
     * @param bool       $strict        [optional] <p>
3217
     *                                  Determines if strict comparison (===) should be used during the search.
3218
     *                                  </p>
3219
     *
3220
     * @return static
3221
     *                <p>(Immutable) An array of all the keys in input.</p>
3222
     *
3223
     * @psalm-return static<array-key,TKey>
3224
     * @psalm-mutation-free
3225
     */
3226 29
    public function keys(
3227
        bool $recursive = false,
3228
        $search_values = null,
3229
        bool $strict = true
3230
    ): self {
3231
3232
        // recursive
3233
3234 29
        if ($recursive === true) {
3235 4
            $array = $this->array_keys_recursive(
3236 4
                null,
3237 4
                $search_values,
3238 4
                $strict
3239
            );
3240
3241 4
            return static::create(
3242 4
                $array,
3243 4
                $this->iteratorClass,
3244 4
                false
3245
            );
3246
        }
3247
3248
        // non recursive
3249
3250 28
        if ($search_values === null) {
3251
            $arrayFunction = function (): \Generator {
3252 28
                foreach ($this->getGenerator() as $key => $value) {
3253 26
                    yield $key;
3254
                }
3255 28
            };
3256
        } else {
3257
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3258 1
                $is_array_tmp = \is_array($search_values);
3259
3260 1
                foreach ($this->getGenerator() as $key => $value) {
3261 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...
3262
                        (
3263 1
                            $is_array_tmp === false
3264
                            &&
3265 1
                            $strict === true
3266
                            &&
3267 1
                            $search_values === $value
3268
                        )
3269
                        ||
3270
                        (
3271 1
                            $is_array_tmp === false
3272
                            &&
3273 1
                            $strict === false
3274
                            &&
3275 1
                            $search_values == $value
3276
                        )
3277
                        ||
3278
                        (
3279 1
                            $is_array_tmp === true
3280
                            &&
3281 1
                            \in_array($value, $search_values, $strict)
3282
                        )
3283
                    ) {
3284 1
                        yield $key;
3285
                    }
3286
                }
3287 1
            };
3288
        }
3289
3290 28
        return static::create(
3291 28
            $arrayFunction,
3292 28
            $this->iteratorClass,
3293 28
            false
3294
        );
3295
    }
3296
3297
    /**
3298
     * Sort an array by key in reverse order.
3299
     *
3300
     * @param int $sort_flags [optional] <p>
3301
     *                        You may modify the behavior of the sort using the optional
3302
     *                        parameter sort_flags, for details
3303
     *                        see sort.
3304
     *                        </p>
3305
     *
3306
     * @return $this
3307
     *               <p>(Mutable) Return this Arrayy object.</p>
3308
     *
3309
     * @psalm-return static<TKey,T>
3310
     */
3311 4
    public function krsort(int $sort_flags = 0): self
3312
    {
3313 4
        $this->generatorToArray();
3314
3315 4
        \krsort($this->array, $sort_flags);
3316
3317 4
        return $this;
3318
    }
3319
3320
    /**
3321
     * Sort an array by key in reverse order.
3322
     *
3323
     * @param int $sort_flags [optional] <p>
3324
     *                        You may modify the behavior of the sort using the optional
3325
     *                        parameter sort_flags, for details
3326
     *                        see sort.
3327
     *                        </p>
3328
     *
3329
     * @return $this
3330
     *               <p>(Immutable) Return this Arrayy object.</p>
3331
     *
3332
     * @psalm-return static<TKey,T>
3333
     * @psalm-mutation-free
3334
     */
3335 4
    public function krsortImmutable(int $sort_flags = 0): self
3336
    {
3337 4
        $that = clone $this;
3338
3339
        /**
3340
         * @psalm-suppress ImpureMethodCall - object is already cloned
3341
         */
3342 4
        $that->krsort($sort_flags);
3343
3344 4
        return $that;
3345
    }
3346
3347
    /**
3348
     * Get the last value from the current array.
3349
     *
3350
     * @return mixed|null
3351
     *                    <p>Return null if there wasn't a element.</p>
3352
     * @psalm-mutation-free
3353
     */
3354 17
    public function last()
3355
    {
3356 17
        $key_last = $this->lastKey();
3357 17
        if ($key_last === null) {
3358 2
            return null;
3359
        }
3360
3361 15
        return $this->get($key_last);
3362
    }
3363
3364
    /**
3365
     * Get the last key from the current array.
3366
     *
3367
     * @return mixed|null
3368
     *                    <p>Return null if there wasn't a element.</p>
3369
     * @psalm-mutation-free
3370
     */
3371 21
    public function lastKey()
3372
    {
3373 21
        $this->generatorToArray();
3374
3375 21
        return \array_key_last($this->array);
3376
    }
3377
3378
    /**
3379
     * Get the last value(s) from the current array.
3380
     *
3381
     * @param int|null $number
3382
     *
3383
     * @return static
3384
     *                <p>(Immutable)</p>
3385
     *
3386
     * @psalm-return static<TKey,T>
3387
     * @psalm-mutation-free
3388
     */
3389 13
    public function lastsImmutable(int $number = null): self
3390
    {
3391 13
        if ($this->isEmpty()) {
3392 1
            return static::create(
3393 1
                [],
3394 1
                $this->iteratorClass,
3395 1
                false
3396
            );
3397
        }
3398
3399 12
        if ($number === null) {
3400 8
            $poppedValue = $this->last();
3401
3402 8
            if ($poppedValue === null) {
3403 1
                $poppedValue = [$poppedValue];
3404
            } else {
3405 7
                $poppedValue = (array) $poppedValue;
3406
            }
3407
3408 8
            $arrayy = static::create(
3409 8
                $poppedValue,
3410 8
                $this->iteratorClass,
3411 8
                false
3412
            );
3413
        } else {
3414 4
            $number = (int) $number;
3415 4
            $arrayy = $this->rest(-$number);
3416
        }
3417
3418 12
        return $arrayy;
3419
    }
3420
3421
    /**
3422
     * Get the last value(s) from the current array.
3423
     *
3424
     * @param int|null $number
3425
     *
3426
     * @return $this
3427
     *               <p>(Mutable)</p>
3428
     *
3429
     * @psalm-return static<TKey,T>
3430
     */
3431 13
    public function lastsMutable(int $number = null): self
3432
    {
3433 13
        if ($this->isEmpty()) {
3434 1
            return $this;
3435
        }
3436
3437 12
        if ($number === null) {
3438 8
            $poppedValue = $this->last();
3439
3440 8
            if ($poppedValue === null) {
3441 1
                $poppedValue = [$poppedValue];
3442
            } else {
3443 7
                $poppedValue = (array) $poppedValue;
3444
            }
3445
3446 8
            $this->array = static::create(
3447 8
                $poppedValue,
3448 8
                $this->iteratorClass,
3449 8
                false
3450 8
            )->toArray();
3451
        } else {
3452 4
            $number = (int) $number;
3453 4
            $this->array = $this->rest(-$number)->toArray();
3454
        }
3455
3456 12
        $this->generator = null;
3457
3458 12
        return $this;
3459
    }
3460
3461
    /**
3462
     * Count the values from the current array.
3463
     *
3464
     * alias: for "Arrayy->count()"
3465
     *
3466
     * @param int $mode
3467
     *
3468
     * @return int
3469
     *
3470
     * @see Arrayy::count()
3471
     */
3472 20
    public function length(int $mode = \COUNT_NORMAL): int
3473
    {
3474 20
        return $this->count($mode);
3475
    }
3476
3477
    /**
3478
     * Apply the given function to the every element of the array,
3479
     * collecting the results.
3480
     *
3481
     * @param callable $callable
3482
     * @param bool     $useKeyAsSecondParameter
3483
     * @param mixed    ...$arguments
3484
     *
3485
     * @return static
3486
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3487
     *
3488
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3489
     * @psalm-return static<TKey,T>
3490
     * @psalm-mutation-free
3491
     */
3492 5
    public function map(
3493
        callable $callable,
3494
        bool $useKeyAsSecondParameter = false,
3495
        ...$arguments
3496
    ) {
3497
        /**
3498
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3499
         */
3500 5
        $useArguments = \func_num_args() > 2;
3501
3502 5
        return static::create(
3503
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3504 5
                foreach ($this->getGenerator() as $key => $value) {
3505 4
                    if ($useArguments) {
3506 3
                        if ($useKeyAsSecondParameter) {
3507
                            yield $key => $callable($value, $key, ...$arguments);
3508
                        } else {
3509 3
                            yield $key => $callable($value, ...$arguments);
3510
                        }
3511
                    } else {
3512
                        /** @noinspection NestedPositiveIfStatementsInspection */
3513 4
                        if ($useKeyAsSecondParameter) {
3514
                            yield $key => $callable($value, $key);
3515
                        } else {
3516 4
                            yield $key => $callable($value);
3517
                        }
3518
                    }
3519
                }
3520 5
            },
3521 5
            $this->iteratorClass,
3522 5
            false
3523
        );
3524
    }
3525
3526
    /**
3527
     * Check if all items in current array match a truth test.
3528
     *
3529
     * @param \Closure $closure
3530
     *
3531
     * @return bool
3532
     */
3533 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...
3534
    {
3535 15
        if ($this->count() === 0) {
3536 2
            return false;
3537
        }
3538
3539 13
        foreach ($this->getGenerator() as $key => $value) {
3540 13
            $value = $closure($value, $key);
3541
3542 13
            if ($value === false) {
3543 7
                return false;
3544
            }
3545
        }
3546
3547 7
        return true;
3548
    }
3549
3550
    /**
3551
     * Check if any item in the current array matches a truth test.
3552
     *
3553
     * @param \Closure $closure
3554
     *
3555
     * @return bool
3556
     */
3557 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...
3558
    {
3559 14
        if ($this->count() === 0) {
3560 2
            return false;
3561
        }
3562
3563 12
        foreach ($this->getGenerator() as $key => $value) {
3564 12
            $value = $closure($value, $key);
3565
3566 12
            if ($value === true) {
3567 9
                return true;
3568
            }
3569
        }
3570
3571 4
        return false;
3572
    }
3573
3574
    /**
3575
     * Get the max value from an array.
3576
     *
3577
     * @return false|mixed
3578
     *                     <p>Will return false if there are no values.</p>
3579
     */
3580 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...
3581
    {
3582 10
        if ($this->count() === 0) {
3583 1
            return false;
3584
        }
3585
3586 9
        $max = false;
3587 9
        foreach ($this->getGenerator() as $value) {
3588
            if (
3589 9
                $max === false
3590
                ||
3591 9
                $value > $max
3592
            ) {
3593 9
                $max = $value;
3594
            }
3595
        }
3596
3597 9
        return $max;
3598
    }
3599
3600
    /**
3601
     * Merge the new $array into the current array.
3602
     *
3603
     * - keep key,value from the current array, also if the index is in the new $array
3604
     *
3605
     * @param array $array
3606
     * @param bool  $recursive
3607
     *
3608
     * @return static
3609
     *                <p>(Immutable)</p>
3610
     *
3611
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3612
     * @psalm-return static<int|TKey,T>
3613
     * @psalm-mutation-free
3614
     */
3615 32 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...
3616
    {
3617 32
        if ($recursive === true) {
3618 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
3619 9
            $result = \array_replace_recursive($this->toArray(), $array);
3620
        } else {
3621 23
            $result = \array_replace($this->toArray(), $array);
3622
        }
3623
3624 32
        return static::create(
3625 32
            $result,
3626 32
            $this->iteratorClass,
3627 32
            false
3628
        );
3629
    }
3630
3631
    /**
3632
     * Merge the new $array into the current array.
3633
     *
3634
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3635
     * - create new indexes
3636
     *
3637
     * @param array $array
3638
     * @param bool  $recursive
3639
     *
3640
     * @return static
3641
     *                <p>(Immutable)</p>
3642
     *
3643
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3644
     * @psalm-return static<TKey,T>
3645
     * @psalm-mutation-free
3646
     */
3647 19 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...
3648
    {
3649 19
        if ($recursive === true) {
3650 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
3651 5
            $result = \array_merge_recursive($this->toArray(), $array);
3652
        } else {
3653 14
            $result = \array_merge($this->toArray(), $array);
3654
        }
3655
3656 19
        return static::create(
3657 19
            $result,
3658 19
            $this->iteratorClass,
3659 19
            false
3660
        );
3661
    }
3662
3663
    /**
3664
     * Merge the the current array into the $array.
3665
     *
3666
     * - use key,value from the new $array, also if the index is in the current array
3667
     *
3668
     * @param array $array
3669
     * @param bool  $recursive
3670
     *
3671
     * @return static
3672
     *                <p>(Immutable)</p>
3673
     *
3674
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3675
     * @psalm-return static<TKey,T>
3676
     * @psalm-mutation-free
3677
     */
3678 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...
3679
    {
3680 16
        if ($recursive === true) {
3681 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
3682 4
            $result = \array_replace_recursive($array, $this->toArray());
3683
        } else {
3684 12
            $result = \array_replace($array, $this->toArray());
3685
        }
3686
3687 16
        return static::create(
3688 16
            $result,
3689 16
            $this->iteratorClass,
3690 16
            false
3691
        );
3692
    }
3693
3694
    /**
3695
     * Merge the current array into the new $array.
3696
     *
3697
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3698
     * - create new indexes
3699
     *
3700
     * @param array $array
3701
     * @param bool  $recursive
3702
     *
3703
     * @return static
3704
     *                <p>(Immutable)</p>
3705
     *
3706
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3707
     * @psalm-return static<TKey,T>
3708
     * @psalm-mutation-free
3709
     */
3710 20 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...
3711
    {
3712 20
        if ($recursive === true) {
3713 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
3714 7
            $result = \array_merge_recursive($array, $this->toArray());
3715
        } else {
3716 13
            $result = \array_merge($array, $this->toArray());
3717
        }
3718
3719 20
        return static::create(
3720 20
            $result,
3721 20
            $this->iteratorClass,
3722 20
            false
3723
        );
3724
    }
3725
3726
    /**
3727
     * @return ArrayyMeta|static
3728
     */
3729 15
    public static function meta()
3730
    {
3731 15
        return (new ArrayyMeta())->getMetaObject(static::class);
3732
    }
3733
3734
    /**
3735
     * Get the min value from an array.
3736
     *
3737
     * @return false|mixed
3738
     *                     <p>Will return false if there are no values.</p>
3739
     */
3740 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...
3741
    {
3742 10
        if ($this->count() === 0) {
3743 1
            return false;
3744
        }
3745
3746 9
        $min = false;
3747 9
        foreach ($this->getGenerator() as $value) {
3748
            if (
3749 9
                $min === false
3750
                ||
3751 9
                $value < $min
3752
            ) {
3753 9
                $min = $value;
3754
            }
3755
        }
3756
3757 9
        return $min;
3758
    }
3759
3760
    /**
3761
     * Get the most used value from the array.
3762
     *
3763
     * @return mixed|null
3764
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
3765
     * @psalm-mutation-free
3766
     */
3767 3
    public function mostUsedValue()
3768
    {
3769 3
        return $this->countValues()->arsortImmutable()->firstKey();
3770
    }
3771
3772
    /**
3773
     * Get the most used value from the array.
3774
     *
3775
     * @param int|null $number <p>How many values you will take?</p>
3776
     *
3777
     * @return static
3778
     *                <p>(Immutable)</p>
3779
     *
3780
     * @psalm-return static<TKey,T>
3781
     * @psalm-mutation-free
3782
     */
3783 3
    public function mostUsedValues(int $number = null): self
3784
    {
3785 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
3786
    }
3787
3788
    /**
3789
     * Move an array element to a new index.
3790
     *
3791
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3792
     *
3793
     * @param int|string $from
3794
     * @param int        $to
3795
     *
3796
     * @return static
3797
     *                <p>(Immutable)</p>
3798
     *
3799
     * @psalm-return static<TKey,T>
3800
     * @psalm-mutation-free
3801
     */
3802 1
    public function moveElement($from, $to): self
3803
    {
3804 1
        $array = $this->toArray();
3805
3806 1
        if ((int) $from === $from) {
3807 1
            $tmp = \array_splice($array, $from, 1);
3808 1
            \array_splice($array, (int) $to, 0, $tmp);
3809 1
            $output = $array;
3810 1
        } elseif ((string) $from === $from) {
3811 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3812 1
            $itemToMove = $array[$from];
3813 1
            if ($indexToMove !== false) {
3814 1
                \array_splice($array, $indexToMove, 1);
3815
            }
3816 1
            $i = 0;
3817 1
            $output = [];
3818 1
            foreach ($array as $key => $item) {
3819 1
                if ($i === $to) {
3820 1
                    $output[$from] = $itemToMove;
3821
                }
3822 1
                $output[$key] = $item;
3823 1
                ++$i;
3824
            }
3825
        } else {
3826
            $output = [];
3827
        }
3828
3829 1
        return static::create(
3830 1
            $output,
3831 1
            $this->iteratorClass,
3832 1
            false
3833
        );
3834
    }
3835
3836
    /**
3837
     * Move an array element to the first place.
3838
     *
3839
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3840
     *       loss the keys of an indexed array.
3841
     *
3842
     * @param int|string $key
3843
     *
3844
     * @return static
3845
     *                <p>(Immutable)</p>
3846
     *
3847
     * @psalm-return static<TKey,T>
3848
     * @psalm-mutation-free
3849
     */
3850 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...
3851
    {
3852 1
        $array = $this->toArray();
3853
3854 1
        if ($this->offsetExists($key)) {
3855 1
            $tmpValue = $this->get($key);
3856 1
            unset($array[$key]);
3857 1
            $array = [$key => $tmpValue] + $array;
3858
        }
3859
3860 1
        return static::create(
3861 1
            $array,
3862 1
            $this->iteratorClass,
3863 1
            false
3864
        );
3865
    }
3866
3867
    /**
3868
     * Move an array element to the last place.
3869
     *
3870
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3871
     *       loss the keys of an indexed array.
3872
     *
3873
     * @param int|string $key
3874
     *
3875
     * @return static
3876
     *                <p>(Immutable)</p>
3877
     *
3878
     * @psalm-return static<TKey,T>
3879
     * @psalm-mutation-free
3880
     */
3881 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...
3882
    {
3883 1
        $array = $this->toArray();
3884
3885 1
        if ($this->offsetExists($key)) {
3886 1
            $tmpValue = $this->get($key);
3887 1
            unset($array[$key]);
3888 1
            $array += [$key => $tmpValue];
3889
        }
3890
3891 1
        return static::create(
3892 1
            $array,
3893 1
            $this->iteratorClass,
3894 1
            false
3895
        );
3896
    }
3897
3898
    /**
3899
     * Moves the internal iterator position to the next element and returns this element.
3900
     *
3901
     * @return false|mixed
3902
     *                     <p>(Mutable) Will return false if there are no values.</p>
3903
     */
3904
    public function next()
3905
    {
3906
        return \next($this->array);
3907
    }
3908
3909
    /**
3910
     * Get the next nth keys and values from the array.
3911
     *
3912
     * @param int $step
3913
     * @param int $offset
3914
     *
3915
     * @return static
3916
     *                <p>(Immutable)</p>
3917
     *
3918
     * @psalm-return static<TKey,T>
3919
     * @psalm-mutation-free
3920
     */
3921 1
    public function nth(int $step, int $offset = 0): self
3922
    {
3923
        $arrayFunction = function () use ($step, $offset): \Generator {
3924 1
            $position = 0;
3925 1
            foreach ($this->getGenerator() as $key => $value) {
3926 1
                if ($position++ % $step !== $offset) {
3927 1
                    continue;
3928
                }
3929
3930 1
                yield $key => $value;
3931
            }
3932 1
        };
3933
3934 1
        return static::create(
3935 1
            $arrayFunction,
3936 1
            $this->iteratorClass,
3937 1
            false
3938
        );
3939
    }
3940
3941
    /**
3942
     * Get a subset of the items from the given array.
3943
     *
3944
     * @param mixed[] $keys
3945
     *
3946
     * @return static
3947
     *                <p>(Immutable)</p>
3948
     *
3949
     * @psalm-return static<TKey,T>
3950
     * @psalm-mutation-free
3951
     */
3952 1
    public function only(array $keys): self
3953
    {
3954 1
        $array = $this->toArray();
3955
3956 1
        return static::create(
3957 1
            \array_intersect_key($array, \array_flip($keys)),
3958 1
            $this->iteratorClass,
3959 1
            false
3960
        );
3961
    }
3962
3963
    /**
3964
     * Pad array to the specified size with a given value.
3965
     *
3966
     * @param int   $size  <p>Size of the result array.</p>
3967
     * @param mixed $value <p>Empty value by default.</p>
3968
     *
3969
     * @return static
3970
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3971
     *
3972
     * @psalm-return static<TKey,T>
3973
     * @psalm-mutation-free
3974
     */
3975 5
    public function pad(int $size, $value): self
3976
    {
3977 5
        return static::create(
3978 5
            \array_pad($this->toArray(), $size, $value),
3979 5
            $this->iteratorClass,
3980 5
            false
3981
        );
3982
    }
3983
3984
    /**
3985
     * Partitions this array in two array according to a predicate.
3986
     * Keys are preserved in the resulting array.
3987
     *
3988
     * @param \Closure $closure
3989
     *                          <p>The predicate on which to partition.</p>
3990
     *
3991
     * @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...
3992
     *                    <p>An array with two elements. The first element contains the array
3993
     *                    of elements where the predicate returned TRUE, the second element
3994
     *                    contains the array of elements where the predicate returned FALSE.</p>
3995
     *
3996
     * @psalm-return array<int, static<TKey,T>>
3997
     */
3998 1
    public function partition(\Closure $closure): array
3999
    {
4000
        // init
4001 1
        $matches = [];
4002 1
        $noMatches = [];
4003
4004 1
        foreach ($this->array as $key => $value) {
4005 1
            if ($closure($value, $key)) {
4006 1
                $matches[$key] = $value;
4007
            } else {
4008 1
                $noMatches[$key] = $value;
4009
            }
4010
        }
4011
4012 1
        return [self::create($matches), self::create($noMatches)];
4013
    }
4014
4015
    /**
4016
     * Pop a specified value off the end of the current array.
4017
     *
4018
     * @return mixed|null
4019
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4020
     */
4021 5
    public function pop()
4022
    {
4023 5
        $this->generatorToArray();
4024
4025 5
        return \array_pop($this->array);
4026
    }
4027
4028
    /**
4029
     * Prepend a (key) + value to the current array.
4030
     *
4031
     * @param mixed $value
4032
     * @param mixed $key
4033
     *
4034
     * @return $this
4035
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4036
     *
4037
     * @psalm-return static<TKey,T>
4038
     */
4039 11
    public function prepend($value, $key = null)
4040
    {
4041 11
        $this->generatorToArray();
4042
4043 11
        if ($this->properties !== []) {
4044 3
            $this->checkType($key, $value);
4045
        }
4046
4047 9
        if ($key === null) {
4048 8
            \array_unshift($this->array, $value);
4049
        } else {
4050 2
            $this->array = [$key => $value] + $this->array;
4051
        }
4052
4053 9
        return $this;
4054
    }
4055
4056
    /**
4057
     * Add a suffix to each key.
4058
     *
4059
     * @param mixed $suffix
4060
     *
4061
     * @return static
4062
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4063
     *
4064
     * @psalm-return static<TKey,T>
4065
     * @psalm-mutation-free
4066
     */
4067 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...
4068
    {
4069
        // init
4070 10
        $result = [];
4071
4072 10
        foreach ($this->getGenerator() as $key => $item) {
4073 9
            if ($item instanceof self) {
4074
                $result[$key] = $item->prependToEachKey($suffix);
4075 9
            } elseif (\is_array($item) === true) {
4076
                $result[$key] = self::create(
4077
                    $item,
4078
                    $this->iteratorClass,
4079
                    false
4080
                )->prependToEachKey($suffix)
4081
                    ->toArray();
4082
            } else {
4083 9
                $result[$key . $suffix] = $item;
4084
            }
4085
        }
4086
4087 10
        return self::create(
4088 10
            $result,
4089 10
            $this->iteratorClass,
4090 10
            false
4091
        );
4092
    }
4093
4094
    /**
4095
     * Add a suffix to each value.
4096
     *
4097
     * @param mixed $suffix
4098
     *
4099
     * @return static
4100
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4101
     *
4102
     * @psalm-return static<TKey,T>
4103
     * @psalm-mutation-free
4104
     */
4105 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...
4106
    {
4107
        // init
4108 10
        $result = [];
4109
4110 10
        foreach ($this->getGenerator() as $key => $item) {
4111 9
            if ($item instanceof self) {
4112
                $result[$key] = $item->prependToEachValue($suffix);
4113 9
            } elseif (\is_array($item) === true) {
4114
                $result[$key] = self::create(
4115
                    $item,
4116
                    $this->iteratorClass,
4117
                    false
4118
                )->prependToEachValue($suffix)
4119
                    ->toArray();
4120 9
            } elseif (\is_object($item) === true) {
4121 1
                $result[$key] = $item;
4122
            } else {
4123 8
                $result[$key] = $item . $suffix;
4124
            }
4125
        }
4126
4127 10
        return self::create(
4128 10
            $result,
4129 10
            $this->iteratorClass,
4130 10
            false
4131
        );
4132
    }
4133
4134
    /**
4135
     * Return the value of a given key and
4136
     * delete the key.
4137
     *
4138
     * @param int|int[]|string|string[]|null $keyOrKeys
4139
     * @param mixed                          $fallback
4140
     *
4141
     * @return mixed
4142
     */
4143 5
    public function pull($keyOrKeys = null, $fallback = null)
4144
    {
4145 5
        if ($keyOrKeys === null) {
4146 1
            $array = $this->toArray();
4147 1
            $this->clear();
4148
4149 1
            return $array;
4150
        }
4151
4152 4
        if (\is_array($keyOrKeys) === true) {
4153 1
            $valueOrValues = [];
4154 1
            foreach ($keyOrKeys as $key) {
4155 1
                $valueOrValues[] = $this->get($key, $fallback);
4156 1
                $this->offsetUnset($key);
4157
            }
4158
        } else {
4159 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4160 4
            $this->offsetUnset($keyOrKeys);
4161
        }
4162
4163 4
        return $valueOrValues;
4164
    }
4165
4166
    /**
4167
     * Push one or more values onto the end of array at once.
4168
     *
4169
     * @param array ...$args
4170
     *
4171
     * @return $this
4172
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4173
     *
4174
     * @noinspection ReturnTypeCanBeDeclaredInspection
4175
     *
4176
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4177
     * @psalm-return static<TKey,T>
4178
     */
4179 7
    public function push(...$args)
4180
    {
4181 7
        $this->generatorToArray();
4182
4183
        if (
4184 7
            $this->checkPropertyTypes
4185
            &&
4186 7
            $this->properties !== []
4187
        ) {
4188 1
            foreach ($args as $key => $value) {
4189 1
                $this->checkType($key, $value);
4190
            }
4191
        }
4192
4193 7
        \array_push($this->array, ...$args);
4194
4195 7
        return $this;
4196
    }
4197
4198
    /**
4199
     * Get a random value from the current array.
4200
     *
4201
     * @param int|null $number <p>How many values you will take?</p>
4202
     *
4203
     * @return static
4204
     *                <p>(Immutable)</p>
4205
     *
4206
     * @psalm-return static<int|array-key,T>
4207
     */
4208 19
    public function randomImmutable(int $number = null): self
4209
    {
4210 19
        $this->generatorToArray();
4211
4212 19
        if ($this->count() === 0) {
4213 1
            return static::create(
4214 1
                [],
4215 1
                $this->iteratorClass,
4216 1
                false
4217
            );
4218
        }
4219
4220 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...
4221
            /** @noinspection NonSecureArrayRandUsageInspection */
4222 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4223
4224 13
            return static::create(
4225 13
                $arrayRandValue,
4226 13
                $this->iteratorClass,
4227 13
                false
4228
            );
4229
        }
4230
4231 6
        $arrayTmp = $this->array;
4232
        /** @noinspection NonSecureShuffleUsageInspection */
4233 6
        \shuffle($arrayTmp);
4234
4235 6
        return static::create(
4236 6
            $arrayTmp,
4237 6
            $this->iteratorClass,
4238 6
            false
4239 6
        )->firstsImmutable($number);
4240
    }
4241
4242
    /**
4243
     * Pick a random key/index from the keys of this array.
4244
     *
4245
     * @throws \RangeException If array is empty
4246
     *
4247
     * @return mixed
4248
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4249
     */
4250 4
    public function randomKey()
4251
    {
4252 4
        $result = $this->randomKeys(1);
4253
4254 4
        if (!isset($result[0])) {
4255
            $result[0] = null;
4256
        }
4257
4258 4
        return $result[0];
4259
    }
4260
4261
    /**
4262
     * Pick a given number of random keys/indexes out of this array.
4263
     *
4264
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4265
     *
4266
     * @throws \RangeException If array is empty
4267
     *
4268
     * @return static
4269
     *                <p>(Immutable)</p>
4270
     *
4271
     * @psalm-return static<TKey,T>
4272
     */
4273 13
    public function randomKeys(int $number): self
4274
    {
4275 13
        $this->generatorToArray();
4276
4277 13
        $count = $this->count();
4278
4279
        if (
4280 13
            $number === 0
4281
            ||
4282 13
            $number > $count
4283
        ) {
4284 2
            throw new \RangeException(
4285 2
                \sprintf(
4286 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4287 2
                    $number,
4288 2
                    $count
4289
                )
4290
            );
4291
        }
4292
4293 11
        $result = (array) \array_rand($this->array, $number);
4294
4295 11
        return static::create(
4296 11
            $result,
4297 11
            $this->iteratorClass,
4298 11
            false
4299
        );
4300
    }
4301
4302
    /**
4303
     * Get a random value from the current array.
4304
     *
4305
     * @param int|null $number <p>How many values you will take?</p>
4306
     *
4307
     * @return $this
4308
     *               <p>(Mutable) Return this Arrayy object.</p>
4309
     *
4310
     * @psalm-return static<TKey,T>
4311
     */
4312 17
    public function randomMutable(int $number = null): self
4313
    {
4314 17
        $this->generatorToArray();
4315
4316 17
        if ($this->count() === 0) {
4317
            return static::create(
4318
                [],
4319
                $this->iteratorClass,
4320
                false
4321
            );
4322
        }
4323
4324 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...
4325
            /** @noinspection NonSecureArrayRandUsageInspection */
4326 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4327 7
            $this->array = $arrayRandValue;
4328
4329 7
            return $this;
4330
        }
4331
4332
        /** @noinspection NonSecureShuffleUsageInspection */
4333 11
        \shuffle($this->array);
4334
4335 11
        return $this->firstsMutable($number);
4336
    }
4337
4338
    /**
4339
     * Pick a random value from the values of this array.
4340
     *
4341
     * @return mixed
4342
     *               <p>Get a random value or null if there wasn't a value.</p>
4343
     */
4344 4
    public function randomValue()
4345
    {
4346 4
        $result = $this->randomImmutable();
4347
4348 4
        if (!isset($result[0])) {
4349
            $result[0] = null;
4350
        }
4351
4352 4
        return $result[0];
4353
    }
4354
4355
    /**
4356
     * Pick a given number of random values out of this array.
4357
     *
4358
     * @param int $number
4359
     *
4360
     * @return static
4361
     *                <p>(Mutable)</p>
4362
     *
4363
     * @psalm-return static<TKey,T>
4364
     */
4365 7
    public function randomValues(int $number): self
4366
    {
4367 7
        return $this->randomMutable($number);
4368
    }
4369
4370
    /**
4371
     * Get a random value from an array, with the ability to skew the results.
4372
     *
4373
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4374
     *
4375
     * @param array    $array
4376
     * @param int|null $number <p>How many values you will take?</p>
4377
     *
4378
     * @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...
4379
     *                           <p>(Immutable)</p>
4380
     *
4381
     * @psalm-param  array<mixed,mixed> $array
4382
     * @psalm-return static<int|array-key,T>
4383
     */
4384 9
    public function randomWeighted(array $array, int $number = null): self
4385
    {
4386
        // init
4387 9
        $options = [];
4388
4389 9
        foreach ($array as $option => $weight) {
4390 9
            if ($this->searchIndex($option) !== false) {
4391 2
                for ($i = 0; $i < $weight; ++$i) {
4392 1
                    $options[] = $option;
4393
                }
4394
            }
4395
        }
4396
4397 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4398
    }
4399
4400
    /**
4401
     * Reduce the current array via callable e.g. anonymous-function.
4402
     *
4403
     * @param callable $callable
4404
     * @param mixed    $init
4405
     *
4406
     * @return static
4407
     *                <p>(Immutable)</p>
4408
     *
4409
     * @psalm-return static<TKey,T>
4410
     * @psalm-mutation-free
4411
     */
4412 18
    public function reduce($callable, $init = []): self
4413
    {
4414 18
        if ($this->generator) {
4415 1
            $result = $init;
4416
4417 1
            foreach ($this->getGenerator() as $value) {
4418 1
                $result = $callable($result, $value);
4419
            }
4420
4421 1
            return static::create(
4422 1
                $result,
4423 1
                $this->iteratorClass,
4424 1
                false
4425
            );
4426
        }
4427
4428 18
        $result = \array_reduce($this->array, $callable, $init);
4429
4430 18
        if ($result === null) {
4431
            $this->array = [];
4432
        } else {
4433 18
            $this->array = (array) $result;
4434
        }
4435
4436 18
        return static::create(
4437 18
            $this->array,
4438 18
            $this->iteratorClass,
4439 18
            false
4440
        );
4441
    }
4442
4443
    /**
4444
     * @param bool $unique
4445
     *
4446
     * @return static
4447
     *                <p>(Immutable)</p>
4448
     *
4449
     * @psalm-return static<TKey,T>
4450
     * @psalm-mutation-free
4451
     */
4452 14
    public function reduce_dimension(bool $unique = true): self
4453
    {
4454
        // init
4455 14
        $result = [];
4456
4457 14
        foreach ($this->getGenerator() as $val) {
4458 12
            if (\is_array($val) === true) {
4459 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4460
            } else {
4461 12
                $result[] = [$val];
4462
            }
4463
        }
4464
4465 14
        $result = $result === [] ? [] : \array_merge(...$result);
4466
4467 14
        $resultArrayy = new static($result);
4468
4469
        /**
4470
         * @psalm-suppress ImpureMethodCall - object is already re-created
4471
         * @psalm-suppress InvalidReturnStatement - why?
4472
         */
4473 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4474
    }
4475
4476
    /**
4477
     * Create a numerically re-indexed Arrayy object.
4478
     *
4479
     * @return $this
4480
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4481
     *
4482
     * @psalm-return static<TKey,T>
4483
     */
4484 9
    public function reindex(): self
4485
    {
4486 9
        $this->generatorToArray(false);
4487
4488 9
        $this->array = \array_values($this->array);
4489
4490 9
        return $this;
4491
    }
4492
4493
    /**
4494
     * Return all items that fail the truth test.
4495
     *
4496
     * @param \Closure $closure
4497
     *
4498
     * @return static
4499
     *                <p>(Immutable)</p>
4500
     *
4501
     * @psalm-return static<TKey,T>
4502
     * @psalm-mutation-free
4503
     */
4504 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...
4505
    {
4506
        // init
4507 1
        $filtered = [];
4508
4509 1
        foreach ($this->getGenerator() as $key => $value) {
4510 1
            if (!$closure($value, $key)) {
4511 1
                $filtered[$key] = $value;
4512
            }
4513
        }
4514
4515 1
        return static::create(
4516 1
            $filtered,
4517 1
            $this->iteratorClass,
4518 1
            false
4519
        );
4520
    }
4521
4522
    /**
4523
     * Remove a value from the current array (optional using dot-notation).
4524
     *
4525
     * @param mixed $key
4526
     *
4527
     * @return static
4528
     *                <p>(Mutable)</p>
4529
     *
4530
     * @psalm-param  TKey $key
4531
     * @psalm-return static<TKey,T>
4532
     */
4533 21
    public function remove($key)
4534
    {
4535
        // recursive call
4536 21
        if (\is_array($key) === true) {
4537
            foreach ($key as $k) {
4538
                $this->internalRemove($k);
4539
            }
4540
4541
            return static::create(
4542
                $this->toArray(),
4543
                $this->iteratorClass,
4544
                false
4545
            );
4546
        }
4547
4548 21
        $this->internalRemove($key);
4549
4550 21
        return static::create(
4551 21
            $this->toArray(),
4552 21
            $this->iteratorClass,
4553 21
            false
4554
        );
4555
    }
4556
4557
    /**
4558
     * alias: for "Arrayy->removeValue()"
4559
     *
4560
     * @param mixed $element
4561
     *
4562
     * @return static
4563
     *                <p>(Immutable)</p>
4564
     *
4565
     * @psalm-param  T $element
4566
     * @psalm-return static<TKey,T>
4567
     * @psalm-mutation-free
4568
     */
4569 8
    public function removeElement($element)
4570
    {
4571 8
        return $this->removeValue($element);
4572
    }
4573
4574
    /**
4575
     * Remove the first value from the current array.
4576
     *
4577
     * @return static
4578
     *                <p>(Immutable)</p>
4579
     *
4580
     * @psalm-return static<TKey,T>
4581
     * @psalm-mutation-free
4582
     */
4583 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...
4584
    {
4585 7
        $tmpArray = $this->toArray();
4586
4587 7
        \array_shift($tmpArray);
4588
4589 7
        return static::create(
4590 7
            $tmpArray,
4591 7
            $this->iteratorClass,
4592 7
            false
4593
        );
4594
    }
4595
4596
    /**
4597
     * Remove the last value from the current array.
4598
     *
4599
     * @return static
4600
     *                <p>(Immutable)</p>
4601
     *
4602
     * @psalm-return static<TKey,T>
4603
     * @psalm-mutation-free
4604
     */
4605 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...
4606
    {
4607 7
        $tmpArray = $this->toArray();
4608
4609 7
        \array_pop($tmpArray);
4610
4611 7
        return static::create(
4612 7
            $tmpArray,
4613 7
            $this->iteratorClass,
4614 7
            false
4615
        );
4616
    }
4617
4618
    /**
4619
     * Removes a particular value from an array (numeric or associative).
4620
     *
4621
     * @param mixed $value
4622
     *
4623
     * @return static
4624
     *                <p>(Immutable)</p>
4625
     *
4626
     * @psalm-param  T $value
4627
     * @psalm-return static<TKey,T>
4628
     * @psalm-mutation-free
4629
     */
4630 8
    public function removeValue($value): self
4631
    {
4632 8
        $this->generatorToArray();
4633
4634
        // init
4635 8
        $isSequentialArray = $this->isSequential();
4636
4637 8
        foreach ($this->array as $key => $item) {
4638 7
            if ($item === $value) {
4639 7
                unset($this->array[$key]);
4640
            }
4641
        }
4642
4643 8
        if ($isSequentialArray) {
4644 6
            $this->array = \array_values($this->array);
4645
        }
4646
4647 8
        return static::create(
4648 8
            $this->array,
4649 8
            $this->iteratorClass,
4650 8
            false
4651
        );
4652
    }
4653
4654
    /**
4655
     * Generate array of repeated arrays.
4656
     *
4657
     * @param int $times <p>How many times has to be repeated.</p>
4658
     *
4659
     * @return static
4660
     *                <p>(Immutable)</p>
4661
     *
4662
     * @psalm-return static<TKey,T>
4663
     * @psalm-mutation-free
4664
     */
4665 1
    public function repeat($times): self
4666
    {
4667 1
        if ($times === 0) {
4668 1
            return static::create([], $this->iteratorClass);
4669
        }
4670
4671 1
        return static::create(
4672 1
            \array_fill(0, (int) $times, $this->toArray()),
4673 1
            $this->iteratorClass,
4674 1
            false
4675
        );
4676
    }
4677
4678
    /**
4679
     * Replace a key with a new key/value pair.
4680
     *
4681
     * @param mixed $oldKey
4682
     * @param mixed $newKey
4683
     * @param mixed $newValue
4684
     *
4685
     * @return static
4686
     *                <p>(Immutable)</p>
4687
     *
4688
     * @psalm-return static<TKey,T>
4689
     * @psalm-mutation-free
4690
     */
4691 5
    public function replace($oldKey, $newKey, $newValue): self
4692
    {
4693 5
        $that = clone $this;
4694
4695
        /**
4696
         * @psalm-suppress ImpureMethodCall - object is already cloned
4697
         */
4698 5
        return $that->remove($oldKey)
4699 5
            ->set($newKey, $newValue);
4700
    }
4701
4702
    /**
4703
     * Create an array using the current array as values and the other array as keys.
4704
     *
4705
     * @param array $keys <p>An array of keys.</p>
4706
     *
4707
     * @return static
4708
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4709
     *
4710
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4711
     * @psalm-return static<TKey,T>
4712
     * @psalm-mutation-free
4713
     */
4714 2
    public function replaceAllKeys(array $keys): self
4715
    {
4716 2
        return static::create(
4717 2
            \array_combine($keys, $this->toArray()),
4718 2
            $this->iteratorClass,
4719 2
            false
4720
        );
4721
    }
4722
4723
    /**
4724
     * Create an array using the current array as keys and the other array as values.
4725
     *
4726
     * @param array $array <p>An array o values.</p>
4727
     *
4728
     * @return static
4729
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4730
     *
4731
     * @psalm-param  array<mixed,T> $array
4732
     * @psalm-return static<TKey,T>
4733
     * @psalm-mutation-free
4734
     */
4735 2
    public function replaceAllValues(array $array): self
4736
    {
4737 2
        return static::create(
4738 2
            \array_combine($this->array, $array),
4739 2
            $this->iteratorClass,
4740 2
            false
4741
        );
4742
    }
4743
4744
    /**
4745
     * Replace the keys in an array with another set.
4746
     *
4747
     * @param array $keys <p>An array of keys matching the array's size</p>
4748
     *
4749
     * @return static
4750
     *                <p>(Immutable)</p>
4751
     *
4752
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4753
     * @psalm-return static<TKey,T>
4754
     * @psalm-mutation-free
4755
     */
4756 1
    public function replaceKeys(array $keys): self
4757
    {
4758 1
        $values = \array_values($this->toArray());
4759 1
        $result = \array_combine($keys, $values);
4760
4761 1
        return static::create(
4762 1
            $result,
4763 1
            $this->iteratorClass,
4764 1
            false
4765
        );
4766
    }
4767
4768
    /**
4769
     * Replace the first matched value in an array.
4770
     *
4771
     * @param mixed $search      <p>The value to replace.</p>
4772
     * @param mixed $replacement <p>The value to replace.</p>
4773
     *
4774
     * @return static
4775
     *                <p>(Immutable)</p>
4776
     *
4777
     * @psalm-return static<TKey,T>
4778
     * @psalm-mutation-free
4779
     */
4780 3
    public function replaceOneValue($search, $replacement = ''): self
4781
    {
4782 3
        $array = $this->toArray();
4783 3
        $key = \array_search($search, $array, true);
4784
4785 3
        if ($key !== false) {
4786 3
            $array[$key] = $replacement;
4787
        }
4788
4789 3
        return static::create(
4790 3
            $array,
4791 3
            $this->iteratorClass,
4792 3
            false
4793
        );
4794
    }
4795
4796
    /**
4797
     * Replace values in the current array.
4798
     *
4799
     * @param mixed $search      <p>The value to replace.</p>
4800
     * @param mixed $replacement <p>What to replace it with.</p>
4801
     *
4802
     * @return static
4803
     *                <p>(Immutable)</p>
4804
     *
4805
     * @psalm-return static<TKey,T>
4806
     * @psalm-mutation-free
4807
     */
4808 1
    public function replaceValues($search, $replacement = ''): self
4809
    {
4810
        /**
4811
         * @psalm-suppress MissingClosureReturnType
4812
         * @psalm-suppress MissingClosureParamType
4813
         */
4814 1
        return $this->each(
4815
            static function ($value) use ($search, $replacement) {
4816 1
                return \str_replace($search, $replacement, $value);
4817 1
            }
4818
        );
4819
    }
4820
4821
    /**
4822
     * Get the last elements from index $from until the end of this array.
4823
     *
4824
     * @param int $from
4825
     *
4826
     * @return static
4827
     *                <p>(Immutable)</p>
4828
     *
4829
     * @psalm-return static<TKey,T>
4830
     * @psalm-mutation-free
4831
     */
4832 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...
4833
    {
4834 15
        $tmpArray = $this->toArray();
4835
4836 15
        return static::create(
4837 15
            \array_splice($tmpArray, $from),
4838 15
            $this->iteratorClass,
4839 15
            false
4840
        );
4841
    }
4842
4843
    /**
4844
     * Return the array in the reverse order.
4845
     *
4846
     * @return $this
4847
     *               <p>(Mutable) Return this Arrayy object.</p>
4848
     *
4849
     * @psalm-return static<TKey,T>
4850
     */
4851 9
    public function reverse(): self
4852
    {
4853 9
        $this->generatorToArray();
4854
4855 9
        $this->array = \array_reverse($this->array);
4856
4857 9
        return $this;
4858
    }
4859
4860
    /**
4861
     * Sort an array in reverse order.
4862
     *
4863
     * @param int $sort_flags [optional] <p>
4864
     *                        You may modify the behavior of the sort using the optional
4865
     *                        parameter sort_flags, for details
4866
     *                        see sort.
4867
     *                        </p>
4868
     *
4869
     * @return $this
4870
     *               <p>(Mutable) Return this Arrayy object.</p>
4871
     *
4872
     * @psalm-return static<TKey,T>
4873
     */
4874 4
    public function rsort(int $sort_flags = 0): self
4875
    {
4876 4
        $this->generatorToArray();
4877
4878 4
        \rsort($this->array, $sort_flags);
4879
4880 4
        return $this;
4881
    }
4882
4883
    /**
4884
     * Sort an array in reverse order.
4885
     *
4886
     * @param int $sort_flags [optional] <p>
4887
     *                        You may modify the behavior of the sort using the optional
4888
     *                        parameter sort_flags, for details
4889
     *                        see sort.
4890
     *                        </p>
4891
     *
4892
     * @return $this
4893
     *               <p>(Immutable) Return this Arrayy object.</p>
4894
     *
4895
     * @psalm-return static<TKey,T>
4896
     * @psalm-mutation-free
4897
     */
4898 4
    public function rsortImmutable(int $sort_flags = 0): self
4899
    {
4900 4
        $that = clone $this;
4901
4902
        /**
4903
         * @psalm-suppress ImpureMethodCall - object is already cloned
4904
         */
4905 4
        $that->rsort($sort_flags);
4906
4907 4
        return $that;
4908
    }
4909
4910
    /**
4911
     * Search for the first index of the current array via $value.
4912
     *
4913
     * @param mixed $value
4914
     *
4915
     * @return false|float|int|string
4916
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4917
     * @psalm-mutation-free
4918
     */
4919 21
    public function searchIndex($value)
4920
    {
4921 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
4922 20
            if ($value === $valueFromArray) {
4923 10
                return $keyFromArray;
4924
            }
4925
        }
4926
4927 11
        return false;
4928
    }
4929
4930
    /**
4931
     * Search for the value of the current array via $index.
4932
     *
4933
     * @param mixed $index
4934
     *
4935
     * @return static
4936
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
4937
     *
4938
     * @psalm-return static<TKey,T>
4939
     * @psalm-mutation-free
4940
     */
4941 9
    public function searchValue($index): self
4942
    {
4943 9
        $this->generatorToArray();
4944
4945
        // init
4946 9
        $return = [];
4947
4948 9
        if ($this->array === []) {
4949
            return static::create(
4950
                [],
4951
                $this->iteratorClass,
4952
                false
4953
            );
4954
        }
4955
4956
        // php cast "bool"-index into "int"-index
4957 9
        if ((bool) $index === $index) {
4958 1
            $index = (int) $index;
4959
        }
4960
4961 9
        if ($this->offsetExists($index)) {
4962 7
            $return = [$this->array[$index]];
4963
        }
4964
4965 9
        return static::create(
4966 9
            $return,
4967 9
            $this->iteratorClass,
4968 9
            false
4969
        );
4970
    }
4971
4972
    /**
4973
     * Set a value for the current array (optional using dot-notation).
4974
     *
4975
     * @param string $key   <p>The key to set.</p>
4976
     * @param mixed  $value <p>Its value.</p>
4977
     *
4978
     * @return $this
4979
     *               <p>(Mutable) Return this Arrayy object.</p>
4980
     *
4981
     * @psalm-param  TKey $key
4982
     * @psalm-param  T $value
4983
     * @psalm-return static<TKey,T>
4984
     */
4985 26
    public function set($key, $value): self
4986
    {
4987 26
        $this->internalSet($key, $value);
4988
4989 26
        return $this;
4990
    }
4991
4992
    /**
4993
     * Get a value from a array and set it if it was not.
4994
     *
4995
     * WARNING: this method only set the value, if the $key is not already set
4996
     *
4997
     * @param mixed $key      <p>The key</p>
4998
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
4999
     *
5000
     * @return mixed
5001
     *               <p>(Mutable)</p>
5002
     */
5003 11
    public function setAndGet($key, $fallback = null)
5004
    {
5005 11
        $this->generatorToArray();
5006
5007
        // If the key doesn't exist, set it.
5008 11
        if (!$this->has($key)) {
5009 4
            $this->array = $this->set($key, $fallback)->toArray();
5010
        }
5011
5012 11
        return $this->get($key);
5013
    }
5014
5015
    /**
5016
     * Shifts a specified value off the beginning of array.
5017
     *
5018
     * @return mixed
5019
     *               <p>(Mutable) A shifted element from the current array.</p>
5020
     */
5021 5
    public function shift()
5022
    {
5023 5
        $this->generatorToArray();
5024
5025 5
        return \array_shift($this->array);
5026
    }
5027
5028
    /**
5029
     * Shuffle the current array.
5030
     *
5031
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5032
     * @param array $array  [optional]
5033
     *
5034
     * @return static
5035
     *                <p>(Immutable)</p>
5036
     *
5037
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5038
     * @psalm-return static<TKey,T>
5039
     *
5040
     * @noinspection BadExceptionsProcessingInspection
5041
     * @noinspection RandomApiMigrationInspection
5042
     * @noinspection NonSecureShuffleUsageInspection
5043
     */
5044 2
    public function shuffle(bool $secure = false, array $array = null): self
5045
    {
5046 2
        if ($array === null) {
5047 2
            $array = $this->toArray(false);
5048
        }
5049
5050 2
        if ($secure !== true) {
5051 2
            \shuffle($array);
5052
        } else {
5053 1
            $size = \count($array, \COUNT_NORMAL);
5054 1
            $keys = \array_keys($array);
5055 1
            for ($i = $size - 1; $i > 0; --$i) {
5056
                try {
5057 1
                    $r = \random_int(0, $i);
5058
                } catch (\Exception $e) {
5059
                    $r = \mt_rand(0, $i);
5060
                }
5061 1
                if ($r !== $i) {
5062 1
                    $temp = $array[$keys[$r]];
5063 1
                    $array[$keys[$r]] = $array[$keys[$i]];
5064 1
                    $array[$keys[$i]] = $temp;
5065
                }
5066
            }
5067
        }
5068
5069 2
        foreach ($array as $key => $value) {
5070
            // check if recursive is needed
5071 2
            if (\is_array($value) === true) {
5072
                $array[$key] = $this->shuffle($secure, $value);
5073
            }
5074
        }
5075
5076 2
        return static::create(
5077 2
            $array,
5078 2
            $this->iteratorClass,
5079 2
            false
5080
        );
5081
    }
5082
5083
    /**
5084
     * Count the values from the current array.
5085
     *
5086
     * alias: for "Arrayy->count()"
5087
     *
5088
     * @param int $mode
5089
     *
5090
     * @return int
5091
     */
5092 20
    public function size(int $mode = \COUNT_NORMAL): int
5093
    {
5094 20
        return $this->count($mode);
5095
    }
5096
5097
    /**
5098
     * Checks whether array has exactly $size items.
5099
     *
5100
     * @param int $size
5101
     *
5102
     * @return bool
5103
     */
5104 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...
5105
    {
5106
        // init
5107 1
        $itemsTempCount = 0;
5108
5109 1
        foreach ($this->getGenerator() as $key => $value) {
5110 1
            ++$itemsTempCount;
5111 1
            if ($itemsTempCount > $size) {
5112 1
                return false;
5113
            }
5114
        }
5115
5116 1
        return $itemsTempCount === $size;
5117
    }
5118
5119
    /**
5120
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5121
     * smaller than $fromSize.
5122
     *
5123
     * @param int $fromSize
5124
     * @param int $toSize
5125
     *
5126
     * @return bool
5127
     */
5128 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5129
    {
5130 1
        if ($fromSize > $toSize) {
5131 1
            $tmp = $toSize;
5132 1
            $toSize = $fromSize;
5133 1
            $fromSize = $tmp;
5134
        }
5135
5136
        // init
5137 1
        $itemsTempCount = 0;
5138
5139 1
        foreach ($this->getGenerator() as $key => $value) {
5140 1
            ++$itemsTempCount;
5141 1
            if ($itemsTempCount > $toSize) {
5142 1
                return false;
5143
            }
5144
        }
5145
5146 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5147
    }
5148
5149
    /**
5150
     * Checks whether array has more than $size items.
5151
     *
5152
     * @param int $size
5153
     *
5154
     * @return bool
5155
     */
5156 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...
5157
    {
5158
        // init
5159 1
        $itemsTempCount = 0;
5160
5161 1
        foreach ($this->getGenerator() as $key => $value) {
5162 1
            ++$itemsTempCount;
5163 1
            if ($itemsTempCount > $size) {
5164 1
                return true;
5165
            }
5166
        }
5167
5168 1
        return $itemsTempCount > $size;
5169
    }
5170
5171
    /**
5172
     * Checks whether array has less than $size items.
5173
     *
5174
     * @param int $size
5175
     *
5176
     * @return bool
5177
     */
5178 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...
5179
    {
5180
        // init
5181 1
        $itemsTempCount = 0;
5182
5183 1
        foreach ($this->getGenerator() as $key => $value) {
5184 1
            ++$itemsTempCount;
5185 1
            if ($itemsTempCount > $size) {
5186 1
                return false;
5187
            }
5188
        }
5189
5190 1
        return $itemsTempCount < $size;
5191
    }
5192
5193
    /**
5194
     * Counts all elements in an array, or something in an object.
5195
     *
5196
     * <p>
5197
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5198
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5199
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5200
     * implemented and used in PHP.
5201
     * </p>
5202
     *
5203
     * @return int
5204
     *             <p>
5205
     *             The number of elements in var, which is
5206
     *             typically an array, since anything else will have one
5207
     *             element.
5208
     *             </p>
5209
     *             <p>
5210
     *             If var is not an array or an object with
5211
     *             implemented Countable interface,
5212
     *             1 will be returned.
5213
     *             There is one exception, if var is &null;,
5214
     *             0 will be returned.
5215
     *             </p>
5216
     *             <p>
5217
     *             Caution: count may return 0 for a variable that isn't set,
5218
     *             but it may also return 0 for a variable that has been initialized with an
5219
     *             empty array. Use isset to test if a variable is set.
5220
     *             </p>
5221
     */
5222 10
    public function sizeRecursive(): int
5223
    {
5224 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5225
    }
5226
5227
    /**
5228
     * Extract a slice of the array.
5229
     *
5230
     * @param int      $offset       <p>Slice begin index.</p>
5231
     * @param int|null $length       <p>Length of the slice.</p>
5232
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5233
     *
5234
     * @return static
5235
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5236
     *
5237
     * @psalm-return static<TKey,T>
5238
     * @psalm-mutation-free
5239
     */
5240 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5241
    {
5242 5
        return static::create(
5243 5
            \array_slice(
5244 5
                $this->toArray(),
5245 5
                $offset,
5246 5
                $length,
5247 5
                $preserveKeys
5248
            ),
5249 5
            $this->iteratorClass,
5250 5
            false
5251
        );
5252
    }
5253
5254
    /**
5255
     * Sort the current array and optional you can keep the keys.
5256
     *
5257
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5258
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5259
     *                              <strong>SORT_NATURAL</strong></p>
5260
     * @param bool       $keepKeys
5261
     *
5262
     * @return static
5263
     *                <p>(Mutable) Return this Arrayy object.</p>
5264
     *
5265
     * @psalm-return static<TKey,T>
5266
     */
5267 20
    public function sort(
5268
        $direction = \SORT_ASC,
5269
        int $strategy = \SORT_REGULAR,
5270
        bool $keepKeys = false
5271
    ): self {
5272 20
        $this->generatorToArray();
5273
5274 20
        return $this->sorting(
5275 20
            $this->array,
5276 20
            $direction,
5277 20
            $strategy,
5278 20
            $keepKeys
5279
        );
5280
    }
5281
5282
    /**
5283
     * Sort the current array and optional you can keep the keys.
5284
     *
5285
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5286
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5287
     *                              <strong>SORT_NATURAL</strong></p>
5288
     * @param bool       $keepKeys
5289
     *
5290
     * @return static
5291
     *                <p>(Immutable) Return this Arrayy object.</p>
5292
     *
5293
     * @psalm-return static<TKey,T>
5294
     */
5295 12
    public function sortImmutable(
5296
        $direction = \SORT_ASC,
5297
        int $strategy = \SORT_REGULAR,
5298
        bool $keepKeys = false
5299
    ): self {
5300 12
        $that = clone $this;
5301
5302 12
        $that->generatorToArray();
5303
5304 12
        return $that->sorting(
5305 12
            $that->array,
5306 12
            $direction,
5307 12
            $strategy,
5308 12
            $keepKeys
5309
        );
5310
    }
5311
5312
    /**
5313
     * Sort the current array by key.
5314
     *
5315
     * @see          http://php.net/manual/en/function.ksort.php
5316
     * @see          http://php.net/manual/en/function.krsort.php
5317
     *
5318
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5319
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5320
     *                              <strong>SORT_NATURAL</strong></p>
5321
     *
5322
     * @return $this
5323
     *               <p>(Mutable) Return this Arrayy object.</p>
5324
     *
5325
     * @psalm-return static<TKey,T>
5326
     */
5327 18
    public function sortKeys(
5328
        $direction = \SORT_ASC,
5329
        int $strategy = \SORT_REGULAR
5330
    ): self {
5331 18
        $this->generatorToArray();
5332
5333 18
        $this->sorterKeys($this->array, $direction, $strategy);
5334
5335 18
        return $this;
5336
    }
5337
5338
    /**
5339
     * Sort the current array by key.
5340
     *
5341
     * @see          http://php.net/manual/en/function.ksort.php
5342
     * @see          http://php.net/manual/en/function.krsort.php
5343
     *
5344
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5345
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5346
     *                              <strong>SORT_NATURAL</strong></p>
5347
     *
5348
     * @return $this
5349
     *               <p>(Immutable) Return this Arrayy object.</p>
5350
     *
5351
     * @psalm-return static<TKey,T>
5352
     * @psalm-mutation-free
5353
     */
5354 8
    public function sortKeysImmutable(
5355
        $direction = \SORT_ASC,
5356
        int $strategy = \SORT_REGULAR
5357
    ): self {
5358 8
        $that = clone $this;
5359
5360
        /**
5361
         * @psalm-suppress ImpureMethodCall - object is already cloned
5362
         */
5363 8
        $that->sortKeys($direction, $strategy);
5364
5365 8
        return $that;
5366
    }
5367
5368
    /**
5369
     * Sort the current array by value.
5370
     *
5371
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5372
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5373
     *                              <strong>SORT_NATURAL</strong></p>
5374
     *
5375
     * @return static
5376
     *                <p>(Mutable)</p>
5377
     *
5378
     * @psalm-return static<TKey,T>
5379
     */
5380 1
    public function sortValueKeepIndex(
5381
        $direction = \SORT_ASC,
5382
        int $strategy = \SORT_REGULAR
5383
    ): self {
5384 1
        return $this->sort($direction, $strategy, true);
5385
    }
5386
5387
    /**
5388
     * Sort the current array by value.
5389
     *
5390
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5391
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5392
     *                              <strong>SORT_NATURAL</strong></p>
5393
     *
5394
     * @return static
5395
     *                <p>(Mutable)</p>
5396
     *
5397
     * @psalm-return static<TKey,T>
5398
     */
5399 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5400
    {
5401 1
        return $this->sort($direction, $strategy, false);
5402
    }
5403
5404
    /**
5405
     * Sort a array by value, by a closure or by a property.
5406
     *
5407
     * - If the sorter is null, the array is sorted naturally.
5408
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5409
     *
5410
     * @param callable|string|null $sorter
5411
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5412
     *                                        <strong>SORT_DESC</strong></p>
5413
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5414
     *                                        <strong>SORT_NATURAL</strong></p>
5415
     *
5416
     * @return static
5417
     *                <p>(Immutable)</p>
5418
     *
5419
     * @psalm-return static<TKey,T>
5420
     * @psalm-mutation-free
5421
     */
5422 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5423
    {
5424 1
        $array = $this->toArray();
5425 1
        $direction = $this->getDirection($direction);
5426
5427
        // Transform all values into their results.
5428 1
        if ($sorter) {
5429 1
            $arrayy = static::create(
5430 1
                $array,
5431 1
                $this->iteratorClass,
5432 1
                false
5433
            );
5434
5435
            /**
5436
             * @psalm-suppress MissingClosureReturnType
5437
             * @psalm-suppress MissingClosureParamType
5438
             */
5439 1
            $results = $arrayy->each(
5440
                function ($value) use ($sorter) {
5441 1
                    if (\is_callable($sorter) === true) {
5442 1
                        return $sorter($value);
5443
                    }
5444
5445 1
                    return $this->get($sorter);
5446 1
                }
5447
            );
5448
5449 1
            $results = $results->toArray();
5450
        } else {
5451 1
            $results = $array;
5452
        }
5453
5454
        // Sort by the results and replace by original values
5455 1
        \array_multisort($results, $direction, $strategy, $array);
5456
5457 1
        return static::create(
5458 1
            $array,
5459 1
            $this->iteratorClass,
5460 1
            false
5461
        );
5462
    }
5463
5464
    /**
5465
     * @param int      $offset
5466
     * @param int|null $length
5467
     * @param array    $replacement
5468
     *
5469
     * @return static
5470
     *                <p>(Immutable)</p>
5471
     *
5472
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5473
     * @psalm-return static<TKey,T>
5474
     * @psalm-mutation-free
5475
     */
5476 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5477
    {
5478 1
        $tmpArray = $this->toArray();
5479
5480 1
        \array_splice(
5481 1
            $tmpArray,
5482 1
            $offset,
5483 1
            $length ?? $this->count(),
5484 1
            $replacement
5485
        );
5486
5487 1
        return static::create(
5488 1
            $tmpArray,
5489 1
            $this->iteratorClass,
5490 1
            false
5491
        );
5492
    }
5493
5494
    /**
5495
     * Split an array in the given amount of pieces.
5496
     *
5497
     * @param int  $numberOfPieces
5498
     * @param bool $keepKeys
5499
     *
5500
     * @return static
5501
     *                <p>(Immutable)</p>
5502
     *
5503
     * @psalm-return static<TKey,T>
5504
     * @psalm-mutation-free
5505
     */
5506 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5507
    {
5508 1
        $this->generatorToArray();
5509
5510 1
        $count = $this->count();
5511
5512 1
        if ($count === 0) {
5513 1
            $result = [];
5514
        } else {
5515 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5516 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5517
        }
5518
5519 1
        return static::create(
5520 1
            $result,
5521 1
            $this->iteratorClass,
5522 1
            false
5523
        );
5524
    }
5525
5526
    /**
5527
     * Stripe all empty items.
5528
     *
5529
     * @return static
5530
     *                <p>(Immutable)</p>
5531
     *
5532
     * @psalm-return static<TKey,T>
5533
     * @psalm-mutation-free
5534
     */
5535 1
    public function stripEmpty(): self
5536
    {
5537 1
        return $this->filter(
5538
            static function ($item) {
5539 1
                if ($item === null) {
5540 1
                    return false;
5541
                }
5542
5543 1
                return (bool) \trim((string) $item);
5544 1
            }
5545
        );
5546
    }
5547
5548
    /**
5549
     * Swap two values between positions by key.
5550
     *
5551
     * @param int|string $swapA <p>a key in the array</p>
5552
     * @param int|string $swapB <p>a key in the array</p>
5553
     *
5554
     * @return static
5555
     *                <p>(Immutable)</p>
5556
     *
5557
     * @psalm-return static<TKey,T>
5558
     * @psalm-mutation-free
5559
     */
5560 1
    public function swap($swapA, $swapB): self
5561
    {
5562 1
        $array = $this->toArray();
5563
5564 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5565
5566 1
        return static::create(
5567 1
            $array,
5568 1
            $this->iteratorClass,
5569 1
            false
5570
        );
5571
    }
5572
5573
    /**
5574
     * Get the current array from the "Arrayy"-object.
5575
     * alias for "getArray()"
5576
     *
5577
     * @param bool $convertAllArrayyElements <p>
5578
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5579
     *                                       </p>
5580
     * @param bool $preserveKeys             <p>
5581
     *                                       e.g.: A generator maybe return the same key more then once,
5582
     *                                       so maybe you will ignore the keys.
5583
     *                                       </p>
5584
     *
5585
     * @return array
5586
     *
5587
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5588
     * @psalm-mutation-free
5589
     */
5590 929
    public function toArray(
5591
        bool $convertAllArrayyElements = false,
5592
        bool $preserveKeys = true
5593
    ): array {
5594
        // init
5595 929
        $array = [];
5596
5597 929
        if ($convertAllArrayyElements) {
5598 2
            foreach ($this->getGenerator() as $key => $value) {
5599 2
                if ($value instanceof self) {
5600 1
                    $value = $value->toArray(true);
5601
                }
5602
5603 2
                if ($preserveKeys) {
5604 1
                    $array[$key] = $value;
5605
                } else {
5606 1
                    $array[] = $value;
5607
                }
5608
            }
5609
        } else {
5610 929
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5611
        }
5612
5613 929
        return $array;
5614
    }
5615
5616
    /**
5617
     * Get the current array from the "Arrayy"-object as list.
5618
     *
5619
     * @param bool $convertAllArrayyElements <p>
5620
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5621
     *                                       </p>
5622
     *
5623
     * @return array
5624
     *
5625
     * @psalm-return array<int,mixed>|array<int,T>
5626
     * @psalm-mutation-free
5627
     */
5628 1
    public function toList(bool $convertAllArrayyElements = false): array
5629
    {
5630 1
        return $this->toArray(
5631 1
            $convertAllArrayyElements,
5632 1
            false
5633
        );
5634
    }
5635
5636
    /**
5637
     * Convert the current array to JSON.
5638
     *
5639
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
5640
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
5641
     *
5642
     * @return string
5643
     */
5644 11
    public function toJson(int $options = 0, int $depth = 512): string
5645
    {
5646 11
        $return = \json_encode($this->toArray(), $options, $depth);
5647 11
        if ($return === false) {
5648
            return '';
5649
        }
5650
5651 11
        return $return;
5652
    }
5653
5654
    /**
5655
     * @param string[]|null $items  [optional]
5656
     * @param string[]      $helper [optional]
5657
     *
5658
     * @return static|static[]
5659
     *
5660
     * @psalm-return static<int, static<TKey,T>>
5661
     */
5662 1
    public function toPermutation(array $items = null, array $helper = []): self
5663
    {
5664
        // init
5665 1
        $return = [];
5666
5667 1
        if ($items === null) {
5668 1
            $items = $this->toArray();
5669
        }
5670
5671 1
        if (empty($items)) {
5672 1
            $return[] = $helper;
5673
        } else {
5674 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5675 1
                $new_items = $items;
5676 1
                $new_helper = $helper;
5677 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5678
                /** @noinspection PhpSillyAssignmentInspection */
5679
                /** @var string[] $new_items */
5680 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...
5681 1
                \array_unshift($new_helper, $tmp_helper);
5682
                /** @noinspection SlowArrayOperationsInLoopInspection */
5683 1
                $return = \array_merge(
5684 1
                    $return,
5685 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5686
                );
5687
            }
5688
        }
5689
5690 1
        return static::create(
5691 1
            $return,
5692 1
            $this->iteratorClass,
5693 1
            false
5694
        );
5695
    }
5696
5697
    /**
5698
     * Implodes array to a string with specified separator.
5699
     *
5700
     * @param string $separator [optional] <p>The element's separator.</p>
5701
     *
5702
     * @return string
5703
     *                <p>The string representation of array, separated by ",".</p>
5704
     */
5705 19
    public function toString(string $separator = ','): string
5706
    {
5707 19
        return $this->implode($separator);
5708
    }
5709
5710
    /**
5711
     * Return a duplicate free copy of the current array.
5712
     *
5713
     * @return $this
5714
     *               <p>(Mutable)</p>
5715
     *
5716
     * @psalm-return static<TKey,T>
5717
     */
5718 13
    public function unique(): self
5719
    {
5720
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5721
5722
        /**
5723
         * @psalm-suppress MissingClosureReturnType
5724
         * @psalm-suppress MissingClosureParamType
5725
         */
5726 13
        $this->array = $this->reduce(
5727
            static function ($resultArray, $value) {
5728 12
                if (!\in_array($value, $resultArray, true)) {
5729 12
                    $resultArray[] = $value;
5730
                }
5731
5732 12
                return $resultArray;
5733 13
            },
5734 13
            []
5735 13
        )->toArray();
5736 13
        $this->generator = null;
5737
5738 13
        return $this;
5739
    }
5740
5741
    /**
5742
     * Return a duplicate free copy of the current array. (with the old keys)
5743
     *
5744
     * @return $this
5745
     *               <p>(Mutable)</p>
5746
     *
5747
     * @psalm-return static<TKey,T>
5748
     */
5749 11
    public function uniqueKeepIndex(): self
5750
    {
5751
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5752
5753
        // init
5754 11
        $array = $this->toArray();
5755
5756
        /**
5757
         * @psalm-suppress MissingClosureReturnType
5758
         * @psalm-suppress MissingClosureParamType
5759
         */
5760 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...
5761 11
            \array_keys($array),
5762
            static function ($resultArray, $key) use ($array) {
5763 10
                if (!\in_array($array[$key], $resultArray, true)) {
5764 10
                    $resultArray[$key] = $array[$key];
5765
                }
5766
5767 10
                return $resultArray;
5768 11
            },
5769 11
            []
5770
        );
5771 11
        $this->generator = null;
5772
5773 11
        return $this;
5774
    }
5775
5776
    /**
5777
     * alias: for "Arrayy->unique()"
5778
     *
5779
     * @return static
5780
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
5781
     *
5782
     * @see          Arrayy::unique()
5783
     *
5784
     * @psalm-return static<TKey,T>
5785
     */
5786 10
    public function uniqueNewIndex(): self
5787
    {
5788 10
        return $this->unique();
5789
    }
5790
5791
    /**
5792
     * Prepends one or more values to the beginning of array at once.
5793
     *
5794
     * @param array ...$args
5795
     *
5796
     * @return $this
5797
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
5798
     *
5799
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
5800
     * @psalm-return static<TKey,T>
5801
     */
5802 4
    public function unshift(...$args): self
5803
    {
5804 4
        $this->generatorToArray();
5805
5806 4
        \array_unshift($this->array, ...$args);
5807
5808 4
        return $this;
5809
    }
5810
5811
    /**
5812
     * Tests whether the given closure return something valid for all elements of this array.
5813
     *
5814
     * @param \Closure $closure the predicate
5815
     *
5816
     * @return bool
5817
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
5818
     */
5819 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...
5820
    {
5821 1
        foreach ($this->getGenerator() as $key => $value) {
5822 1
            if (!$closure($value, $key)) {
5823 1
                return false;
5824
            }
5825
        }
5826
5827 1
        return true;
5828
    }
5829
5830
    /**
5831
     * Get all values from a array.
5832
     *
5833
     * @return static
5834
     *                <p>(Immutable)</p>
5835
     *
5836
     * @psalm-return static<TKey,T>
5837
     * @psalm-mutation-free
5838
     */
5839 2
    public function values(): self
5840
    {
5841 2
        return static::create(
5842
            function () {
5843
                /** @noinspection YieldFromCanBeUsedInspection */
5844 2
                foreach ($this->getGenerator() as $value) {
5845 2
                    yield $value;
5846
                }
5847 2
            },
5848 2
            $this->iteratorClass,
5849 2
            false
5850
        );
5851
    }
5852
5853
    /**
5854
     * Apply the given function to every element in the array, discarding the results.
5855
     *
5856
     * @param callable $callable
5857
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
5858
     *
5859
     * @return $this
5860
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
5861
     *
5862
     * @psalm-return static<TKey,T>
5863
     */
5864 12
    public function walk($callable, bool $recursive = false): self
5865
    {
5866 12
        $this->generatorToArray();
5867
5868 12
        if ($this->array !== []) {
5869 10
            if ($recursive === true) {
5870 5
                \array_walk_recursive($this->array, $callable);
5871
            } else {
5872 5
                \array_walk($this->array, $callable);
5873
            }
5874
        }
5875
5876 12
        return $this;
5877
    }
5878
5879
    /**
5880
     * Returns a collection of matching items.
5881
     *
5882
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
5883
     * @param mixed  $value                 the value to match
5884
     *
5885
     * @throws \InvalidArgumentException if property or method is not defined
5886
     *
5887
     * @return static
5888
     *
5889
     * @psalm-param  T $value
5890
     * @psalm-return static<TKey,T>
5891
     */
5892 1
    public function where(string $keyOrPropertyOrMethod, $value): self
5893
    {
5894 1
        return $this->filter(
5895
            function ($item) use ($keyOrPropertyOrMethod, $value) {
5896 1
                $accessorValue = $this->extractValue(
5897 1
                    $item,
5898 1
                    $keyOrPropertyOrMethod
5899
                );
5900
5901 1
                return $accessorValue === $value;
5902 1
            }
5903
        );
5904
    }
5905
5906
    /**
5907
     * Convert an array into a object.
5908
     *
5909
     * @param array $array
5910
     *
5911
     * @return \stdClass
5912
     *
5913
     * @psalm-param array<mixed,mixed> $array
5914
     */
5915 4
    final protected static function arrayToObject(array $array = []): \stdClass
5916
    {
5917
        // init
5918 4
        $object = new \stdClass();
5919
5920 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
5921 1
            return $object;
5922
        }
5923
5924 3
        foreach ($array as $name => $value) {
5925 3
            if (\is_array($value) === true) {
5926 1
                $object->{$name} = static::arrayToObject($value);
5927
            } else {
5928 3
                $object->{$name} = $value;
5929
            }
5930
        }
5931
5932 3
        return $object;
5933
    }
5934
5935
    /**
5936
     * @param array|\Generator|null $input         <p>
5937
     *                                             An array containing keys to return.
5938
     *                                             </p>
5939
     * @param mixed|null            $search_values [optional] <p>
5940
     *                                             If specified, then only keys containing these values are returned.
5941
     *                                             </p>
5942
     * @param bool                  $strict        [optional] <p>
5943
     *                                             Determines if strict comparison (===) should be used during the
5944
     *                                             search.
5945
     *                                             </p>
5946
     *
5947
     * @return array
5948
     *               <p>an array of all the keys in input</p>
5949
     *
5950
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
5951
     * @psalm-return array<TKey|mixed>
5952
     * @psalm-mutation-free
5953
     */
5954 11
    protected function array_keys_recursive(
5955
        $input = null,
5956
        $search_values = null,
5957
        bool $strict = true
5958
    ): array {
5959
        // init
5960 11
        $keys = [];
5961 11
        $keysTmp = [];
5962
5963 11
        if ($input === null) {
5964 4
            $input = $this->getGenerator();
5965
        }
5966
5967 11
        if ($search_values === null) {
5968 11
            foreach ($input as $key => $value) {
5969 11
                $keys[] = $key;
5970
5971
                // check if recursive is needed
5972 11
                if (\is_array($value) === true) {
5973 4
                    $keysTmp[] = $this->array_keys_recursive($value);
5974
                }
5975
            }
5976
        } else {
5977 1
            $is_array_tmp = \is_array($search_values);
5978
5979 1
            foreach ($input as $key => $value) {
5980 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...
5981
                    (
5982 1
                        $is_array_tmp === false
5983
                        &&
5984 1
                        $strict === true
5985
                        &&
5986 1
                        $search_values === $value
5987
                    )
5988
                    ||
5989
                    (
5990 1
                        $is_array_tmp === false
5991
                        &&
5992 1
                        $strict === false
5993
                        &&
5994 1
                        $search_values == $value
5995
                    )
5996
                    ||
5997
                    (
5998 1
                        $is_array_tmp === true
5999
                        &&
6000 1
                        \in_array($value, $search_values, $strict)
6001
                    )
6002
                ) {
6003 1
                    $keys[] = $key;
6004
                }
6005
6006
                // check if recursive is needed
6007 1
                if (\is_array($value) === true) {
6008 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6009
                }
6010
            }
6011
        }
6012
6013 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6014
    }
6015
6016
    /**
6017
     * @param mixed      $path
6018
     * @param callable   $callable
6019
     * @param array|null $currentOffset
6020
     *
6021
     * @return void
6022
     *
6023
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6024
     * @psalm-mutation-free
6025
     */
6026 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6027
    {
6028 10
        $this->generatorToArray();
6029
6030 10
        if ($currentOffset === null) {
6031 10
            $currentOffset = &$this->array;
6032
        }
6033
6034 10
        $explodedPath = \explode($this->pathSeparator, $path);
6035 10
        if ($explodedPath === false) {
6036
            return;
6037
        }
6038
6039 10
        $nextPath = \array_shift($explodedPath);
6040
6041 10
        if (!isset($currentOffset[$nextPath])) {
6042 1
            return;
6043
        }
6044
6045 9
        if (!empty($explodedPath)) {
6046 1
            $this->callAtPath(
6047 1
                \implode($this->pathSeparator, $explodedPath),
6048 1
                $callable,
6049 1
                $currentOffset[$nextPath]
6050
            );
6051
        } else {
6052 9
            $callable($currentOffset[$nextPath]);
6053
        }
6054 9
    }
6055
6056
    /**
6057
     * Extracts the value of the given property or method from the object.
6058
     *
6059
     * @param static $object                <p>The object to extract the value from.</p>
6060
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
6061
     *                                      value should be extracted.</p>
6062
     *
6063
     * @throws \InvalidArgumentException if the method or property is not defined
6064
     *
6065
     * @return mixed
6066
     *               <p>The value extracted from the specified property or method.</p>
6067
     *
6068
     * @psalm-param self<TKey,T> $object
6069
     */
6070 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
6071
    {
6072 2
        if (isset($object[$keyOrPropertyOrMethod])) {
6073 2
            $return = $object->get($keyOrPropertyOrMethod);
6074
6075 2
            if ($return instanceof self) {
6076 1
                return $return->toArray();
6077
            }
6078
6079 1
            return $return;
6080
        }
6081
6082
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
6083
            return $object->{$keyOrPropertyOrMethod};
6084
        }
6085
6086
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
6087
            return $object->{$keyOrPropertyOrMethod}();
6088
        }
6089
6090
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6091
    }
6092
6093
    /**
6094
     * create a fallback for array
6095
     *
6096
     * 1. use the current array, if it's a array
6097
     * 2. fallback to empty array, if there is nothing
6098
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6099
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6100
     * 5. call "__toArray()" on object, if the method exists
6101
     * 6. cast a string or object with "__toString()" into an array
6102
     * 7. throw a "InvalidArgumentException"-Exception
6103
     *
6104
     * @param mixed $data
6105
     *
6106
     * @throws \InvalidArgumentException
6107
     *
6108
     * @return array
6109
     *
6110
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6111
     */
6112 1167
    protected function fallbackForArray(&$data): array
6113
    {
6114 1167
        $data = $this->internalGetArray($data);
6115
6116 1167
        if ($data === null) {
6117 2
            throw new \InvalidArgumentException('Passed value should be a array');
6118
        }
6119
6120 1165
        return $data;
6121
    }
6122
6123
    /**
6124
     * @param bool $preserveKeys <p>
6125
     *                           e.g.: A generator maybe return the same key more then once,
6126
     *                           so maybe you will ignore the keys.
6127
     *                           </p>
6128
     *
6129
     * @return bool
6130
     *
6131
     * @noinspection ReturnTypeCanBeDeclaredInspection
6132
     * @psalm-mutation-free :/
6133
     */
6134 1079
    protected function generatorToArray(bool $preserveKeys = true)
6135
    {
6136 1079
        if ($this->generator) {
6137 2
            $this->array = $this->toArray(false, $preserveKeys);
6138 2
            $this->generator = null;
6139
6140 2
            return true;
6141
        }
6142
6143 1079
        return false;
6144
    }
6145
6146
    /**
6147
     * Get correct PHP constant for direction.
6148
     *
6149
     * @param int|string $direction
6150
     *
6151
     * @return int
6152
     * @psalm-mutation-free
6153
     */
6154 43
    protected function getDirection($direction): int
6155
    {
6156 43
        if ((string) $direction === $direction) {
6157 10
            $direction = \strtolower($direction);
6158
6159 10
            if ($direction === 'desc') {
6160 2
                $direction = \SORT_DESC;
6161
            } else {
6162 8
                $direction = \SORT_ASC;
6163
            }
6164
        }
6165
6166
        if (
6167 43
            $direction !== \SORT_DESC
6168
            &&
6169 43
            $direction !== \SORT_ASC
6170
        ) {
6171
            $direction = \SORT_ASC;
6172
        }
6173
6174 43
        return $direction;
6175
    }
6176
6177
    /**
6178
     * @return TypeCheckPhpDoc[]
6179
     *
6180
     * @noinspection ReturnTypeCanBeDeclaredInspection
6181
     */
6182 16
    protected function getPropertiesFromPhpDoc()
6183
    {
6184 16
        static $PROPERTY_CACHE = [];
6185 16
        $cacheKey = 'Class::' . static::class;
6186
6187 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6188 15
            return $PROPERTY_CACHE[$cacheKey];
6189
        }
6190
6191
        // init
6192 2
        $properties = [];
6193
6194 2
        $reflector = new \ReflectionClass($this);
6195 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6196 2
        $docComment = $reflector->getDocComment();
6197 2
        if ($docComment) {
6198 2
            $docblock = $factory->create($docComment);
6199
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6200 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6201 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
6202
            }
6203
        }
6204
6205 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6206
    }
6207
6208
    /**
6209
     * @param mixed $glue
6210
     * @param mixed $pieces
6211
     * @param bool  $useKeys
6212
     *
6213
     * @return string
6214
     * @psalm-mutation-free
6215
     */
6216 36
    protected function implode_recursive(
6217
        $glue = '',
6218
        $pieces = [],
6219
        bool $useKeys = false
6220
    ): string {
6221 36
        if ($pieces instanceof self) {
6222 1
            $pieces = $pieces->toArray();
6223
        }
6224
6225 36
        if (\is_array($pieces) === true) {
6226 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6227 36
            $pieces_count_not_zero = $pieces_count > 0;
6228
6229 36
            return \implode(
6230 36
                $glue,
6231 36
                \array_map(
6232 36
                    [$this, 'implode_recursive'],
6233 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6234 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6235
                )
6236
            );
6237
        }
6238
6239
        if (
6240 36
            \is_scalar($pieces) === true
6241
            ||
6242 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6243
        ) {
6244 32
            return (string) $pieces;
6245
        }
6246
6247 8
        return '';
6248
    }
6249
6250
    /**
6251
     * @param mixed                 $needle   <p>
6252
     *                                        The searched value.
6253
     *                                        </p>
6254
     *                                        <p>
6255
     *                                        If needle is a string, the comparison is done
6256
     *                                        in a case-sensitive manner.
6257
     *                                        </p>
6258
     * @param array|\Generator|null $haystack <p>
6259
     *                                        The array.
6260
     *                                        </p>
6261
     * @param bool                  $strict   [optional] <p>
6262
     *                                        If the third parameter strict is set to true
6263
     *                                        then the in_array function will also check the
6264
     *                                        types of the
6265
     *                                        needle in the haystack.
6266
     *                                        </p>
6267
     *
6268
     * @return bool
6269
     *              <p>true if needle is found in the array, false otherwise</p>
6270
     *
6271
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6272
     * @psalm-mutation-free
6273
     */
6274 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6275
    {
6276 18
        if ($haystack === null) {
6277
            $haystack = $this->getGenerator();
6278
        }
6279
6280 18
        foreach ($haystack as $item) {
6281 14
            if (\is_array($item) === true) {
6282 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6283
            } else {
6284
                /** @noinspection NestedPositiveIfStatementsInspection */
6285 14
                if ($strict === true) {
6286 14
                    $returnTmp = $item === $needle;
6287
                } else {
6288
                    $returnTmp = $item == $needle;
6289
                }
6290
            }
6291
6292 14
            if ($returnTmp === true) {
6293 10
                return true;
6294
            }
6295
        }
6296
6297 8
        return false;
6298
    }
6299
6300
    /**
6301
     * @param mixed $data
6302
     *
6303
     * @return array|null
6304
     *
6305
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6306
     */
6307 1167
    protected function internalGetArray(&$data)
6308
    {
6309 1167
        if (\is_array($data) === true) {
6310 1161
            return $data;
6311
        }
6312
6313 56
        if (!$data) {
6314 6
            return [];
6315
        }
6316
6317 55
        if (\is_object($data) === true) {
6318 49
            if ($data instanceof \ArrayObject) {
6319 5
                return $data->getArrayCopy();
6320
            }
6321
6322 45
            if ($data instanceof \Generator) {
6323
                return static::createFromGeneratorImmutable($data)->toArray();
6324
            }
6325
6326 45
            if ($data instanceof \Traversable) {
6327
                return static::createFromObject($data)->toArray();
6328
            }
6329
6330 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...
6331
                return (array) $data->jsonSerialize();
6332
            }
6333
6334 45
            if (\method_exists($data, '__toArray')) {
6335
                return (array) $data->__toArray();
6336
            }
6337
6338 45
            if (\method_exists($data, '__toString')) {
6339
                return [(string) $data];
6340
            }
6341
        }
6342
6343 51
        if (\is_callable($data)) {
6344
            /**
6345
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6346
             */
6347 43
            $this->generator = new ArrayyRewindableGenerator($data);
6348
6349 43
            return [];
6350
        }
6351
6352 10
        if (\is_scalar($data)) {
6353 8
            return [$data];
6354
        }
6355
6356 2
        return null;
6357
    }
6358
6359
    /**
6360
     * Internal mechanics of remove method.
6361
     *
6362
     * @param mixed $key
6363
     *
6364
     * @return bool
6365
     */
6366 21
    protected function internalRemove($key): bool
6367
    {
6368 21
        $this->generatorToArray();
6369
6370
        if (
6371 21
            $this->pathSeparator
6372
            &&
6373 21
            (string) $key === $key
6374
            &&
6375 21
            \strpos($key, $this->pathSeparator) !== false
6376
        ) {
6377
            $path = \explode($this->pathSeparator, (string) $key);
6378
6379
            if ($path !== false) {
6380
                // crawl though the keys
6381
                while (\count($path, \COUNT_NORMAL) > 1) {
6382
                    $key = \array_shift($path);
6383
6384
                    if (!$this->has($key)) {
6385
                        return false;
6386
                    }
6387
6388
                    $this->array = &$this->array[$key];
6389
                }
6390
6391
                $key = \array_shift($path);
6392
            }
6393
        }
6394
6395 21
        unset($this->array[$key]);
6396
6397 21
        return true;
6398
    }
6399
6400
    /**
6401
     * Internal mechanic of set method.
6402
     *
6403
     * @param int|string|null $key
6404
     * @param mixed           $value
6405
     * @param bool            $checkProperties
6406
     *
6407
     * @return bool
6408
     */
6409 1018
    protected function internalSet(
6410
        $key,
6411
        &$value,
6412
        bool $checkProperties = true
6413
    ): bool {
6414
        if (
6415 1018
            $checkProperties === true
6416
            &&
6417 1018
            $this->properties !== []
6418
        ) {
6419 87
            $this->checkType($key, $value);
6420
        }
6421
6422 1016
        if ($key === null) {
6423
            return false;
6424
        }
6425
6426 1016
        $this->generatorToArray();
6427
6428
        /** @var array<int|string,mixed> $array */
6429 1016
        $array = &$this->array;
6430
6431
        /**
6432
         * https://github.com/vimeo/psalm/issues/2536
6433
         *
6434
         * @psalm-suppress PossiblyInvalidArgument
6435
         * @psalm-suppress InvalidScalarArgument
6436
         */
6437
        if (
6438 1016
            $this->pathSeparator
6439
            &&
6440 1016
            (string) $key === $key
6441
            &&
6442 1016
            \strpos($key, $this->pathSeparator) !== false
6443
        ) {
6444 6
            $path = \explode($this->pathSeparator, (string) $key);
6445
6446 6
            if ($path !== false) {
6447
                // crawl through the keys
6448 6
                while (\count($path, \COUNT_NORMAL) > 1) {
6449 6
                    $key = \array_shift($path);
6450
6451 6
                    $array = &$array[$key];
6452
                }
6453
6454 6
                $key = \array_shift($path);
6455
            }
6456
        }
6457
6458 1016
        $array[$key] = $value;
6459
6460 1016
        return true;
6461
    }
6462
6463
    /**
6464
     * Convert a object into an array.
6465
     *
6466
     * @param mixed|object $object
6467
     *
6468
     * @return array|mixed
6469
     *
6470
     * @psalm-mutation-free
6471
     */
6472 5
    protected static function objectToArray($object)
6473
    {
6474 5
        if (!\is_object($object)) {
6475 4
            return $object;
6476
        }
6477
6478 5
        $object = \get_object_vars($object);
6479
6480
        /**
6481
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6482
         */
6483 5
        return \array_map(['static', 'objectToArray'], $object);
6484
    }
6485
6486
    /**
6487
     * @param array $data
6488
     * @param bool  $checkPropertiesInConstructor
6489
     *
6490
     * @return void
6491
     *
6492
     * @psalm-param array<mixed,T> $data
6493
     */
6494 1165
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6495
    {
6496 1165
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6497
                                        &&
6498 1165
                                        $checkPropertiesInConstructor === true;
6499
6500 1165
        if ($this->properties !== []) {
6501 78
            foreach ($data as $key => &$valueInner) {
6502 78
                $this->internalSet(
6503 78
                    $key,
6504 78
                    $valueInner,
6505 78
                    $checkPropertiesInConstructor
6506
                );
6507
            }
6508
        } else {
6509
            if (
6510 1098
                $this->checkPropertyTypes === true
6511
                ||
6512 1098
                $checkPropertiesInConstructor === true
6513
            ) {
6514 16
                $this->properties = $this->getPropertiesFromPhpDoc();
6515
            }
6516
6517
            /** @var TypeCheckInterface[] $properties */
6518 1098
            $properties = $this->properties;
6519
6520
            if (
6521 1098
                $this->checkPropertiesMismatchInConstructor === true
6522
                &&
6523 1098
                \count($data) !== 0
6524
                &&
6525 1098
                \count(\array_diff_key($properties, $data)) > 0
6526
            ) {
6527 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...
6528
            }
6529
6530 1097
            foreach ($data as $key => &$valueInner) {
6531 938
                $this->internalSet(
6532 938
                    $key,
6533 938
                    $valueInner,
6534 938
                    $checkPropertiesInConstructor
6535
                );
6536
            }
6537
        }
6538 1158
    }
6539
6540
    /**
6541
     * sorting keys
6542
     *
6543
     * @param array      $elements
6544
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6545
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6546
     *                              <strong>SORT_NATURAL</strong></p>
6547
     *
6548
     * @return $this
6549
     *               <p>(Mutable) Return this Arrayy object.</p>
6550
     *
6551
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6552
     * @psalm-return static<TKey,T>
6553
     */
6554 18
    protected function sorterKeys(
6555
        array &$elements,
6556
        $direction = \SORT_ASC,
6557
        int $strategy = \SORT_REGULAR
6558
    ): self {
6559 18
        $direction = $this->getDirection($direction);
6560
6561 18
        switch ($direction) {
6562 18
            case 'desc':
6563
            case \SORT_DESC:
6564 6
                \krsort($elements, $strategy);
6565
6566 6
                break;
6567 13
            case 'asc':
6568 13
            case \SORT_ASC:
6569
            default:
6570 13
                \ksort($elements, $strategy);
6571
        }
6572
6573 18
        return $this;
6574
    }
6575
6576
    /**
6577
     * @param array      $elements  <p>Warning: used as reference</p>
6578
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6579
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6580
     *                              <strong>SORT_NATURAL</strong></p>
6581
     * @param bool       $keepKeys
6582
     *
6583
     * @return $this
6584
     *               <p>(Mutable) Return this Arrayy object.</p>
6585
     *
6586
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6587
     * @psalm-return static<TKey,T>
6588
     */
6589 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6590
    {
6591 24
        $direction = $this->getDirection($direction);
6592
6593 24
        if (!$strategy) {
6594 24
            $strategy = \SORT_REGULAR;
6595
        }
6596
6597 24
        switch ($direction) {
6598 24
            case 'desc':
6599
            case \SORT_DESC:
6600 13
                if ($keepKeys) {
6601 9
                    \arsort($elements, $strategy);
6602
                } else {
6603 4
                    \rsort($elements, $strategy);
6604
                }
6605
6606 13
                break;
6607 11
            case 'asc':
6608 11
            case \SORT_ASC:
6609
            default:
6610 11
                if ($keepKeys) {
6611 4
                    \asort($elements, $strategy);
6612
                } else {
6613 7
                    \sort($elements, $strategy);
6614
                }
6615
        }
6616
6617 24
        return $this;
6618
    }
6619
6620
    /**
6621
     * @param array $array
6622
     *
6623
     * @return array
6624
     *
6625
     * @psalm-mutation-free
6626
     */
6627 25
    private function getArrayRecursiveHelperArrayy(array $array)
6628
    {
6629 25
        if ($array === []) {
6630
            return [];
6631
        }
6632
6633 25
        \array_walk_recursive(
6634 25
            $array,
6635
            /**
6636
             * @param array|self $item
6637
             *
6638
             * @return void
6639
             */
6640
            static function (&$item) {
6641 25
                if ($item instanceof self) {
6642 1
                    $item = $item->getArray();
6643
                }
6644 25
            }
6645
        );
6646
6647 25
        return $array;
6648
    }
6649
6650
    /**
6651
     * @param int|string|null $key
6652
     * @param mixed           $value
6653
     *
6654
     * @return void
6655
     */
6656 87
    private function checkType($key, $value)
6657
    {
6658
        if (
6659 87
            $key !== null
6660
            &&
6661 87
            isset($this->properties[$key]) === false
6662
            &&
6663 87
            $this->checkPropertiesMismatch === true
6664
        ) {
6665
            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...
6666
        }
6667
6668 87
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6669 76
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6670 17
        } elseif ($key !== null && isset($this->properties[$key])) {
6671 17
            $this->properties[$key]->checkType($value);
6672
        }
6673 85
    }
6674
}
6675