Completed
Push — master ( 6d90b1...2c6270 )
by Lars
01:52
created

Arrayy::only()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

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

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
127
        }
128
129 47
        if ($this->generator !== null) {
130
            $this->generator = clone $this->generator;
131
        }
132 47
    }
133
134
    /**
135
     * Call object as function.
136
     *
137
     * @param mixed $key
138
     *
139
     * @return mixed
140
     */
141 1
    public function __invoke($key = null)
142
    {
143 1
        if ($key !== null) {
144 1
            $this->generatorToArray();
145
146 1
            return $this->array[$key] ?? false;
147
        }
148
149
        return $this->toArray();
150
    }
151
152
    /**
153
     * Whether or not an element exists by key.
154
     *
155
     * @param mixed $key
156
     *
157
     * @return bool
158
     *              <p>True is the key/index exists, otherwise false.</p>
159
     */
160
    public function __isset($key): bool
161
    {
162
        return $this->offsetExists($key);
163
    }
164
165
    /**
166
     * Assigns a value to the specified element.
167
     *
168
     * @param mixed $key
169
     * @param mixed $value
170
     *
171
     * @return void
172
     */
173 2
    public function __set($key, $value)
174
    {
175 2
        $this->internalSet($key, $value);
176 2
    }
177
178
    /**
179
     * magic to string
180
     *
181
     * @return string
182
     */
183 15
    public function __toString(): string
184
    {
185 15
        return $this->toString();
186
    }
187
188
    /**
189
     * Unset element by key.
190
     *
191
     * @param mixed $key
192
     */
193
    public function __unset($key)
194
    {
195
        $this->internalRemove($key);
196
    }
197
198
    /**
199
     * Get a value by key.
200
     *
201
     * @param mixed $key
202
     *
203
     * @return mixed
204
     *               <p>Get a Value from the current array.</p>
205
     */
206 4
    public function &__get($key)
207
    {
208 4
        $return = $this->get($key);
209
210 4
        if (\is_array($return) === true) {
211
            return static::create($return, $this->iteratorClass, false);
212
        }
213
214 4
        return $return;
215
    }
216
217
    /**
218
     * alias: for "Arrayy->append()"
219
     *
220
     * @param mixed $value
221
     *
222
     * @return static
223
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
224
     *
225
     * @see          Arrayy::append()
226
     *
227
     * @psalm-param  T $value
228
     * @psalm-return static<TKey,T>
229
     */
230 3
    public function add($value)
231
    {
232 3
        return $this->append($value);
233
    }
234
235
    /**
236
     * Append a (key) + value to the current array.
237
     *
238
     * @param mixed $value
239
     * @param mixed $key
240
     *
241
     * @return $this
242
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
243
     *
244
     * @psalm-return static<TKey,T>
245
     */
246 13
    public function append($value, $key = null): self
247
    {
248 13
        $this->generatorToArray();
249
250 13
        if ($this->properties !== []) {
251 4
            $this->checkType($key, $value);
252
        }
253
254 12
        if ($key !== null) {
255
            if (
256
                isset($this->array[$key])
257
                &&
258
                \is_array($this->array[$key]) === true
259
            ) {
260
                $this->array[$key][] = $value;
261
            } else {
262
                $this->array[$key] = $value;
263
            }
264
        } else {
265 12
            $this->array[] = $value;
266
        }
267
268 12
        return $this;
269
    }
270
271
    /**
272
     * Sort the entries by value.
273
     *
274
     * @param int $sort_flags [optional] <p>
275
     *                        You may modify the behavior of the sort using the optional
276
     *                        parameter sort_flags, for details
277
     *                        see sort.
278
     *                        </p>
279
     *
280
     * @return $this
281
     *               <p>(Mutable) Return this Arrayy object.</p>
282
     *
283
     * @psalm-return static<TKey,T>
284
     */
285 4
    public function asort(int $sort_flags = 0): self
286
    {
287 4
        $this->generatorToArray();
288
289 4
        \asort($this->array, $sort_flags);
290
291 4
        return $this;
292
    }
293
294
    /**
295
     * Sort the entries by value.
296
     *
297
     * @param int $sort_flags [optional] <p>
298
     *                        You may modify the behavior of the sort using the optional
299
     *                        parameter sort_flags, for details
300
     *                        see sort.
301
     *                        </p>
302
     *
303
     * @return $this
304
     *               <p>(Immutable) Return this Arrayy object.</p>
305
     *
306
     * @psalm-return static<TKey,T>
307
     * @psalm-mutation-free
308
     */
309 4
    public function asortImmutable(int $sort_flags = 0): self
310
    {
311 4
        $that = clone $this;
312
313
        /**
314
         * @psalm-suppress ImpureMethodCall - object is already cloned
315
         */
316 4
        $that->asort($sort_flags);
317
318 4
        return $that;
319
    }
320
321
    /**
322
     * Counts all elements in an array, or something in an object.
323
     *
324
     * <p>
325
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
326
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
327
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
328
     * implemented and used in PHP.
329
     * </p>
330
     *
331
     * @see http://php.net/manual/en/function.count.php
332
     *
333
     * @param int $mode [optional] If the optional mode parameter is set to
334
     *                  COUNT_RECURSIVE (or 1), count
335
     *                  will recursively count the array. This is particularly useful for
336
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
337
     *
338
     * @return int
339
     *             <p>
340
     *             The number of elements in var, which is
341
     *             typically an array, since anything else will have one
342
     *             element.
343
     *             </p>
344
     *             <p>
345
     *             If var is not an array or an object with
346
     *             implemented Countable interface,
347
     *             1 will be returned.
348
     *             There is one exception, if var is &null;,
349
     *             0 will be returned.
350
     *             </p>
351
     *             <p>
352
     *             Caution: count may return 0 for a variable that isn't set,
353
     *             but it may also return 0 for a variable that has been initialized with an
354
     *             empty array. Use isset to test if a variable is set.
355
     *             </p>
356
     * @psalm-mutation-free
357
     */
358 145
    public function count(int $mode = \COUNT_NORMAL): int
359
    {
360
        if (
361 145
            $this->generator
362
            &&
363 145
            $mode === \COUNT_NORMAL
364
        ) {
365 4
            return \iterator_count($this->generator);
366
        }
367
368 141
        return \count($this->toArray(), $mode);
369
    }
370
371
    /**
372
     * Exchange the array for another one.
373
     *
374
     * @param array|static $data
375
     *
376
     * @return array
377
     *
378
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
379
     * @psalm-return array<mixed,mixed>|array<TKey,T>
380
     */
381 1
    public function exchangeArray($data): array
382
    {
383 1
        $this->array = $this->fallbackForArray($data);
384
385 1
        return $this->array;
386
    }
387
388
    /**
389
     * Creates a copy of the ArrayyObject.
390
     *
391
     * @return array
392
     *
393
     * @psalm-return array<mixed,mixed>|array<TKey,T>
394
     */
395 5
    public function getArrayCopy(): array
396
    {
397 5
        $this->generatorToArray();
398
399 5
        return $this->array;
400
    }
401
402
    /**
403
     * Returns a new iterator, thus implementing the \Iterator interface.
404
     *
405
     * @return \Iterator<mixed, mixed>
0 ignored issues
show
Documentation introduced by
The doc-type \Iterator<mixed, could not be parsed: Expected "|" or "end of type", but got "<" at position 9. (view supported doc-types)

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

Loading history...
406
     *                          <p>An iterator for the values in the array.</p>
407
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
408
     */
409 24
    public function getIterator(): \Iterator
410
    {
411 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
412 1
            return $this->generator;
413
        }
414
415 23
        $iterator = $this->getIteratorClass();
416
417 23
        if ($iterator === ArrayyIterator::class) {
418 23
            return new $iterator($this->toArray(), 0, static::class);
419
        }
420
421
        $return = new $iterator($this->toArray());
422
        \assert($return instanceof \Iterator);
423
424
        return $return;
425
    }
426
427
    /**
428
     * Gets the iterator classname for the ArrayObject.
429
     *
430
     * @return string
431
     *
432
     * @psalm-return class-string
433
     */
434 23
    public function getIteratorClass(): string
435
    {
436 23
        return $this->iteratorClass;
437
    }
438
439
    /**
440
     * Sort the entries by key.
441
     *
442
     * @param int $sort_flags [optional] <p>
443
     *                        You may modify the behavior of the sort using the optional
444
     *                        parameter sort_flags, for details
445
     *                        see sort.
446
     *                        </p>
447
     *
448
     * @return $this
449
     *               <p>(Mutable) Return this Arrayy object.</p>
450
     *
451
     * @psalm-return static<TKey,T>
452
     */
453 4
    public function ksort(int $sort_flags = 0): self
454
    {
455 4
        $this->generatorToArray();
456
457 4
        \ksort($this->array, $sort_flags);
458
459 4
        return $this;
460
    }
461
462
    /**
463
     * Sort the entries by key.
464
     *
465
     * @param int $sort_flags [optional] <p>
466
     *                        You may modify the behavior of the sort using the optional
467
     *                        parameter sort_flags, for details
468
     *                        see sort.
469
     *                        </p>
470
     *
471
     * @return $this
472
     *               <p>(Immutable) Return this Arrayy object.</p>
473
     *
474
     * @psalm-return static<TKey,T>
475
     */
476 4
    public function ksortImmutable(int $sort_flags = 0): self
477
    {
478 4
        $that = clone $this;
479
480
        /**
481
         * @psalm-suppress ImpureMethodCall - object is already cloned
482
         */
483 4
        $that->ksort($sort_flags);
484
485 4
        return $that;
486
    }
487
488
    /**
489
     * Sort an array using a case insensitive "natural order" algorithm.
490
     *
491
     * @return $this
492
     *               <p>(Mutable) Return this Arrayy object.</p>
493
     *
494
     * @psalm-return static<TKey,T>
495
     */
496 8
    public function natcasesort(): self
497
    {
498 8
        $this->generatorToArray();
499
500 8
        \natcasesort($this->array);
501
502 8
        return $this;
503
    }
504
505
    /**
506
     * Sort an array using a case insensitive "natural order" algorithm.
507
     *
508
     * @return $this
509
     *               <p>(Immutable) Return this Arrayy object.</p>
510
     *
511
     * @psalm-return static<TKey,T>
512
     * @psalm-mutation-free
513
     */
514 4
    public function natcasesortImmutable(): self
515
    {
516 4
        $that = clone $this;
517
518
        /**
519
         * @psalm-suppress ImpureMethodCall - object is already cloned
520
         */
521 4
        $that->natcasesort();
522
523 4
        return $that;
524
    }
525
526
    /**
527
     * Sort entries using a "natural order" algorithm.
528
     *
529
     * @return $this
530
     *               <p>(Mutable) Return this Arrayy object.</p>
531
     *
532
     * @psalm-return static<TKey,T>
533
     */
534 9
    public function natsort(): self
535
    {
536 9
        $this->generatorToArray();
537
538 9
        \natsort($this->array);
539
540 9
        return $this;
541
    }
542
543
    /**
544
     * Sort entries using a "natural order" algorithm.
545
     *
546
     * @return $this
547
     *               <p>(Immutable) Return this Arrayy object.</p>
548
     *
549
     * @psalm-return static<TKey,T>
550
     * @psalm-mutation-free
551
     */
552 4
    public function natsortImmutable(): self
553
    {
554 4
        $that = clone $this;
555
556
        /**
557
         * @psalm-suppress ImpureMethodCall - object is already cloned
558
         */
559 4
        $that->natsort();
560
561 4
        return $that;
562
    }
563
564
    /**
565
     * Whether or not an offset exists.
566
     *
567
     * @param bool|int|string $offset
568
     *
569
     * @return bool
570
     *
571
     * @noinspection PhpSillyAssignmentInspection
572
     *
573
     * @psalm-mutation-free
574
     */
575 130
    public function offsetExists($offset): bool
576
    {
577 130
        $this->generatorToArray();
578
579 130
        if ($this->array === []) {
580 5
            return false;
581
        }
582
583
        // php cast "bool"-index into "int"-index
584 125
        if ((bool) $offset === $offset) {
585 1
            $offset = (int) $offset;
586
        }
587
588
        /** @var int|string $offset - hint for phpstan */
589 125
        $offset = $offset;
0 ignored issues
show
Bug introduced by
Why assign $offset to itself?

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

This assignement can be removed without consequences.

Loading history...
590
591 125
        $tmpReturn = $this->keyExists($offset);
592
593
        if (
594 125
            $tmpReturn === true
595
            ||
596 125
            \strpos((string) $offset, $this->pathSeparator) === false
597
        ) {
598 123
            return $tmpReturn;
599
        }
600
601 3
        $offsetExists = false;
602
603
        /**
604
         * https://github.com/vimeo/psalm/issues/2536
605
         *
606
         * @psalm-suppress PossiblyInvalidArgument
607
         * @psalm-suppress InvalidScalarArgument
608
         */
609
        if (
610 3
            $this->pathSeparator
611
            &&
612 3
            (string) $offset === $offset
613
            &&
614 3
            \strpos($offset, $this->pathSeparator) !== false
615
        ) {
616 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
617 3
            if ($explodedPath !== false) {
618
                /** @var string $lastOffset - helper for phpstan */
619 3
                $lastOffset = \array_pop($explodedPath);
620 3
                $containerPath = \implode($this->pathSeparator, $explodedPath);
621
622
                /**
623
                 * @psalm-suppress MissingClosureReturnType
624
                 * @psalm-suppress MissingClosureParamType
625
                 */
626 3
                $this->callAtPath(
627 3
                    $containerPath,
628
                    static function ($container) use ($lastOffset, &$offsetExists) {
629 3
                        $offsetExists = \array_key_exists($lastOffset, $container);
630 3
                    }
631
                );
632
            }
633
        }
634
635 3
        return $offsetExists;
636
    }
637
638
    /**
639
     * Returns the value at specified offset.
640
     *
641
     * @param int|string $offset
642
     *
643
     * @return mixed
644
     *               <p>Will return null if the offset did not exists.</p>
645
     */
646 101
    public function offsetGet($offset)
647
    {
648 101
        return $this->offsetExists($offset) ? $this->get($offset) : null;
649
    }
650
651
    /**
652
     * Assigns a value to the specified offset + check the type.
653
     *
654
     * @param int|string|null $offset
655
     * @param mixed           $value
656
     *
657
     * @return void
658
     */
659 18
    public function offsetSet($offset, $value)
660
    {
661 18
        $this->generatorToArray();
662
663 18
        if ($offset === null) {
664 5
            if ($this->properties !== []) {
665 1
                $this->checkType(null, $value);
666
            }
667
668 4
            $this->array[] = $value;
669
        } else {
670 13
            $this->internalSet(
671 13
                $offset,
672 13
                $value,
673 13
                true
674
            );
675
        }
676 17
    }
677
678
    /**
679
     * Unset an offset.
680
     *
681
     * @param int|string $offset
682
     *
683
     * @return void
684
     */
685 12
    public function offsetUnset($offset)
686
    {
687 12
        $this->generatorToArray();
688
689 12
        if ($this->array === []) {
690 3
            return;
691
        }
692
693 10
        if ($this->keyExists($offset)) {
694 7
            unset($this->array[$offset]);
695
696 7
            return;
697
        }
698
699
        /**
700
         * https://github.com/vimeo/psalm/issues/2536
701
         *
702
         * @psalm-suppress PossiblyInvalidArgument
703
         * @psalm-suppress InvalidScalarArgument
704
         */
705
        if (
706 5
            $this->pathSeparator
707
            &&
708 5
            (string) $offset === $offset
709
            &&
710 5
            \strpos($offset, $this->pathSeparator) !== false
711
        ) {
712 2
            $path = \explode($this->pathSeparator, (string) $offset);
713
714 2
            if ($path !== false) {
715 2
                $pathToUnset = \array_pop($path);
716
717
                /**
718
                 * @psalm-suppress MissingClosureReturnType
719
                 * @psalm-suppress MissingClosureParamType
720
                 */
721 2
                $this->callAtPath(
722 2
                    \implode($this->pathSeparator, $path),
723
                    static function (&$offset) use ($pathToUnset) {
724 2
                        unset($offset[$pathToUnset]);
725 2
                    }
726
                );
727
            }
728
        }
729
730 5
        unset($this->array[$offset]);
731 5
    }
732
733
    /**
734
     * Serialize the current "Arrayy"-object.
735
     *
736
     * @return string
737
     */
738 2
    public function serialize(): string
739
    {
740 2
        $this->generatorToArray();
741
742 2
        if (\PHP_VERSION_ID < 70400) {
743 2
            return parent::serialize();
744
        }
745
746
        return \serialize($this);
747
    }
748
749
    /**
750
     * Sets the iterator classname for the current "Arrayy"-object.
751
     *
752
     * @param string $iteratorClass
753
     *
754
     * @throws \InvalidArgumentException
755
     *
756
     * @return void
757
     *
758
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
759
     */
760 1091
    public function setIteratorClass($iteratorClass)
761
    {
762 1091
        if (\class_exists($iteratorClass)) {
763 1091
            $this->iteratorClass = $iteratorClass;
764
765 1091
            return;
766
        }
767
768
        if (\strpos($iteratorClass, '\\') === 0) {
769
            $iteratorClass = '\\' . $iteratorClass;
770
            if (\class_exists($iteratorClass)) {
771
                /**
772
                 * @psalm-suppress PropertyTypeCoercion
773
                 */
774
                $this->iteratorClass = $iteratorClass;
775
776
                return;
777
            }
778
        }
779
780
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
781
    }
782
783
    /**
784
     * Sort the entries with a user-defined comparison function and maintain key association.
785
     *
786
     * @param callable $function
787
     *
788
     * @throws \InvalidArgumentException
789
     *
790
     * @return $this
791
     *               <p>(Mutable) Return this Arrayy object.</p>
792
     *
793
     * @psalm-return static<TKey,T>
794
     */
795 8 View Code Duplication
    public function uasort($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
796
    {
797 8
        if (!\is_callable($function)) {
798
            throw new \InvalidArgumentException('Passed function must be callable');
799
        }
800
801 8
        $this->generatorToArray();
802
803 8
        \uasort($this->array, $function);
804
805 8
        return $this;
806
    }
807
808
    /**
809
     * Sort the entries with a user-defined comparison function and maintain key association.
810
     *
811
     * @param callable $function
812
     *
813
     * @throws \InvalidArgumentException
814
     *
815
     * @return $this
816
     *               <p>(Immutable) Return this Arrayy object.</p>
817
     *
818
     * @psalm-return static<TKey,T>
819
     * @psalm-mutation-free
820
     */
821 4
    public function uasortImmutable($function): self
822
    {
823 4
        $that = clone $this;
824
825
        /**
826
         * @psalm-suppress ImpureMethodCall - object is already cloned
827
         */
828 4
        $that->uasort($function);
829
830 4
        return $that;
831
    }
832
833
    /**
834
     * Sort the entries by keys using a user-defined comparison function.
835
     *
836
     * @param callable $function
837
     *
838
     * @throws \InvalidArgumentException
839
     *
840
     * @return static
841
     *                <p>(Mutable) Return this Arrayy object.</p>
842
     *
843
     * @psalm-return static<TKey,T>
844
     */
845 5
    public function uksort($function): self
846
    {
847 5
        return $this->customSortKeys($function);
848
    }
849
850
    /**
851
     * Sort the entries by keys using a user-defined comparison function.
852
     *
853
     * @param callable $function
854
     *
855
     * @throws \InvalidArgumentException
856
     *
857
     * @return static
858
     *                <p>(Immutable) Return this Arrayy object.</p>
859
     *
860
     * @psalm-return static<TKey,T>
861
     * @psalm-mutation-free
862
     */
863 1
    public function uksortImmutable($function): self
864
    {
865 1
        return $this->customSortKeysImmutable($function);
866
    }
867
868
    /**
869
     * Unserialize an string and return the instance of the "Arrayy"-class.
870
     *
871
     * @param string $string
872
     *
873
     * @return $this
874
     *
875
     * @psalm-return static<TKey,T>
876
     */
877 2
    public function unserialize($string): self
878
    {
879 2
        if (\PHP_VERSION_ID < 70400) {
880 2
            parent::unserialize($string);
881
882 2
            return $this;
883
        }
884
885
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
886
    }
887
888
    /**
889
     * Append a (key) + values to the current array.
890
     *
891
     * @param array $values
892
     * @param mixed $key
893
     *
894
     * @return $this
895
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
896
     *
897
     * @psalm-param  array<mixed,T> $values
898
     * @psalm-param  TKey|null $key
899
     * @psalm-return static<TKey,T>
900
     */
901 1
    public function appendArrayValues(array $values, $key = null)
902
    {
903 1
        $this->generatorToArray();
904
905 1
        if ($key !== null) {
906
            if (
907 1
                isset($this->array[$key])
908
                &&
909 1
                \is_array($this->array[$key]) === true
910
            ) {
911 1
                foreach ($values as $value) {
912 1
                    $this->array[$key][] = $value;
913
                }
914
            } else {
915
                foreach ($values as $value) {
916 1
                    $this->array[$key] = $value;
917
                }
918
            }
919
        } else {
920
            foreach ($values as $value) {
921
                $this->array[] = $value;
922
            }
923
        }
924
925 1
        return $this;
926
    }
927
928
    /**
929
     * Add a suffix to each key.
930
     *
931
     * @param mixed $prefix
932
     *
933
     * @return static
934
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
935
     *
936
     * @psalm-return static<TKey,T>
937
     * @psalm-mutation-free
938
     */
939 10 View Code Duplication
    public function appendToEachKey($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
940
    {
941
        // init
942 10
        $result = [];
943
944 10
        foreach ($this->getGenerator() as $key => $item) {
945 9
            if ($item instanceof self) {
946
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
947 9
            } elseif (\is_array($item) === true) {
948
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
949
                    ->appendToEachKey($prefix)
950
                    ->toArray();
951
            } else {
952 9
                $result[$prefix . $key] = $item;
953
            }
954
        }
955
956 10
        return self::create($result, $this->iteratorClass, false);
957
    }
958
959
    /**
960
     * Add a prefix to each value.
961
     *
962
     * @param mixed $prefix
963
     *
964
     * @return static
965
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
966
     *
967
     * @psalm-return static<TKey,T>
968
     * @psalm-mutation-free
969
     */
970 10 View Code Duplication
    public function appendToEachValue($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
971
    {
972
        // init
973 10
        $result = [];
974
975 10
        foreach ($this->getGenerator() as $key => $item) {
976 9
            if ($item instanceof self) {
977
                $result[$key] = $item->appendToEachValue($prefix);
978 9
            } elseif (\is_array($item) === true) {
979
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
980 9
            } elseif (\is_object($item) === true) {
981 1
                $result[$key] = $item;
982
            } else {
983 9
                $result[$key] = $prefix . $item;
984
            }
985
        }
986
987 10
        return self::create($result, $this->iteratorClass, false);
988
    }
989
990
    /**
991
     * Sort an array in reverse order and maintain index association.
992
     *
993
     * @return $this
994
     *               <p>(Mutable) Return this Arrayy object.</p>
995
     *
996
     * @psalm-return static<TKey,T>
997
     */
998 4
    public function arsort(): self
999
    {
1000 4
        $this->generatorToArray();
1001
1002 4
        \arsort($this->array);
1003
1004 4
        return $this;
1005
    }
1006
1007
    /**
1008
     * Sort an array in reverse order and maintain index association.
1009
     *
1010
     * @return $this
1011
     *               <p>(Immutable) Return this Arrayy object.</p>
1012
     *
1013
     * @psalm-return static<TKey,T>
1014
     * @psalm-mutation-free
1015
     */
1016 10
    public function arsortImmutable(): self
1017
    {
1018 10
        $that = clone $this;
1019
1020 10
        $that->generatorToArray();
1021
1022 10
        \arsort($that->array);
1023
1024 10
        return $that;
1025
    }
1026
1027
    /**
1028
     * Iterate over the current array and execute a callback for each loop.
1029
     *
1030
     * @param \Closure $closure
1031
     *
1032
     * @return static
1033
     *                <p>(Immutable)</p>
1034
     *
1035
     * @psalm-return static<TKey,T>
1036
     * @psalm-mutation-free
1037
     */
1038 2
    public function at(\Closure $closure): self
1039
    {
1040 2
        $that = clone $this;
1041
1042 2
        foreach ($that->getGenerator() as $key => $value) {
1043 2
            $closure($value, $key);
1044
        }
1045
1046 2
        return static::create(
1047 2
            $that->toArray(),
1048 2
            $this->iteratorClass,
1049 2
            false
1050
        );
1051
    }
1052
1053
    /**
1054
     * Returns the average value of the current array.
1055
     *
1056
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1057
     *
1058
     * @return float|int
1059
     *                   <p>The average value.</p>
1060
     * @psalm-mutation-free
1061
     */
1062 10
    public function average($decimals = 0)
1063
    {
1064 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1065
1066 10
        if (!$count) {
1067 2
            return 0;
1068
        }
1069
1070 8
        if ((int) $decimals !== $decimals) {
1071 3
            $decimals = 0;
1072
        }
1073
1074 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1075
    }
1076
1077
    /**
1078
     * Changes all keys in an array.
1079
     *
1080
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1081
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1082
     *
1083
     * @return static
1084
     *                <p>(Immutable)</p>
1085
     *
1086
     * @psalm-return static<TKey,T>
1087
     * @psalm-mutation-free
1088
     */
1089 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1090
    {
1091
        if (
1092 1
            $case !== \CASE_LOWER
1093
            &&
1094 1
            $case !== \CASE_UPPER
1095
        ) {
1096
            $case = \CASE_LOWER;
1097
        }
1098
1099 1
        $return = [];
1100 1
        foreach ($this->getGenerator() as $key => $value) {
1101 1
            if ($case === \CASE_LOWER) {
1102 1
                $key = \mb_strtolower((string) $key);
1103
            } else {
1104 1
                $key = \mb_strtoupper((string) $key);
1105
            }
1106
1107 1
            $return[$key] = $value;
1108
        }
1109
1110 1
        return static::create(
1111 1
            $return,
1112 1
            $this->iteratorClass,
1113 1
            false
1114
        );
1115
    }
1116
1117
    /**
1118
     * Change the path separator of the array wrapper.
1119
     *
1120
     * By default, the separator is: "."
1121
     *
1122
     * @param string $separator <p>Separator to set.</p>
1123
     *
1124
     * @return $this
1125
     *               <p>(Mutable) Return this Arrayy object.</p>
1126
     *
1127
     * @psalm-return static<TKey,T>
1128
     */
1129 11
    public function changeSeparator($separator): self
1130
    {
1131 11
        $this->pathSeparator = $separator;
1132
1133 11
        return $this;
1134
    }
1135
1136
    /**
1137
     * Create a chunked version of the current array.
1138
     *
1139
     * @param int  $size         <p>Size of each chunk.</p>
1140
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1141
     *
1142
     * @return static
1143
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1144
     *
1145
     * @psalm-return static<TKey,T>
1146
     * @psalm-mutation-free
1147
     */
1148 5
    public function chunk($size, $preserveKeys = false): self
1149
    {
1150 5
        return static::create(
1151 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1152 5
            $this->iteratorClass,
1153 5
            false
1154
        );
1155
    }
1156
1157
    /**
1158
     * Clean all falsy values from the current array.
1159
     *
1160
     * @return static
1161
     *                <p>(Immutable)</p>
1162
     *
1163
     * @psalm-return static<TKey,T>
1164
     * @psalm-mutation-free
1165
     */
1166 8
    public function clean(): self
1167
    {
1168 8
        return $this->filter(
1169
            static function ($value) {
1170 7
                return (bool) $value;
1171 8
            }
1172
        );
1173
    }
1174
1175
    /**
1176
     * WARNING!!! -> Clear the current array.
1177
     *
1178
     * @return $this
1179
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1180
     *
1181
     * @psalm-return static<TKey,T>
1182
     */
1183 5
    public function clear(): self
1184
    {
1185 5
        $this->array = [];
1186 5
        $this->generator = null;
1187
1188 5
        return $this;
1189
    }
1190
1191
    /**
1192
     * Check if an item is in the current array.
1193
     *
1194
     * @param float|int|string $value
1195
     * @param bool             $recursive
1196
     * @param bool             $strict
1197
     *
1198
     * @return bool
1199
     * @psalm-mutation-free
1200
     */
1201 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1202
    {
1203 23
        if ($recursive === true) {
1204 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1205
        }
1206
1207 14
        foreach ($this->getGenerator() as $valueFromArray) {
1208 11
            if ($strict) {
1209 11
                if ($value === $valueFromArray) {
1210 11
                    return true;
1211
                }
1212
            } else {
1213
                /** @noinspection NestedPositiveIfStatementsInspection */
1214
                if ($value == $valueFromArray) {
1215 7
                    return true;
1216
                }
1217
            }
1218
        }
1219
1220 7
        return false;
1221
    }
1222
1223
    /**
1224
     * Check if an (case-insensitive) string is in the current array.
1225
     *
1226
     * @param mixed $value
1227
     * @param bool  $recursive
1228
     *
1229
     * @return bool
1230
     * @psalm-mutation-free
1231
     *
1232
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1233
     */
1234 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1235
    {
1236 26
        if ($value === null) {
1237 2
            return false;
1238
        }
1239
1240 24
        if ($recursive === true) {
1241 24
            foreach ($this->getGenerator() as $key => $valueTmp) {
1242 22
                if (\is_array($valueTmp) === true) {
1243 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1244 5
                    if ($return === true) {
1245 5
                        return $return;
1246
                    }
1247 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1248 22
                    return true;
1249
                }
1250
            }
1251
1252 8
            return false;
1253
        }
1254
1255 12
        foreach ($this->getGenerator() as $key => $valueTmp) {
1256 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1257 11
                return true;
1258
            }
1259
        }
1260
1261 4
        return false;
1262
    }
1263
1264
    /**
1265
     * Check if the given key/index exists in the array.
1266
     *
1267
     * @param int|string $key <p>key/index to search for</p>
1268
     *
1269
     * @return bool
1270
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1271
     *
1272
     * @psalm-mutation-free
1273
     */
1274 4
    public function containsKey($key): bool
1275
    {
1276 4
        return $this->offsetExists($key);
1277
    }
1278
1279
    /**
1280
     * Check if all given needles are present in the array as key/index.
1281
     *
1282
     * @param array $needles   <p>The keys you are searching for.</p>
1283
     * @param bool  $recursive
1284
     *
1285
     * @return bool
1286
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1287
     *
1288
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1289
     * @psalm-mutation-free
1290
     */
1291 2
    public function containsKeys(array $needles, $recursive = false): bool
1292
    {
1293 2
        if ($recursive === true) {
1294
            return
1295 2
                \count(
1296 2
                    \array_intersect(
1297 2
                        $needles,
1298 2
                        $this->keys(true)->toArray()
1299
                    ),
1300 2
                    \COUNT_RECURSIVE
1301
                )
1302
                ===
1303 2
                \count(
1304 2
                    $needles,
1305 2
                    \COUNT_RECURSIVE
1306
                );
1307
        }
1308
1309 1
        return \count(
1310 1
            \array_intersect($needles, $this->keys()->toArray()),
1311 1
            \COUNT_NORMAL
1312
        )
1313
               ===
1314 1
               \count(
1315 1
                   $needles,
1316 1
                   \COUNT_NORMAL
1317
               );
1318
    }
1319
1320
    /**
1321
     * Check if all given needles are present in the array as key/index.
1322
     *
1323
     * @param array $needles <p>The keys you are searching for.</p>
1324
     *
1325
     * @return bool
1326
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1327
     *
1328
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1329
     * @psalm-mutation-free
1330
     */
1331 1
    public function containsKeysRecursive(array $needles): bool
1332
    {
1333 1
        return $this->containsKeys($needles, true);
1334
    }
1335
1336
    /**
1337
     * alias: for "Arrayy->contains()"
1338
     *
1339
     * @param float|int|string $value
1340
     *
1341
     * @return bool
1342
     *
1343
     * @see Arrayy::contains()
1344
     * @psalm-mutation-free
1345
     */
1346 9
    public function containsValue($value): bool
1347
    {
1348 9
        return $this->contains($value);
1349
    }
1350
1351
    /**
1352
     * alias: for "Arrayy->contains($value, true)"
1353
     *
1354
     * @param float|int|string $value
1355
     *
1356
     * @return bool
1357
     *
1358
     * @see Arrayy::contains()
1359
     * @psalm-mutation-free
1360
     */
1361 18
    public function containsValueRecursive($value): bool
1362
    {
1363 18
        return $this->contains($value, true);
1364
    }
1365
1366
    /**
1367
     * Check if all given needles are present in the array.
1368
     *
1369
     * @param array $needles
1370
     *
1371
     * @return bool
1372
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1373
     *
1374
     * @psalm-param array<mixed>|array<T> $needles
1375
     * @psalm-mutation-free
1376
     */
1377 1
    public function containsValues(array $needles): bool
1378
    {
1379 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1380
               ===
1381 1
               \count($needles, \COUNT_NORMAL);
1382
    }
1383
1384
    /**
1385
     * Counts all the values of an array
1386
     *
1387
     * @see          http://php.net/manual/en/function.array-count-values.php
1388
     *
1389
     * @return static
1390
     *                <p>
1391
     *                (Immutable)
1392
     *                An associative Arrayy-object of values from input as
1393
     *                keys and their count as value.
1394
     *                </p>
1395
     *
1396
     * @psalm-return static<TKey,T>
1397
     * @psalm-mutation-free
1398
     */
1399 7
    public function countValues(): self
1400
    {
1401 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1402
    }
1403
1404
    /**
1405
     * Creates an Arrayy object.
1406
     *
1407
     * @param mixed  $data
1408
     * @param string $iteratorClass
1409
     * @param bool   $checkPropertiesInConstructor
1410
     *
1411
     * @return static
1412
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1413
     *
1414
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1415
     *
1416
     * @psalm-mutation-free
1417
     */
1418 672
    public static function create(
1419
        $data = [],
1420
        string $iteratorClass = ArrayyIterator::class,
1421
        bool $checkPropertiesInConstructor = true
1422
    ) {
1423 672
        return new static(
1424 672
            $data,
1425 672
            $iteratorClass,
1426 672
            $checkPropertiesInConstructor
1427
        );
1428
    }
1429
1430
    /**
1431
     * WARNING: Creates an Arrayy object by reference.
1432
     *
1433
     * @param array $array
1434
     *
1435
     * @return $this
1436
     *               <p>(Mutable) Return this Arrayy object.</p>
1437
     *
1438
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1439
     */
1440 1
    public function createByReference(array &$array = []): self
1441
    {
1442 1
        $array = $this->fallbackForArray($array);
1443
1444 1
        $this->array = &$array;
1445
1446 1
        return $this;
1447
    }
1448
1449
    /**
1450
     * Create an new instance from a callable function which will return an Generator.
1451
     *
1452
     * @param callable $generatorFunction
1453
     *
1454
     * @return static
1455
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1456
     *
1457
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1458
     *
1459
     * @psalm-mutation-free
1460
     */
1461 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1462
    {
1463 7
        return self::create($generatorFunction);
1464
    }
1465
1466
    /**
1467
     * Create an new instance filled with a copy of values from a "Generator"-object.
1468
     *
1469
     * @param \Generator $generator
1470
     *
1471
     * @return static
1472
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1473
     *
1474
     * @psalm-param \Generator<array-key,mixed> $generator
1475
     *
1476
     * @psalm-mutation-free
1477
     */
1478 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1479
    {
1480 4
        return self::create(\iterator_to_array($generator, true));
1481
    }
1482
1483
    /**
1484
     * Create an new Arrayy object via JSON.
1485
     *
1486
     * @param string $json
1487
     *
1488
     * @return static
1489
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1490
     *
1491
     * @psalm-mutation-free
1492
     */
1493 5
    public static function createFromJson(string $json): self
1494
    {
1495 5
        return static::create(\json_decode($json, true));
1496
    }
1497
1498
    /**
1499
     * Create an new instance filled with values from an object that is iterable.
1500
     *
1501
     * @param \Traversable $object <p>iterable object</p>
1502
     *
1503
     * @return static
1504
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1505
     *
1506
     * @psalm-param \Traversable<array-key,mixed> $object
1507
     *
1508
     * @psalm-mutation-free
1509
     */
1510 4
    public static function createFromObject(\Traversable $object): self
1511
    {
1512
        // init
1513 4
        $arrayy = new static();
1514
1515 4
        if ($object instanceof self) {
1516 4
            $objectArray = $object->getGenerator();
1517
        } else {
1518
            $objectArray = $object;
1519
        }
1520
1521 4
        foreach ($objectArray as $key => $value) {
1522
            /**
1523
             * @psalm-suppress ImpureMethodCall - object is already re-created
1524
             */
1525 3
            $arrayy->internalSet($key, $value);
1526
        }
1527
1528 4
        return $arrayy;
1529
    }
1530
1531
    /**
1532
     * Create an new instance filled with values from an object.
1533
     *
1534
     * @param object $object
1535
     *
1536
     * @return static
1537
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1538
     *
1539
     * @psalm-mutation-free
1540
     */
1541 5
    public static function createFromObjectVars($object): self
1542
    {
1543 5
        return self::create(self::objectToArray($object));
1544
    }
1545
1546
    /**
1547
     * Create an new Arrayy object via string.
1548
     *
1549
     * @param string      $str       <p>The input string.</p>
1550
     * @param string|null $delimiter <p>The boundary string.</p>
1551
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1552
     *                               used.</p>
1553
     *
1554
     * @return static
1555
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1556
     *
1557
     * @psalm-mutation-free
1558
     */
1559 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1560
    {
1561 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1562 1
            \preg_match_all($regEx, $str, $array);
1563
1564 1
            if (!empty($array)) {
1565 1
                $array = $array[0];
1566
            }
1567
        } else {
1568
            /** @noinspection NestedPositiveIfStatementsInspection */
1569 9
            if ($delimiter !== null) {
1570 7
                $array = \explode($delimiter, $str);
1571
            } else {
1572 2
                $array = [$str];
1573
            }
1574
        }
1575
1576
        // trim all string in the array
1577
        /**
1578
         * @psalm-suppress MissingClosureParamType
1579
         */
1580 10
        \array_walk(
1581 10
            $array,
1582
            static function (&$val) {
1583 10
                if ((string) $val === $val) {
1584 10
                    $val = \trim($val);
1585
                }
1586 10
            }
1587
        );
1588
1589 10
        return static::create($array);
1590
    }
1591
1592
    /**
1593
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1594
     *
1595
     * @param \Traversable $traversable
1596
     *
1597
     * @return static
1598
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1599
     *
1600
     * @psalm-param \Traversable<array-key,mixed> $traversable
1601
     *
1602
     * @psalm-mutation-free
1603
     */
1604 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1605
    {
1606 1
        return self::create(\iterator_to_array($traversable, true));
1607
    }
1608
1609
    /**
1610
     * Create an new instance containing a range of elements.
1611
     *
1612
     * @param float|int|string $low  <p>First value of the sequence.</p>
1613
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1614
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1615
     *
1616
     * @return static
1617
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1618
     *
1619
     * @psalm-mutation-free
1620
     */
1621 2
    public static function createWithRange($low, $high, $step = 1): self
1622
    {
1623 2
        return static::create(\range($low, $high, $step));
1624
    }
1625
1626
    /**
1627
     * Gets the element of the array at the current internal iterator position.
1628
     *
1629
     * @return false|mixed
1630
     */
1631
    public function current()
1632
    {
1633
        return \current($this->array);
1634
    }
1635
1636
    /**
1637
     * Custom sort by index via "uksort".
1638
     *
1639
     * @see          http://php.net/manual/en/function.uksort.php
1640
     *
1641
     * @param callable $function
1642
     *
1643
     * @throws \InvalidArgumentException
1644
     *
1645
     * @return $this
1646
     *               <p>(Mutable) Return this Arrayy object.</p>
1647
     *
1648
     * @psalm-return static<TKey,T>
1649
     */
1650 5
    public function customSortKeys(callable $function): self
1651
    {
1652 5
        $this->generatorToArray();
1653
1654 5
        \uksort($this->array, $function);
1655
1656 5
        return $this;
1657
    }
1658
1659
    /**
1660
     * Custom sort by index via "uksort".
1661
     *
1662
     * @see          http://php.net/manual/en/function.uksort.php
1663
     *
1664
     * @param callable $function
1665
     *
1666
     * @throws \InvalidArgumentException
1667
     *
1668
     * @return $this
1669
     *               <p>(Immutable) Return this Arrayy object.</p>
1670
     *
1671
     * @psalm-return static<TKey,T>
1672
     * @psalm-mutation-free
1673
     */
1674 1
    public function customSortKeysImmutable(callable $function): self
1675
    {
1676 1
        $that = clone $this;
1677
1678 1
        $that->generatorToArray();
1679
1680
        /**
1681
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1682
         */
1683 1
        \uksort($that->array, $function);
1684
1685 1
        return $that;
1686
    }
1687
1688
    /**
1689
     * Custom sort by value via "usort".
1690
     *
1691
     * @see          http://php.net/manual/en/function.usort.php
1692
     *
1693
     * @param callable $function
1694
     *
1695
     * @throws \InvalidArgumentException
1696
     *
1697
     * @return $this
1698
     *               <p>(Mutable) Return this Arrayy object.</p>
1699
     *
1700
     * @psalm-return static<TKey,T>
1701
     */
1702 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...
1703
    {
1704 10
        if (\is_callable($function) === false) {
1705
            throw new \InvalidArgumentException('Passed function must be callable');
1706
        }
1707
1708 10
        $this->generatorToArray();
1709
1710 10
        \usort($this->array, $function);
1711
1712 10
        return $this;
1713
    }
1714
1715
    /**
1716
     * Custom sort by value via "usort".
1717
     *
1718
     * @see          http://php.net/manual/en/function.usort.php
1719
     *
1720
     * @param callable $function
1721
     *
1722
     * @throws \InvalidArgumentException
1723
     *
1724
     * @return $this
1725
     *               <p>(Immutable) Return this Arrayy object.</p>
1726
     *
1727
     * @psalm-return static<TKey,T>
1728
     * @psalm-mutation-free
1729
     */
1730 4
    public function customSortValuesImmutable($function): self
1731
    {
1732 4
        $that = clone $this;
1733
1734
        /**
1735
         * @psalm-suppress ImpureMethodCall - object is already cloned
1736
         */
1737 4
        $that->customSortValues($function);
1738
1739 4
        return $that;
1740
    }
1741
1742
    /**
1743
     * Delete the given key or keys.
1744
     *
1745
     * @param int|int[]|string|string[] $keyOrKeys
1746
     *
1747
     * @return void
1748
     */
1749 4
    public function delete($keyOrKeys)
1750
    {
1751 4
        $keyOrKeys = (array) $keyOrKeys;
1752
1753 4
        foreach ($keyOrKeys as $key) {
1754 4
            $this->offsetUnset($key);
1755
        }
1756 4
    }
1757
1758
    /**
1759
     * Return values that are only in the current array.
1760
     *
1761
     * @param array ...$array
1762
     *
1763
     * @return static
1764
     *                <p>(Immutable)</p>
1765
     *
1766
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1767
     * @psalm-return static<TKey,T>
1768
     * @psalm-mutation-free
1769
     */
1770 13
    public function diff(...$array): self
1771
    {
1772 13
        return static::create(
1773 13
            \array_diff($this->toArray(), ...$array),
1774 13
            $this->iteratorClass,
1775 13
            false
1776
        );
1777
    }
1778
1779
    /**
1780
     * Return values that are only in the current array.
1781
     *
1782
     * @param array ...$array
1783
     *
1784
     * @return static
1785
     *                <p>(Immutable)</p>
1786
     *
1787
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1788
     * @psalm-return static<TKey,T>
1789
     * @psalm-mutation-free
1790
     */
1791 8
    public function diffKey(...$array): self
1792
    {
1793 8
        return static::create(
1794 8
            \array_diff_key($this->toArray(), ...$array),
1795 8
            $this->iteratorClass,
1796 8
            false
1797
        );
1798
    }
1799
1800
    /**
1801
     * Return values and Keys that are only in the current array.
1802
     *
1803
     * @param array $array
1804
     *
1805
     * @return static
1806
     *                <p>(Immutable)</p>
1807
     *
1808
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1809
     * @psalm-return static<TKey,T>
1810
     * @psalm-mutation-free
1811
     */
1812 8
    public function diffKeyAndValue(array $array = []): self
1813
    {
1814 8
        return static::create(
1815 8
            \array_diff_assoc($this->toArray(), $array),
1816 8
            $this->iteratorClass,
1817 8
            false
1818
        );
1819
    }
1820
1821
    /**
1822
     * Return values that are only in the current multi-dimensional array.
1823
     *
1824
     * @param array                 $array
1825
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
1826
     *
1827
     * @return static
1828
     *                <p>(Immutable)</p>
1829
     *
1830
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1831
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
1832
     * @psalm-return static<TKey,T>
1833
     * @psalm-mutation-free
1834
     */
1835 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1836
    {
1837
        // init
1838 1
        $result = [];
1839
1840
        if (
1841 1
            $helperVariableForRecursion !== null
1842
            &&
1843 1
            \is_array($helperVariableForRecursion) === true
1844
        ) {
1845
            $arrayForTheLoop = $helperVariableForRecursion;
1846
        } else {
1847 1
            $arrayForTheLoop = $this->getGenerator();
1848
        }
1849
1850 1
        foreach ($arrayForTheLoop as $key => $value) {
1851 1
            if ($value instanceof self) {
1852
                $value = $value->toArray();
1853
            }
1854
1855 1
            if (\array_key_exists($key, $array)) {
1856 1
                if ($value !== $array[$key]) {
1857 1
                    $result[$key] = $value;
1858
                }
1859
            } else {
1860 1
                $result[$key] = $value;
1861
            }
1862
        }
1863
1864 1
        return static::create(
1865 1
            $result,
1866 1
            $this->iteratorClass,
1867 1
            false
1868
        );
1869
    }
1870
1871
    /**
1872
     * Return values that are only in the new $array.
1873
     *
1874
     * @param array $array
1875
     *
1876
     * @return static
1877
     *                <p>(Immutable)</p>
1878
     *
1879
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1880
     * @psalm-return static<TKey,T>
1881
     * @psalm-mutation-free
1882
     */
1883 8
    public function diffReverse(array $array = []): self
1884
    {
1885 8
        return static::create(
1886 8
            \array_diff($array, $this->toArray()),
1887 8
            $this->iteratorClass,
1888 8
            false
1889
        );
1890
    }
1891
1892
    /**
1893
     * Divide an array into two arrays. One with keys and the other with values.
1894
     *
1895
     * @return static
1896
     *                <p>(Immutable)</p>
1897
     *
1898
     * @psalm-return static<TKey,T>
1899
     * @psalm-mutation-free
1900
     */
1901 1
    public function divide(): self
1902
    {
1903 1
        return static::create(
1904
            [
1905 1
                $this->keys(),
1906 1
                $this->values(),
1907
            ],
1908 1
            $this->iteratorClass,
1909 1
            false
1910
        );
1911
    }
1912
1913
    /**
1914
     * Iterate over the current array and modify the array's value.
1915
     *
1916
     * @param \Closure $closure
1917
     *
1918
     * @return static
1919
     *                <p>(Immutable)</p>
1920
     *
1921
     * @psalm-return static<TKey,T>
1922
     * @psalm-mutation-free
1923
     */
1924 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...
1925
    {
1926
        // init
1927 5
        $array = [];
1928
1929 5
        foreach ($this->getGenerator() as $key => $value) {
1930 5
            $array[$key] = $closure($value, $key);
1931
        }
1932
1933 5
        return static::create(
1934 5
            $array,
1935 5
            $this->iteratorClass,
1936 5
            false
1937
        );
1938
    }
1939
1940
    /**
1941
     * Sets the internal iterator to the last element in the array and returns this element.
1942
     *
1943
     * @return mixed
1944
     */
1945
    public function end()
1946
    {
1947
        return \end($this->array);
1948
    }
1949
1950
    /**
1951
     * Check if a value is in the current array using a closure.
1952
     *
1953
     * @param \Closure $closure
1954
     *
1955
     * @return bool
1956
     *              <p>Returns true if the given value is found, false otherwise.</p>
1957
     */
1958 4
    public function exists(\Closure $closure): bool
1959
    {
1960
        // init
1961 4
        $isExists = false;
1962
1963 4
        foreach ($this->getGenerator() as $key => $value) {
1964 3
            if ($closure($value, $key)) {
1965 1
                $isExists = true;
1966
1967 3
                break;
1968
            }
1969
        }
1970
1971 4
        return $isExists;
1972
    }
1973
1974
    /**
1975
     * Fill the array until "$num" with "$default" values.
1976
     *
1977
     * @param int   $num
1978
     * @param mixed $default
1979
     *
1980
     * @return static
1981
     *                <p>(Immutable)</p>
1982
     *
1983
     * @psalm-return static<TKey,T>
1984
     * @psalm-mutation-free
1985
     */
1986 8
    public function fillWithDefaults(int $num, $default = null): self
1987
    {
1988 8
        if ($num < 0) {
1989 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1990
        }
1991
1992 7
        $this->generatorToArray();
1993
1994 7
        $tmpArray = $this->array;
1995
1996 7
        $count = \count($tmpArray);
1997
1998 7
        while ($count < $num) {
1999 4
            $tmpArray[] = $default;
2000 4
            ++$count;
2001
        }
2002
2003 7
        return static::create(
2004 7
            $tmpArray,
2005 7
            $this->iteratorClass,
2006 7
            false
2007
        );
2008
    }
2009
2010
    /**
2011
     * Find all items in an array that pass the truth test.
2012
     *
2013
     * @param \Closure|null $closure [optional] <p>
2014
     *                               The callback function to use
2015
     *                               </p>
2016
     *                               <p>
2017
     *                               If no callback is supplied, all entries of
2018
     *                               input equal to false (see
2019
     *                               converting to
2020
     *                               boolean) will be removed.
2021
     *                               </p>
2022
     * @param int           $flag    [optional] <p>
2023
     *                               Flag determining what arguments are sent to <i>callback</i>:
2024
     *                               </p><ul>
2025
     *                               <li>
2026
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
2027
     *                               to <i>callback</i> instead of the value</span>
2028
     *                               </li>
2029
     *                               <li>
2030
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
2031
     *                               arguments to <i>callback</i> instead of the value</span>
2032
     *                               </li>
2033
     *                               </ul>
2034
     *
2035
     * @return static
2036
     *                <p>(Immutable)</p>
2037
     *
2038
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
2039
     * @psalm-return static<TKey,T>
2040
     * @psalm-mutation-free
2041
     */
2042 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2043
    {
2044 12
        if (!$closure) {
2045 1
            return $this->clean();
2046
        }
2047
2048 12
        return static::create(
2049 12
            \array_filter($this->toArray(), $closure, $flag),
2050 12
            $this->iteratorClass,
2051 12
            false
2052
        );
2053
    }
2054
2055
    /**
2056
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2057
     * property within that.
2058
     *
2059
     * @param string          $property
2060
     * @param string|string[] $value
2061
     * @param string          $comparisonOp
2062
     *                                      <p>
2063
     *                                      'eq' (equals),<br />
2064
     *                                      'gt' (greater),<br />
2065
     *                                      'gte' || 'ge' (greater or equals),<br />
2066
     *                                      'lt' (less),<br />
2067
     *                                      'lte' || 'le' (less or equals),<br />
2068
     *                                      'ne' (not equals),<br />
2069
     *                                      'contains',<br />
2070
     *                                      'notContains',<br />
2071
     *                                      'newer' (via strtotime),<br />
2072
     *                                      'older' (via strtotime),<br />
2073
     *                                      </p>
2074
     *
2075
     * @return static
2076
     *                <p>(Immutable)</p>
2077
     *
2078
     * @psalm-return static<TKey,T>
2079
     * @psalm-mutation-free
2080
     *
2081
     * @psalm-suppress MissingClosureReturnType
2082
     * @psalm-suppress MissingClosureParamType
2083
     */
2084 1
    public function filterBy(
2085
        string $property,
2086
        $value,
2087
        string $comparisonOp = null
2088
    ): self {
2089 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...
2090 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2091
        }
2092
2093
        $ops = [
2094
            'eq' => static function ($item, $prop, $value): bool {
2095 1
                return $item[$prop] === $value;
2096 1
            },
2097
            'gt' => static function ($item, $prop, $value): bool {
2098
                return $item[$prop] > $value;
2099 1
            },
2100
            'ge' => static function ($item, $prop, $value): bool {
2101
                return $item[$prop] >= $value;
2102 1
            },
2103
            'gte' => static function ($item, $prop, $value): bool {
2104
                return $item[$prop] >= $value;
2105 1
            },
2106
            'lt' => static function ($item, $prop, $value): bool {
2107 1
                return $item[$prop] < $value;
2108 1
            },
2109
            'le' => static function ($item, $prop, $value): bool {
2110
                return $item[$prop] <= $value;
2111 1
            },
2112
            'lte' => static function ($item, $prop, $value): bool {
2113
                return $item[$prop] <= $value;
2114 1
            },
2115
            'ne' => static function ($item, $prop, $value): bool {
2116
                return $item[$prop] !== $value;
2117 1
            },
2118
            'contains' => static function ($item, $prop, $value): bool {
2119 1
                return \in_array($item[$prop], (array) $value, true);
2120 1
            },
2121
            'notContains' => static function ($item, $prop, $value): bool {
2122
                return !\in_array($item[$prop], (array) $value, true);
2123 1
            },
2124
            'newer' => static function ($item, $prop, $value): bool {
2125
                return \strtotime($item[$prop]) > \strtotime($value);
2126 1
            },
2127
            'older' => static function ($item, $prop, $value): bool {
2128
                return \strtotime($item[$prop]) < \strtotime($value);
2129 1
            },
2130
        ];
2131
2132 1
        $result = \array_values(
2133 1
            \array_filter(
2134 1
                $this->toArray(false, true),
2135
                static function ($item) use (
2136 1
                    $property,
2137 1
                    $value,
2138 1
                    $ops,
2139 1
                    $comparisonOp
2140
                ) {
2141 1
                    $item = (array) $item;
2142 1
                    $itemArrayy = static::create($item);
2143 1
                    $item[$property] = $itemArrayy->get($property, []);
2144
2145 1
                    return $ops[$comparisonOp]($item, $property, $value);
2146 1
                }
2147
            )
2148
        );
2149
2150 1
        return static::create(
2151 1
            $result,
2152 1
            $this->iteratorClass,
2153 1
            false
2154
        );
2155
    }
2156
2157
    /**
2158
     * Find the first item in an array that passes the truth test,
2159
     *  otherwise return false
2160
     *
2161
     * @param \Closure $closure
2162
     *
2163
     * @return false|mixed
2164
     *                     <p>Return false if we did not find the value.</p>
2165
     */
2166 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...
2167
    {
2168 8
        foreach ($this->getGenerator() as $key => $value) {
2169 6
            if ($closure($value, $key)) {
2170 6
                return $value;
2171
            }
2172
        }
2173
2174 3
        return false;
2175
    }
2176
2177
    /**
2178
     * find by ...
2179
     *
2180
     * @param string          $property
2181
     * @param string|string[] $value
2182
     * @param string          $comparisonOp
2183
     *
2184
     * @return static
2185
     *                <p>(Immutable)</p>
2186
     *
2187
     * @psalm-return static<TKey,T>
2188
     * @psalm-mutation-free
2189
     */
2190 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2191
    {
2192 1
        return $this->filterBy($property, $value, $comparisonOp);
2193
    }
2194
2195
    /**
2196
     * Get the first value from the current array.
2197
     *
2198
     * @return mixed
2199
     *               <p>Return null if there wasn't a element.</p>
2200
     */
2201 21
    public function first()
2202
    {
2203 21
        $key_first = $this->firstKey();
2204 21
        if ($key_first === null) {
2205 3
            return null;
2206
        }
2207
2208 18
        return $this->get($key_first);
2209
    }
2210
2211
    /**
2212
     * Get the first key from the current array.
2213
     *
2214
     * @return mixed
2215
     *               <p>Return null if there wasn't a element.</p>
2216
     * @psalm-mutation-free
2217
     */
2218 28
    public function firstKey()
2219
    {
2220 28
        $this->generatorToArray();
2221
2222 28
        return \array_key_first($this->array);
2223
    }
2224
2225
    /**
2226
     * Get the first value(s) from the current array.
2227
     * And will return an empty array if there was no first entry.
2228
     *
2229
     * @param int|null $number <p>How many values you will take?</p>
2230
     *
2231
     * @return static
2232
     *                <p>(Immutable)</p>
2233
     *
2234
     * @psalm-return static<TKey,T>
2235
     * @psalm-mutation-free
2236
     */
2237 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...
2238
    {
2239 37
        $arrayTmp = $this->toArray();
2240
2241 37
        if ($number === null) {
2242 14
            $array = (array) \array_shift($arrayTmp);
2243
        } else {
2244 23
            $number = (int) $number;
2245 23
            $array = \array_splice($arrayTmp, 0, $number);
2246
        }
2247
2248 37
        return static::create(
2249 37
            $array,
2250 37
            $this->iteratorClass,
2251 37
            false
2252
        );
2253
    }
2254
2255
    /**
2256
     * Get the first value(s) from the current array.
2257
     * And will return an empty array if there was no first entry.
2258
     *
2259
     * @param int|null $number <p>How many values you will take?</p>
2260
     *
2261
     * @return static
2262
     *                <p>(Immutable)</p>
2263
     *
2264
     * @psalm-return static<TKey,T>
2265
     * @psalm-mutation-free
2266
     */
2267 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...
2268
    {
2269 3
        $arrayTmp = $this->keys()->toArray();
2270
2271 3
        if ($number === null) {
2272
            $array = (array) \array_shift($arrayTmp);
2273
        } else {
2274 3
            $number = (int) $number;
2275 3
            $array = \array_splice($arrayTmp, 0, $number);
2276
        }
2277
2278 3
        return static::create(
2279 3
            $array,
2280 3
            $this->iteratorClass,
2281 3
            false
2282
        );
2283
    }
2284
2285
    /**
2286
     * Get and rmove the first value(s) from the current array.
2287
     * And will return an empty array if there was no first entry.
2288
     *
2289
     * @param int|null $number <p>How many values you will take?</p>
2290
     *
2291
     * @return $this
2292
     *               <p>(Mutable)</p>
2293
     *
2294
     * @psalm-return static<TKey,T>
2295
     */
2296 34
    public function firstsMutable(int $number = null): self
2297
    {
2298 34
        $this->generatorToArray();
2299
2300 34
        if ($number === null) {
2301 19
            $this->array = (array) \array_shift($this->array);
2302
        } else {
2303 15
            $number = (int) $number;
2304 15
            $this->array = \array_splice($this->array, 0, $number);
2305
        }
2306
2307 34
        return $this;
2308
    }
2309
2310
    /**
2311
     * Exchanges all keys with their associated values in an array.
2312
     *
2313
     * @return static
2314
     *                <p>(Immutable)</p>
2315
     *
2316
     * @psalm-return static<TKey,T>
2317
     * @psalm-mutation-free
2318
     */
2319 1
    public function flip(): self
2320
    {
2321 1
        return static::create(
2322 1
            \array_flip($this->toArray()),
2323 1
            $this->iteratorClass,
2324 1
            false
2325
        );
2326
    }
2327
2328
    /**
2329
     * Get a value from an array (optional using dot-notation).
2330
     *
2331
     * @param mixed $key      <p>The key to look for.</p>
2332
     * @param mixed $fallback <p>Value to fallback to.</p>
2333
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
2334
     *                        class.</p>
2335
     *
2336
     * @return mixed|static
2337
     *
2338
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2339
     * @psalm-mutation-free
2340
     */
2341 172
    public function get($key, $fallback = null, array $array = null)
2342
    {
2343 172
        if ($array !== null) {
2344 4
            $usedArray = $array;
2345
        } else {
2346 169
            $this->generatorToArray();
2347
2348 169
            $usedArray = $this->array;
2349
        }
2350
2351 172
        if ($key === null) {
2352 1
            return static::create(
2353 1
                $usedArray,
2354 1
                $this->iteratorClass,
2355 1
                false
2356
            );
2357
        }
2358
2359
        // php cast "bool"-index into "int"-index
2360 172
        if ((bool) $key === $key) {
2361 3
            $key = (int) $key;
2362
        }
2363
2364 172
        if (\array_key_exists($key, $usedArray) === true) {
2365 162
            if (\is_array($usedArray[$key]) === true) {
2366 11
                return static::create(
2367 11
                    $usedArray[$key],
2368 11
                    $this->iteratorClass,
2369 11
                    false
2370
                );
2371
            }
2372
2373 154
            return $usedArray[$key];
2374
        }
2375
2376
        // crawl through array, get key according to object or not
2377 24
        $usePath = false;
2378
        if (
2379 24
            $this->pathSeparator
2380
            &&
2381 24
            (string) $key === $key
2382
            &&
2383 24
            \strpos($key, $this->pathSeparator) !== false
2384
        ) {
2385 7
            $segments = \explode($this->pathSeparator, (string) $key);
2386 7
            if ($segments !== false) {
2387 7
                $usePath = true;
2388
2389 7
                foreach ($segments as $segment) {
2390
                    if (
2391
                        (
2392 7
                            \is_array($usedArray) === true
2393
                            ||
2394 7
                            $usedArray instanceof \ArrayAccess
2395
                        )
2396
                        &&
2397 7
                        isset($usedArray[$segment])
2398
                    ) {
2399 7
                        $usedArray = $usedArray[$segment];
2400
2401 7
                        continue;
2402
                    }
2403
2404
                    if (
2405 6
                        \is_object($usedArray) === true
2406
                        &&
2407 6
                        \property_exists($usedArray, $segment)
2408
                    ) {
2409 1
                        $usedArray = $usedArray->{$segment};
2410
2411 1
                        continue;
2412
                    }
2413
2414 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2415
                }
2416
            }
2417
        }
2418
2419 24
        if (!$usePath && !isset($usedArray[$key])) {
2420 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2421
        }
2422
2423 7
        if (\is_array($usedArray) === true) {
2424 1
            return static::create(
2425 1
                $usedArray,
2426 1
                $this->iteratorClass,
2427 1
                false
2428
            );
2429
        }
2430
2431 7
        return $usedArray;
2432
    }
2433
2434
    /**
2435
     * alias: for "Arrayy->toArray()"
2436
     *
2437
     * @return array
2438
     *
2439
     * @see          Arrayy::getArray()
2440
     *
2441
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2442
     */
2443 1
    public function getAll(): array
2444
    {
2445 1
        return $this->toArray();
2446
    }
2447
2448
    /**
2449
     * Get the current array from the "Arrayy"-object.
2450
     *
2451
     * alias for "toArray()"
2452
     *
2453
     * @param bool $convertAllArrayyElements <p>
2454
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2455
     *                                       </p>
2456
     * @param bool $preserveKeys             <p>
2457
     *                                       e.g.: A generator maybe return the same key more then once,
2458
     *                                       so maybe you will ignore the keys.
2459
     *                                       </p>
2460
     *
2461
     * @return array
2462
     *
2463
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2464
     * @psalm-mutation-free
2465
     *
2466
     * @see Arrayy::toArray()
2467
     */
2468 481
    public function getArray(
2469
        bool $convertAllArrayyElements = false,
2470
        bool $preserveKeys = true
2471
    ): array {
2472 481
        return $this->toArray(
2473 481
            $convertAllArrayyElements,
2474 481
            $preserveKeys
2475
        );
2476
    }
2477
2478
    /**
2479
     * Get the current array from the "Arrayy"-object as list.
2480
     *
2481
     * alias for "toList()"
2482
     *
2483
     * @param bool $convertAllArrayyElements <p>
2484
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2485
     *                                       </p>
2486
     *
2487
     * @return array
2488
     *
2489
     * @psalm-return array<int,mixed>|array<int,T>
2490
     * @psalm-mutation-free
2491
     *
2492
     * @see Arrayy::toList()
2493
     */
2494 1
    public function getList(bool $convertAllArrayyElements = false): array
2495
    {
2496 1
        return $this->toList($convertAllArrayyElements);
2497
    }
2498
2499
    /**
2500
     * Returns the values from a single column of the input array, identified by
2501
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2502
     *
2503
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2504
     * array by the values from the $indexKey column in the input array.
2505
     *
2506
     * @param mixed $columnKey
2507
     * @param mixed $indexKey
2508
     *
2509
     * @return static
2510
     *                <p>(Immutable)</p>
2511
     *
2512
     * @psalm-return static<TKey,T>
2513
     * @psalm-mutation-free
2514
     */
2515 1
    public function getColumn($columnKey = null, $indexKey = null): self
2516
    {
2517 1
        return static::create(
2518 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2519 1
            $this->iteratorClass,
2520 1
            false
2521
        );
2522
    }
2523
2524
    /**
2525
     * Get the current array from the "Arrayy"-object as generator.
2526
     *
2527
     * @return \Generator
2528
     *
2529
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2530
     * @psalm-mutation-free
2531
     */
2532 974
    public function getGenerator(): \Generator
2533
    {
2534 974
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2535 42
            yield from $this->generator;
2536
        }
2537
2538 974
        yield from $this->array;
2539 930
    }
2540
2541
    /**
2542
     * alias: for "Arrayy->keys()"
2543
     *
2544
     * @return static
2545
     *                <p>(Immutable)</p>
2546
     *
2547
     * @see          Arrayy::keys()
2548
     *
2549
     * @psalm-return static<array-key,TKey>
2550
     * @psalm-mutation-free
2551
     */
2552 2
    public function getKeys()
2553
    {
2554 2
        return $this->keys();
2555
    }
2556
2557
    /**
2558
     * Get the current array from the "Arrayy"-object as object.
2559
     *
2560
     * @return \stdClass
2561
     */
2562 4
    public function getObject(): \stdClass
2563
    {
2564 4
        return self::arrayToObject($this->toArray());
2565
    }
2566
2567
    /**
2568
     * alias: for "Arrayy->randomImmutable()"
2569
     *
2570
     * @return static
2571
     *                <p>(Immutable)</p>
2572
     *
2573
     * @see          Arrayy::randomImmutable()
2574
     *
2575
     * @psalm-return static<int|array-key,T>
2576
     */
2577 4
    public function getRandom(): self
2578
    {
2579 4
        return $this->randomImmutable();
2580
    }
2581
2582
    /**
2583
     * alias: for "Arrayy->randomKey()"
2584
     *
2585
     * @return mixed
2586
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2587
     *
2588
     * @see Arrayy::randomKey()
2589
     */
2590 3
    public function getRandomKey()
2591
    {
2592 3
        return $this->randomKey();
2593
    }
2594
2595
    /**
2596
     * alias: for "Arrayy->randomKeys()"
2597
     *
2598
     * @param int $number
2599
     *
2600
     * @return static
2601
     *                <p>(Immutable)</p>
2602
     *
2603
     * @see          Arrayy::randomKeys()
2604
     *
2605
     * @psalm-return static<TKey,T>
2606
     */
2607 8
    public function getRandomKeys(int $number): self
2608
    {
2609 8
        return $this->randomKeys($number);
2610
    }
2611
2612
    /**
2613
     * alias: for "Arrayy->randomValue()"
2614
     *
2615
     * @return mixed
2616
     *               <p>Get a random value or null if there wasn't a value.</p>
2617
     *
2618
     * @see Arrayy::randomValue()
2619
     */
2620 3
    public function getRandomValue()
2621
    {
2622 3
        return $this->randomValue();
2623
    }
2624
2625
    /**
2626
     * alias: for "Arrayy->randomValues()"
2627
     *
2628
     * @param int $number
2629
     *
2630
     * @return static
2631
     *                <p>(Immutable)</p>
2632
     *
2633
     * @see          Arrayy::randomValues()
2634
     *
2635
     * @psalm-return static<TKey,T>
2636
     */
2637 6
    public function getRandomValues(int $number): self
2638
    {
2639 6
        return $this->randomValues($number);
2640
    }
2641
2642
    /**
2643
     * Gets all values.
2644
     *
2645
     * @return static
2646
     *                <p>The values of all elements in this array, in the order they
2647
     *                appear in the array.</p>
2648
     *
2649
     * @psalm-return static<TKey,T>
2650
     */
2651 4
    public function getValues()
2652
    {
2653 4
        $this->generatorToArray(false);
2654
2655 4
        return static::create(
2656 4
            \array_values($this->array),
2657 4
            $this->iteratorClass,
2658 4
            false
2659
        );
2660
    }
2661
2662
    /**
2663
     * Gets all values via Generator.
2664
     *
2665
     * @return \Generator
2666
     *                    <p>The values of all elements in this array, in the order they
2667
     *                    appear in the array as Generator.</p>
2668
     *
2669
     * @psalm-return \Generator<TKey,T>
2670
     */
2671 4
    public function getValuesYield(): \Generator
2672
    {
2673 4
        yield from $this->getGenerator();
2674 4
    }
2675
2676
    /**
2677
     * Group values from a array according to the results of a closure.
2678
     *
2679
     * @param callable|string $grouper  <p>A callable function name.</p>
2680
     * @param bool            $saveKeys
2681
     *
2682
     * @return static
2683
     *                <p>(Immutable)</p>
2684
     *
2685
     * @psalm-return static<TKey,T>
2686
     * @psalm-mutation-free
2687
     */
2688 4
    public function group($grouper, bool $saveKeys = false): self
2689
    {
2690
        // init
2691 4
        $result = [];
2692
2693
        // Iterate over values, group by property/results from closure.
2694 4
        foreach ($this->getGenerator() as $key => $value) {
2695 4
            if (\is_callable($grouper) === true) {
2696 3
                $groupKey = $grouper($value, $key);
2697
            } else {
2698 1
                $groupKey = $this->get($grouper);
2699
            }
2700
2701 4
            $newValue = $this->get($groupKey, null, $result);
2702
2703 4
            if ($groupKey instanceof self) {
2704
                $groupKey = $groupKey->toArray();
2705
            }
2706
2707 4
            if ($newValue instanceof self) {
2708 4
                $newValue = $newValue->toArray();
2709
            }
2710
2711
            // Add to results.
2712 4
            if ($groupKey !== null) {
2713 3
                if ($saveKeys) {
2714 2
                    $result[$groupKey] = $newValue;
2715 2
                    $result[$groupKey][$key] = $value;
2716
                } else {
2717 1
                    $result[$groupKey] = $newValue;
2718 4
                    $result[$groupKey][] = $value;
2719
                }
2720
            }
2721
        }
2722
2723 4
        return static::create(
2724 4
            $result,
2725 4
            $this->iteratorClass,
2726 4
            false
2727
        );
2728
    }
2729
2730
    /**
2731
     * Check if an array has a given key.
2732
     *
2733
     * @param mixed $key
2734
     *
2735
     * @return bool
2736
     */
2737 23
    public function has($key): bool
2738
    {
2739 23
        static $UN_FOUND = null;
2740
2741 23
        if ($UN_FOUND === null) {
2742
            // Generate unique string to use as marker.
2743 1
            $UN_FOUND = \uniqid('arrayy', true);
2744
        }
2745
2746 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2747
    }
2748
2749
    /**
2750
     * Check if an array has a given value.
2751
     *
2752
     * INFO: if you need to search recursive please use ```contains()```
2753
     *
2754
     * @param mixed $value
2755
     *
2756
     * @return bool
2757
     */
2758 1
    public function hasValue($value): bool
2759
    {
2760 1
        return $this->contains($value);
2761
    }
2762
2763
    /**
2764
     * Implodes the values of this array.
2765
     *
2766
     * @param string $glue
2767
     *
2768
     * @return string
2769
     * @psalm-mutation-free
2770
     */
2771 28
    public function implode(string $glue = ''): string
2772
    {
2773 28
        return $this->implode_recursive($glue, $this->toArray(), false);
2774
    }
2775
2776
    /**
2777
     * Implodes the keys of this array.
2778
     *
2779
     * @param string $glue
2780
     *
2781
     * @return string
2782
     * @psalm-mutation-free
2783
     */
2784 8
    public function implodeKeys(string $glue = ''): string
2785
    {
2786 8
        return $this->implode_recursive($glue, $this->toArray(), true);
2787
    }
2788
2789
    /**
2790
     * Given a list and an iterate-function that returns
2791
     * a key for each element in the list (or a property name),
2792
     * returns an object with an index of each item.
2793
     *
2794
     * @param mixed $key
2795
     *
2796
     * @return static
2797
     *                <p>(Immutable)</p>
2798
     *
2799
     * @psalm-return static<TKey,T>
2800
     * @psalm-mutation-free
2801
     */
2802 4
    public function indexBy($key): self
2803
    {
2804
        // init
2805 4
        $results = [];
2806
2807 4
        foreach ($this->getGenerator() as $a) {
2808 4
            if (\array_key_exists($key, $a) === true) {
2809 4
                $results[$a[$key]] = $a;
2810
            }
2811
        }
2812
2813 4
        return static::create(
2814 4
            $results,
2815 4
            $this->iteratorClass,
2816 4
            false
2817
        );
2818
    }
2819
2820
    /**
2821
     * alias: for "Arrayy->searchIndex()"
2822
     *
2823
     * @param mixed $value <p>The value to search for.</p>
2824
     *
2825
     * @return false|mixed
2826
     *
2827
     * @see Arrayy::searchIndex()
2828
     */
2829 4
    public function indexOf($value)
2830
    {
2831 4
        return $this->searchIndex($value);
2832
    }
2833
2834
    /**
2835
     * Get everything but the last..$to items.
2836
     *
2837
     * @param int $to
2838
     *
2839
     * @return static
2840
     *                <p>(Immutable)</p>
2841
     *
2842
     * @psalm-return static<TKey,T>
2843
     * @psalm-mutation-free
2844
     */
2845 12
    public function initial(int $to = 1): self
2846
    {
2847 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
2848
    }
2849
2850
    /**
2851
     * Return an array with all elements found in input array.
2852
     *
2853
     * @param array $search
2854
     * @param bool  $keepKeys
2855
     *
2856
     * @return static
2857
     *                <p>(Immutable)</p>
2858
     *
2859
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
2860
     * @psalm-return static<TKey,T>
2861
     * @psalm-mutation-free
2862
     */
2863 4
    public function intersection(array $search, bool $keepKeys = false): self
2864
    {
2865 4
        if ($keepKeys) {
2866
            /**
2867
             * @psalm-suppress MissingClosureReturnType
2868
             * @psalm-suppress MissingClosureParamType
2869
             */
2870 1
            return static::create(
2871 1
                \array_uintersect(
2872 1
                    $this->toArray(),
2873 1
                    $search,
2874
                    static function ($a, $b) {
2875 1
                        return $a === $b ? 0 : -1;
2876 1
                    }
2877
                ),
2878 1
                $this->iteratorClass,
2879 1
                false
2880
            );
2881
        }
2882
2883 3
        return static::create(
2884 3
            \array_values(\array_intersect($this->toArray(), $search)),
2885 3
            $this->iteratorClass,
2886 3
            false
2887
        );
2888
    }
2889
2890
    /**
2891
     * Return an array with all elements found in input array.
2892
     *
2893
     * @param array ...$array
2894
     *
2895
     * @return static
2896
     *                <p>(Immutable)</p>
2897
     *
2898
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2899
     * @psalm-return static<TKey,T>
2900
     * @psalm-mutation-free
2901
     */
2902 1
    public function intersectionMulti(...$array): self
2903
    {
2904 1
        return static::create(
2905 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
2906 1
            $this->iteratorClass,
2907 1
            false
2908
        );
2909
    }
2910
2911
    /**
2912
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2913
     *
2914
     * @param array $search
2915
     *
2916
     * @return bool
2917
     *
2918
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
2919
     */
2920 1
    public function intersects(array $search): bool
2921
    {
2922 1
        return $this->intersection($search)->count() > 0;
2923
    }
2924
2925
    /**
2926
     * Invoke a function on all of an array's values.
2927
     *
2928
     * @param callable $callable
2929
     * @param mixed    $arguments
2930
     *
2931
     * @return static
2932
     *                <p>(Immutable)</p>
2933
     *
2934
     * @psalm-param  callable(T=,mixed):mixed $callable
2935
     * @psalm-return static<TKey,T>
2936
     * @psalm-mutation-free
2937
     */
2938 1
    public function invoke($callable, $arguments = []): self
2939
    {
2940
        // If one argument given for each iteration, create an array for it.
2941 1
        if (\is_array($arguments) === false) {
2942 1
            $arguments = \array_fill(
2943 1
                0,
2944 1
                $this->count(),
2945 1
                $arguments
2946
            );
2947
        }
2948
2949
        // If the callable has arguments, pass them.
2950 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...
2951 1
            $array = \array_map($callable, $this->toArray(), $arguments);
2952
        } else {
2953 1
            $array = $this->map($callable);
2954
        }
2955
2956 1
        return static::create(
2957 1
            $array,
2958 1
            $this->iteratorClass,
2959 1
            false
2960
        );
2961
    }
2962
2963
    /**
2964
     * Check whether array is associative or not.
2965
     *
2966
     * @param bool $recursive
2967
     *
2968
     * @return bool
2969
     *              <p>Returns true if associative, false otherwise.</p>
2970
     */
2971 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...
2972
    {
2973 15
        if ($this->isEmpty()) {
2974 3
            return false;
2975
        }
2976
2977 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2978 13
            if ((string) $key !== $key) {
2979 13
                return false;
2980
            }
2981
        }
2982
2983 3
        return true;
2984
    }
2985
2986
    /**
2987
     * Check if a given key or keys are empty.
2988
     *
2989
     * @param int|int[]|string|string[]|null $keys
2990
     *
2991
     * @return bool
2992
     *              <p>Returns true if empty, false otherwise.</p>
2993
     * @psalm-mutation-free
2994
     */
2995 38
    public function isEmpty($keys = null): bool
2996
    {
2997 38
        if ($this->generator) {
2998
            return $this->toArray() === [];
2999
        }
3000
3001 38
        if ($keys === null) {
3002 38
            return $this->array === [];
3003
        }
3004
3005
        foreach ((array) $keys as $key) {
3006
            if (!empty($this->get($key))) {
3007
                return false;
3008
            }
3009
        }
3010
3011
        return true;
3012
    }
3013
3014
    /**
3015
     * Check if the current array is equal to the given "$array" or not.
3016
     *
3017
     * @param array $array
3018
     *
3019
     * @return bool
3020
     *
3021
     * @psalm-param array<mixed,mixed> $array
3022
     */
3023 1
    public function isEqual(array $array): bool
3024
    {
3025 1
        return $this->toArray() === $array;
3026
    }
3027
3028
    /**
3029
     * Check if the current array is a multi-array.
3030
     *
3031
     * @return bool
3032
     */
3033 22
    public function isMultiArray(): bool
3034
    {
3035
        return !(
3036 22
            \count($this->toArray(), \COUNT_NORMAL)
3037
            ===
3038 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3039
        );
3040
    }
3041
3042
    /**
3043
     * Check whether array is numeric or not.
3044
     *
3045
     * @return bool
3046
     *              <p>Returns true if numeric, false otherwise.</p>
3047
     */
3048 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...
3049
    {
3050 5
        if ($this->isEmpty()) {
3051 2
            return false;
3052
        }
3053
3054 4
        foreach ($this->keys()->getGenerator() as $key) {
3055 4
            if ((int) $key !== $key) {
3056 4
                return false;
3057
            }
3058
        }
3059
3060 2
        return true;
3061
    }
3062
3063
    /**
3064
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3065
     *
3066
     * @param bool $recursive
3067
     *
3068
     * @return bool
3069
     * @psalm-mutation-free
3070
     */
3071 9
    public function isSequential(bool $recursive = false): bool
3072
    {
3073
3074
        // recursive
3075
3076 9
        if ($recursive === true) {
3077
            return $this->array_keys_recursive($this->toArray())
3078
                   ===
3079
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3080
        }
3081
3082
        // non recursive
3083
3084 9
        return \array_keys($this->toArray())
3085
               ===
3086 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3087
    }
3088
3089
    /**
3090
     * @return array
3091
     *
3092
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3093
     */
3094
    public function jsonSerialize(): array
3095
    {
3096
        return $this->toArray();
3097
    }
3098
3099
    /**
3100
     * Gets the key/index of the element at the current internal iterator position.
3101
     *
3102
     * @return int|string|null
3103
     */
3104
    public function key()
3105
    {
3106
        return \key($this->array);
3107
    }
3108
3109
    /**
3110
     * Checks if the given key exists in the provided array.
3111
     *
3112
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3113
     *       then you need to use "Arrayy->offsetExists()".
3114
     *
3115
     * @param int|string $key the key to look for
3116
     *
3117
     * @return bool
3118
     * @psalm-mutation-free
3119
     */
3120 127
    public function keyExists($key): bool
3121
    {
3122 127
        return \array_key_exists($key, $this->array);
3123
    }
3124
3125
    /**
3126
     * Get all keys from the current array.
3127
     *
3128
     * @param bool       $recursive     [optional] <p>
3129
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3130
     *                                  </p>
3131
     * @param mixed|null $search_values [optional] <p>
3132
     *                                  If specified, then only keys containing these values are returned.
3133
     *                                  </p>
3134
     * @param bool       $strict        [optional] <p>
3135
     *                                  Determines if strict comparison (===) should be used during the search.
3136
     *                                  </p>
3137
     *
3138
     * @return static
3139
     *                <p>(Immutable) An array of all the keys in input.</p>
3140
     *
3141
     * @psalm-return static<array-key,TKey>
3142
     * @psalm-mutation-free
3143
     */
3144 29
    public function keys(
3145
        bool $recursive = false,
3146
        $search_values = null,
3147
        bool $strict = true
3148
    ): self {
3149
3150
        // recursive
3151
3152 29
        if ($recursive === true) {
3153 4
            $array = $this->array_keys_recursive(
3154 4
                null,
3155 4
                $search_values,
3156 4
                $strict
3157
            );
3158
3159 4
            return static::create(
3160 4
                $array,
3161 4
                $this->iteratorClass,
3162 4
                false
3163
            );
3164
        }
3165
3166
        // non recursive
3167
3168 28
        if ($search_values === null) {
3169
            $arrayFunction = function (): \Generator {
3170 28
                foreach ($this->getGenerator() as $key => $value) {
3171 26
                    yield $key;
3172
                }
3173 28
            };
3174
        } else {
3175
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3176 1
                $is_array_tmp = \is_array($search_values);
3177
3178 1
                foreach ($this->getGenerator() as $key => $value) {
3179 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...
3180
                        (
3181 1
                            $is_array_tmp === false
3182
                            &&
3183 1
                            $strict === true
3184
                            &&
3185 1
                            $search_values === $value
3186
                        )
3187
                        ||
3188
                        (
3189 1
                            $is_array_tmp === false
3190
                            &&
3191 1
                            $strict === false
3192
                            &&
3193 1
                            $search_values == $value
3194
                        )
3195
                        ||
3196
                        (
3197 1
                            $is_array_tmp === true
3198
                            &&
3199 1
                            \in_array($value, $search_values, $strict)
3200
                        )
3201
                    ) {
3202 1
                        yield $key;
3203
                    }
3204
                }
3205 1
            };
3206
        }
3207
3208 28
        return static::create(
3209 28
            $arrayFunction,
3210 28
            $this->iteratorClass,
3211 28
            false
3212
        );
3213
    }
3214
3215
    /**
3216
     * Sort an array by key in reverse order.
3217
     *
3218
     * @param int $sort_flags [optional] <p>
3219
     *                        You may modify the behavior of the sort using the optional
3220
     *                        parameter sort_flags, for details
3221
     *                        see sort.
3222
     *                        </p>
3223
     *
3224
     * @return $this
3225
     *               <p>(Mutable) Return this Arrayy object.</p>
3226
     *
3227
     * @psalm-return static<TKey,T>
3228
     */
3229 4
    public function krsort(int $sort_flags = 0): self
3230
    {
3231 4
        $this->generatorToArray();
3232
3233 4
        \krsort($this->array, $sort_flags);
3234
3235 4
        return $this;
3236
    }
3237
3238
    /**
3239
     * Sort an array by key in reverse order.
3240
     *
3241
     * @param int $sort_flags [optional] <p>
3242
     *                        You may modify the behavior of the sort using the optional
3243
     *                        parameter sort_flags, for details
3244
     *                        see sort.
3245
     *                        </p>
3246
     *
3247
     * @return $this
3248
     *               <p>(Immutable) Return this Arrayy object.</p>
3249
     *
3250
     * @psalm-return static<TKey,T>
3251
     * @psalm-mutation-free
3252
     */
3253 4
    public function krsortImmutable(int $sort_flags = 0): self
3254
    {
3255 4
        $that = clone $this;
3256
3257
        /**
3258
         * @psalm-suppress ImpureMethodCall - object is already cloned
3259
         */
3260 4
        $that->krsort($sort_flags);
3261
3262 4
        return $that;
3263
    }
3264
3265
    /**
3266
     * Get the last value from the current array.
3267
     *
3268
     * @return mixed|null
3269
     *                    <p>Return null if there wasn't a element.</p>
3270
     * @psalm-mutation-free
3271
     */
3272 17
    public function last()
3273
    {
3274 17
        $key_last = $this->lastKey();
3275 17
        if ($key_last === null) {
3276 2
            return null;
3277
        }
3278
3279 15
        return $this->get($key_last);
3280
    }
3281
3282
    /**
3283
     * Get the last key from the current array.
3284
     *
3285
     * @return mixed|null
3286
     *                    <p>Return null if there wasn't a element.</p>
3287
     * @psalm-mutation-free
3288
     */
3289 21
    public function lastKey()
3290
    {
3291 21
        $this->generatorToArray();
3292
3293 21
        return \array_key_last($this->array);
3294
    }
3295
3296
    /**
3297
     * Get the last value(s) from the current array.
3298
     *
3299
     * @param int|null $number
3300
     *
3301
     * @return static
3302
     *                <p>(Immutable)</p>
3303
     *
3304
     * @psalm-return static<TKey,T>
3305
     * @psalm-mutation-free
3306
     */
3307 13
    public function lastsImmutable(int $number = null): self
3308
    {
3309 13
        if ($this->isEmpty()) {
3310 1
            return static::create(
3311 1
                [],
3312 1
                $this->iteratorClass,
3313 1
                false
3314
            );
3315
        }
3316
3317 12
        if ($number === null) {
3318 8
            $poppedValue = $this->last();
3319
3320 8
            if ($poppedValue === null) {
3321 1
                $poppedValue = [$poppedValue];
3322
            } else {
3323 7
                $poppedValue = (array) $poppedValue;
3324
            }
3325
3326 8
            $arrayy = static::create(
3327 8
                $poppedValue,
3328 8
                $this->iteratorClass,
3329 8
                false
3330
            );
3331
        } else {
3332 4
            $number = (int) $number;
3333 4
            $arrayy = $this->rest(-$number);
3334
        }
3335
3336 12
        return $arrayy;
3337
    }
3338
3339
    /**
3340
     * Get the last value(s) from the current array.
3341
     *
3342
     * @param int|null $number
3343
     *
3344
     * @return $this
3345
     *               <p>(Mutable)</p>
3346
     *
3347
     * @psalm-return static<TKey,T>
3348
     */
3349 13
    public function lastsMutable(int $number = null): self
3350
    {
3351 13
        if ($this->isEmpty()) {
3352 1
            return $this;
3353
        }
3354
3355 12
        if ($number === null) {
3356 8
            $poppedValue = $this->last();
3357
3358 8
            if ($poppedValue === null) {
3359 1
                $poppedValue = [$poppedValue];
3360
            } else {
3361 7
                $poppedValue = (array) $poppedValue;
3362
            }
3363
3364 8
            $this->array = static::create(
3365 8
                $poppedValue,
3366 8
                $this->iteratorClass,
3367 8
                false
3368 8
            )->toArray();
3369
        } else {
3370 4
            $number = (int) $number;
3371 4
            $this->array = $this->rest(-$number)->toArray();
3372
        }
3373
3374 12
        $this->generator = null;
3375
3376 12
        return $this;
3377
    }
3378
3379
    /**
3380
     * Count the values from the current array.
3381
     *
3382
     * alias: for "Arrayy->count()"
3383
     *
3384
     * @param int $mode
3385
     *
3386
     * @return int
3387
     *
3388
     * @see Arrayy::count()
3389
     */
3390 20
    public function length(int $mode = \COUNT_NORMAL): int
3391
    {
3392 20
        return $this->count($mode);
3393
    }
3394
3395
    /**
3396
     * Apply the given function to the every element of the array,
3397
     * collecting the results.
3398
     *
3399
     * @param callable $callable
3400
     * @param bool     $useKeyAsSecondParameter
3401
     * @param mixed    ...$arguments
3402
     *
3403
     * @return static
3404
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3405
     *
3406
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3407
     * @psalm-return static<TKey,T>
3408
     * @psalm-mutation-free
3409
     */
3410 5
    public function map(
3411
        callable $callable,
3412
        bool $useKeyAsSecondParameter = false,
3413
        ...$arguments
3414
    ) {
3415
        /**
3416
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3417
         */
3418 5
        $useArguments = \func_num_args() > 2;
3419
3420 5
        return static::create(
3421
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3422 5
                foreach ($this->getGenerator() as $key => $value) {
3423 4
                    if ($useArguments) {
3424 3
                        if ($useKeyAsSecondParameter) {
3425
                            yield $key => $callable($value, $key, ...$arguments);
3426
                        } else {
3427 3
                            yield $key => $callable($value, ...$arguments);
3428
                        }
3429
                    } else {
3430
                        /** @noinspection NestedPositiveIfStatementsInspection */
3431 4
                        if ($useKeyAsSecondParameter) {
3432
                            yield $key => $callable($value, $key);
3433
                        } else {
3434 4
                            yield $key => $callable($value);
3435
                        }
3436
                    }
3437
                }
3438 5
            },
3439 5
            $this->iteratorClass,
3440 5
            false
3441
        );
3442
    }
3443
3444
    /**
3445
     * Check if all items in current array match a truth test.
3446
     *
3447
     * @param \Closure $closure
3448
     *
3449
     * @return bool
3450
     */
3451 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...
3452
    {
3453 15
        if ($this->count() === 0) {
3454 2
            return false;
3455
        }
3456
3457 13
        foreach ($this->getGenerator() as $key => $value) {
3458 13
            $value = $closure($value, $key);
3459
3460 13
            if ($value === false) {
3461 13
                return false;
3462
            }
3463
        }
3464
3465 7
        return true;
3466
    }
3467
3468
    /**
3469
     * Check if any item in the current array matches a truth test.
3470
     *
3471
     * @param \Closure $closure
3472
     *
3473
     * @return bool
3474
     */
3475 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...
3476
    {
3477 14
        if ($this->count() === 0) {
3478 2
            return false;
3479
        }
3480
3481 12
        foreach ($this->getGenerator() as $key => $value) {
3482 12
            $value = $closure($value, $key);
3483
3484 12
            if ($value === true) {
3485 12
                return true;
3486
            }
3487
        }
3488
3489 4
        return false;
3490
    }
3491
3492
    /**
3493
     * Get the max value from an array.
3494
     *
3495
     * @return false|mixed
3496
     *                     <p>Will return false if there are no values.</p>
3497
     */
3498 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...
3499
    {
3500 10
        if ($this->count() === 0) {
3501 1
            return false;
3502
        }
3503
3504 9
        $max = false;
3505 9
        foreach ($this->getGenerator() as $value) {
3506
            if (
3507 9
                $max === false
3508
                ||
3509 9
                $value > $max
3510
            ) {
3511 9
                $max = $value;
3512
            }
3513
        }
3514
3515 9
        return $max;
3516
    }
3517
3518
    /**
3519
     * Merge the new $array into the current array.
3520
     *
3521
     * - keep key,value from the current array, also if the index is in the new $array
3522
     *
3523
     * @param array $array
3524
     * @param bool  $recursive
3525
     *
3526
     * @return static
3527
     *                <p>(Immutable)</p>
3528
     *
3529
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3530
     * @psalm-return static<int|TKey,T>
3531
     * @psalm-mutation-free
3532
     */
3533 25 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3534
    {
3535 25
        if ($recursive === true) {
3536 4
            $result = \array_replace_recursive($this->toArray(), $array);
3537
        } else {
3538 21
            $result = \array_replace($this->toArray(), $array);
3539
        }
3540
3541 25
        return static::create(
3542 25
            $result,
3543 25
            $this->iteratorClass,
3544 25
            false
3545
        );
3546
    }
3547
3548
    /**
3549
     * Merge the new $array into the current array.
3550
     *
3551
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3552
     * - create new indexes
3553
     *
3554
     * @param array $array
3555
     * @param bool  $recursive
3556
     *
3557
     * @return static
3558
     *                <p>(Immutable)</p>
3559
     *
3560
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3561
     * @psalm-return static<TKey,T>
3562
     * @psalm-mutation-free
3563
     */
3564 16 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3565
    {
3566 16
        if ($recursive === true) {
3567 4
            $result = \array_merge_recursive($this->toArray(), $array);
3568
        } else {
3569 12
            $result = \array_merge($this->toArray(), $array);
3570
        }
3571
3572 16
        return static::create(
3573 16
            $result,
3574 16
            $this->iteratorClass,
3575 16
            false
3576
        );
3577
    }
3578
3579
    /**
3580
     * Merge the the current array into the $array.
3581
     *
3582
     * - use key,value from the new $array, also if the index is in the current array
3583
     *
3584
     * @param array $array
3585
     * @param bool  $recursive
3586
     *
3587
     * @return static
3588
     *                <p>(Immutable)</p>
3589
     *
3590
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3591
     * @psalm-return static<TKey,T>
3592
     * @psalm-mutation-free
3593
     */
3594 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...
3595
    {
3596 16
        if ($recursive === true) {
3597 4
            $result = \array_replace_recursive($array, $this->toArray());
3598
        } else {
3599 12
            $result = \array_replace($array, $this->toArray());
3600
        }
3601
3602 16
        return static::create(
3603 16
            $result,
3604 16
            $this->iteratorClass,
3605 16
            false
3606
        );
3607
    }
3608
3609
    /**
3610
     * Merge the current array into the new $array.
3611
     *
3612
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3613
     * - create new indexes
3614
     *
3615
     * @param array $array
3616
     * @param bool  $recursive
3617
     *
3618
     * @return static
3619
     *                <p>(Immutable)</p>
3620
     *
3621
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3622
     * @psalm-return static<TKey,T>
3623
     * @psalm-mutation-free
3624
     */
3625 17 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3626
    {
3627 17
        if ($recursive === true) {
3628 4
            $result = \array_merge_recursive($array, $this->toArray());
3629
        } else {
3630 13
            $result = \array_merge($array, $this->toArray());
3631
        }
3632
3633 17
        return static::create(
3634 17
            $result,
3635 17
            $this->iteratorClass,
3636 17
            false
3637
        );
3638
    }
3639
3640
    /**
3641
     * @return ArrayyMeta|static
3642
     */
3643 15
    public static function meta()
3644
    {
3645 15
        return (new ArrayyMeta())->getMetaObject(static::class);
3646
    }
3647
3648
    /**
3649
     * Get the min value from an array.
3650
     *
3651
     * @return false|mixed
3652
     *                     <p>Will return false if there are no values.</p>
3653
     */
3654 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...
3655
    {
3656 10
        if ($this->count() === 0) {
3657 1
            return false;
3658
        }
3659
3660 9
        $min = false;
3661 9
        foreach ($this->getGenerator() as $value) {
3662
            if (
3663 9
                $min === false
3664
                ||
3665 9
                $value < $min
3666
            ) {
3667 9
                $min = $value;
3668
            }
3669
        }
3670
3671 9
        return $min;
3672
    }
3673
3674
    /**
3675
     * Get the most used value from the array.
3676
     *
3677
     * @return mixed|null
3678
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
3679
     * @psalm-mutation-free
3680
     */
3681 3
    public function mostUsedValue()
3682
    {
3683 3
        return $this->countValues()->arsortImmutable()->firstKey();
3684
    }
3685
3686
    /**
3687
     * Get the most used value from the array.
3688
     *
3689
     * @param int|null $number <p>How many values you will take?</p>
3690
     *
3691
     * @return static
3692
     *                <p>(Immutable)</p>
3693
     *
3694
     * @psalm-return static<TKey,T>
3695
     * @psalm-mutation-free
3696
     */
3697 3
    public function mostUsedValues(int $number = null): self
3698
    {
3699 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
3700
    }
3701
3702
    /**
3703
     * Move an array element to a new index.
3704
     *
3705
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3706
     *
3707
     * @param int|string $from
3708
     * @param int        $to
3709
     *
3710
     * @return static
3711
     *                <p>(Immutable)</p>
3712
     *
3713
     * @psalm-return static<TKey,T>
3714
     * @psalm-mutation-free
3715
     */
3716 1
    public function moveElement($from, $to): self
3717
    {
3718 1
        $array = $this->toArray();
3719
3720 1
        if ((int) $from === $from) {
3721 1
            $tmp = \array_splice($array, $from, 1);
3722 1
            \array_splice($array, (int) $to, 0, $tmp);
3723 1
            $output = $array;
3724 1
        } elseif ((string) $from === $from) {
3725 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3726 1
            $itemToMove = $array[$from];
3727 1
            if ($indexToMove !== false) {
3728 1
                \array_splice($array, $indexToMove, 1);
3729
            }
3730 1
            $i = 0;
3731 1
            $output = [];
3732 1
            foreach ($array as $key => $item) {
3733 1
                if ($i === $to) {
3734 1
                    $output[$from] = $itemToMove;
3735
                }
3736 1
                $output[$key] = $item;
3737 1
                ++$i;
3738
            }
3739
        } else {
3740
            $output = [];
3741
        }
3742
3743 1
        return static::create(
3744 1
            $output,
3745 1
            $this->iteratorClass,
3746 1
            false
3747
        );
3748
    }
3749
3750
    /**
3751
     * Move an array element to the first place.
3752
     *
3753
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3754
     *       loss the keys of an indexed array.
3755
     *
3756
     * @param int|string $key
3757
     *
3758
     * @return static
3759
     *                <p>(Immutable)</p>
3760
     *
3761
     * @psalm-return static<TKey,T>
3762
     * @psalm-mutation-free
3763
     */
3764 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...
3765
    {
3766 1
        $array = $this->toArray();
3767
3768 1
        if ($this->offsetExists($key)) {
3769 1
            $tmpValue = $this->get($key);
3770 1
            unset($array[$key]);
3771 1
            $array = [$key => $tmpValue] + $array;
3772
        }
3773
3774 1
        return static::create(
3775 1
            $array,
3776 1
            $this->iteratorClass,
3777 1
            false
3778
        );
3779
    }
3780
3781
    /**
3782
     * Move an array element to the last place.
3783
     *
3784
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3785
     *       loss the keys of an indexed array.
3786
     *
3787
     * @param int|string $key
3788
     *
3789
     * @return static
3790
     *                <p>(Immutable)</p>
3791
     *
3792
     * @psalm-return static<TKey,T>
3793
     * @psalm-mutation-free
3794
     */
3795 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...
3796
    {
3797 1
        $array = $this->toArray();
3798
3799 1
        if ($this->offsetExists($key)) {
3800 1
            $tmpValue = $this->get($key);
3801 1
            unset($array[$key]);
3802 1
            $array += [$key => $tmpValue];
3803
        }
3804
3805 1
        return static::create(
3806 1
            $array,
3807 1
            $this->iteratorClass,
3808 1
            false
3809
        );
3810
    }
3811
3812
    /**
3813
     * Moves the internal iterator position to the next element and returns this element.
3814
     *
3815
     * @return false|mixed
3816
     *                     <p>(Mutable) Will return false if there are no values.</p>
3817
     */
3818
    public function next()
3819
    {
3820
        return \next($this->array);
3821
    }
3822
3823
    /**
3824
     * Get the next nth keys and values from the array.
3825
     *
3826
     * @param int $step
3827
     * @param int $offset
3828
     *
3829
     * @return static
3830
     *                <p>(Immutable)</p>
3831
     *
3832
     * @psalm-return static<TKey,T>
3833
     * @psalm-mutation-free
3834
     */
3835 1
    public function nth(int $step, int $offset = 0): self
3836
    {
3837
        $arrayFunction = function () use ($step, $offset): \Generator {
3838 1
            $position = 0;
3839 1
            foreach ($this->getGenerator() as $key => $value) {
3840 1
                if ($position++ % $step !== $offset) {
3841 1
                    continue;
3842
                }
3843
3844 1
                yield $key => $value;
3845
            }
3846 1
        };
3847
3848 1
        return static::create(
3849 1
            $arrayFunction,
3850 1
            $this->iteratorClass,
3851 1
            false
3852
        );
3853
    }
3854
3855
    /**
3856
     * Get a subset of the items from the given array.
3857
     *
3858
     * @param mixed[] $keys
3859
     *
3860
     * @return static
3861
     *                <p>(Immutable)</p>
3862
     *
3863
     * @psalm-return static<TKey,T>
3864
     * @psalm-mutation-free
3865
     */
3866 1
    public function only(array $keys): self
3867
    {
3868 1
        $array = $this->toArray();
3869
3870 1
        return static::create(
3871 1
            \array_intersect_key($array, \array_flip($keys)),
3872 1
            $this->iteratorClass,
3873 1
            false
3874
        );
3875
    }
3876
3877
    /**
3878
     * Pad array to the specified size with a given value.
3879
     *
3880
     * @param int   $size  <p>Size of the result array.</p>
3881
     * @param mixed $value <p>Empty value by default.</p>
3882
     *
3883
     * @return static
3884
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3885
     *
3886
     * @psalm-return static<TKey,T>
3887
     * @psalm-mutation-free
3888
     */
3889 5
    public function pad(int $size, $value): self
3890
    {
3891 5
        return static::create(
3892 5
            \array_pad($this->toArray(), $size, $value),
3893 5
            $this->iteratorClass,
3894 5
            false
3895
        );
3896
    }
3897
3898
    /**
3899
     * Partitions this array in two array according to a predicate.
3900
     * Keys are preserved in the resulting array.
3901
     *
3902
     * @param \Closure $closure
3903
     *                          <p>The predicate on which to partition.</p>
3904
     *
3905
     * @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...
3906
     *                    <p>An array with two elements. The first element contains the array
3907
     *                    of elements where the predicate returned TRUE, the second element
3908
     *                    contains the array of elements where the predicate returned FALSE.</p>
3909
     *
3910
     * @psalm-return array<int, static<TKey,T>>
3911
     */
3912 1
    public function partition(\Closure $closure): array
3913
    {
3914
        // init
3915 1
        $matches = [];
3916 1
        $noMatches = [];
3917
3918 1
        foreach ($this->array as $key => $value) {
3919 1
            if ($closure($value, $key)) {
3920 1
                $matches[$key] = $value;
3921
            } else {
3922 1
                $noMatches[$key] = $value;
3923
            }
3924
        }
3925
3926 1
        return [self::create($matches), self::create($noMatches)];
3927
    }
3928
3929
    /**
3930
     * Pop a specified value off the end of the current array.
3931
     *
3932
     * @return mixed|null
3933
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
3934
     */
3935 5
    public function pop()
3936
    {
3937 5
        $this->generatorToArray();
3938
3939 5
        return \array_pop($this->array);
3940
    }
3941
3942
    /**
3943
     * Prepend a (key) + value to the current array.
3944
     *
3945
     * @param mixed $value
3946
     * @param mixed $key
3947
     *
3948
     * @return $this
3949
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3950
     *
3951
     * @psalm-return static<TKey,T>
3952
     */
3953 11
    public function prepend($value, $key = null)
3954
    {
3955 11
        $this->generatorToArray();
3956
3957 11
        if ($this->properties !== []) {
3958 3
            $this->checkType($key, $value);
3959
        }
3960
3961 9
        if ($key === null) {
3962 8
            \array_unshift($this->array, $value);
3963
        } else {
3964 2
            $this->array = [$key => $value] + $this->array;
3965
        }
3966
3967 9
        return $this;
3968
    }
3969
3970
    /**
3971
     * Add a suffix to each key.
3972
     *
3973
     * @param mixed $suffix
3974
     *
3975
     * @return static
3976
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3977
     *
3978
     * @psalm-return static<TKey,T>
3979
     * @psalm-mutation-free
3980
     */
3981 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...
3982
    {
3983
        // init
3984 10
        $result = [];
3985
3986 10
        foreach ($this->getGenerator() as $key => $item) {
3987 9
            if ($item instanceof self) {
3988
                $result[$key] = $item->prependToEachKey($suffix);
3989 9
            } elseif (\is_array($item) === true) {
3990
                $result[$key] = self::create(
3991
                    $item,
3992
                    $this->iteratorClass,
3993
                    false
3994
                )->prependToEachKey($suffix)
3995
                    ->toArray();
3996
            } else {
3997 9
                $result[$key . $suffix] = $item;
3998
            }
3999
        }
4000
4001 10
        return self::create(
4002 10
            $result,
4003 10
            $this->iteratorClass,
4004 10
            false
4005
        );
4006
    }
4007
4008
    /**
4009
     * Add a suffix to each value.
4010
     *
4011
     * @param mixed $suffix
4012
     *
4013
     * @return static
4014
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4015
     *
4016
     * @psalm-return static<TKey,T>
4017
     * @psalm-mutation-free
4018
     */
4019 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...
4020
    {
4021
        // init
4022 10
        $result = [];
4023
4024 10
        foreach ($this->getGenerator() as $key => $item) {
4025 9
            if ($item instanceof self) {
4026
                $result[$key] = $item->prependToEachValue($suffix);
4027 9
            } elseif (\is_array($item) === true) {
4028
                $result[$key] = self::create(
4029
                    $item,
4030
                    $this->iteratorClass,
4031
                    false
4032
                )->prependToEachValue($suffix)
4033
                    ->toArray();
4034 9
            } elseif (\is_object($item) === true) {
4035 1
                $result[$key] = $item;
4036
            } else {
4037 9
                $result[$key] = $item . $suffix;
4038
            }
4039
        }
4040
4041 10
        return self::create(
4042 10
            $result,
4043 10
            $this->iteratorClass,
4044 10
            false
4045
        );
4046
    }
4047
4048
    /**
4049
     * Return the value of a given key and
4050
     * delete the key.
4051
     *
4052
     * @param int|int[]|string|string[]|null $keyOrKeys
4053
     * @param mixed                          $fallback
4054
     *
4055
     * @return mixed
4056
     */
4057 1
    public function pull($keyOrKeys = null, $fallback = null)
4058
    {
4059 1
        if ($keyOrKeys === null) {
4060
            $array = $this->toArray();
4061
            $this->clear();
4062
4063
            return $array;
4064
        }
4065
4066 1
        if (\is_array($keyOrKeys) === true) {
4067 1
            $valueOrValues = [];
4068 1
            foreach ($keyOrKeys as $key) {
4069 1
                $valueOrValues[] = $this->get($key, $fallback);
4070 1
                $this->offsetUnset($key);
4071
            }
4072
        } else {
4073 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4074 1
            $this->offsetUnset($keyOrKeys);
4075
        }
4076
4077 1
        return $valueOrValues;
4078
    }
4079
4080
    /**
4081
     * Push one or more values onto the end of array at once.
4082
     *
4083
     * @param array ...$args
4084
     *
4085
     * @return $this
4086
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4087
     *
4088
     * @noinspection ReturnTypeCanBeDeclaredInspection
4089
     *
4090
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4091
     * @psalm-return static<TKey,T>
4092
     */
4093 5
    public function push(...$args)
4094
    {
4095 5
        $this->generatorToArray();
4096
4097
        if (
4098 5
            $this->checkPropertyTypes
4099
            &&
4100 5
            $this->properties !== []
4101
        ) {
4102 1
            foreach ($args as $key => $value) {
4103 1
                $this->checkType($key, $value);
4104
            }
4105
        }
4106
4107 5
        \array_push($this->array, ...$args);
4108
4109 5
        return $this;
4110
    }
4111
4112
    /**
4113
     * Get a random value from the current array.
4114
     *
4115
     * @param int|null $number <p>How many values you will take?</p>
4116
     *
4117
     * @return static
4118
     *                <p>(Immutable)</p>
4119
     *
4120
     * @psalm-return static<int|array-key,T>
4121
     */
4122 19
    public function randomImmutable(int $number = null): self
4123
    {
4124 19
        $this->generatorToArray();
4125
4126 19
        if ($this->count() === 0) {
4127 1
            return static::create(
4128 1
                [],
4129 1
                $this->iteratorClass,
4130 1
                false
4131
            );
4132
        }
4133
4134 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...
4135
            /** @noinspection NonSecureArrayRandUsageInspection */
4136 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4137
4138 13
            return static::create(
4139 13
                $arrayRandValue,
4140 13
                $this->iteratorClass,
4141 13
                false
4142
            );
4143
        }
4144
4145 6
        $arrayTmp = $this->array;
4146
        /** @noinspection NonSecureShuffleUsageInspection */
4147 6
        \shuffle($arrayTmp);
4148
4149 6
        return static::create(
4150 6
            $arrayTmp,
4151 6
            $this->iteratorClass,
4152 6
            false
4153 6
        )->firstsImmutable($number);
4154
    }
4155
4156
    /**
4157
     * Pick a random key/index from the keys of this array.
4158
     *
4159
     * @throws \RangeException If array is empty
4160
     *
4161
     * @return mixed
4162
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4163
     */
4164 4
    public function randomKey()
4165
    {
4166 4
        $result = $this->randomKeys(1);
4167
4168 4
        if (!isset($result[0])) {
4169
            $result[0] = null;
4170
        }
4171
4172 4
        return $result[0];
4173
    }
4174
4175
    /**
4176
     * Pick a given number of random keys/indexes out of this array.
4177
     *
4178
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4179
     *
4180
     * @throws \RangeException If array is empty
4181
     *
4182
     * @return static
4183
     *                <p>(Immutable)</p>
4184
     *
4185
     * @psalm-return static<TKey,T>
4186
     */
4187 13
    public function randomKeys(int $number): self
4188
    {
4189 13
        $this->generatorToArray();
4190
4191 13
        $count = $this->count();
4192
4193
        if (
4194 13
            $number === 0
4195
            ||
4196 13
            $number > $count
4197
        ) {
4198 2
            throw new \RangeException(
4199 2
                \sprintf(
4200 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4201 2
                    $number,
4202 2
                    $count
4203
                )
4204
            );
4205
        }
4206
4207 11
        $result = (array) \array_rand($this->array, $number);
4208
4209 11
        return static::create(
4210 11
            $result,
4211 11
            $this->iteratorClass,
4212 11
            false
4213
        );
4214
    }
4215
4216
    /**
4217
     * Get a random value from the current array.
4218
     *
4219
     * @param int|null $number <p>How many values you will take?</p>
4220
     *
4221
     * @return $this
4222
     *               <p>(Mutable) Return this Arrayy object.</p>
4223
     *
4224
     * @psalm-return static<TKey,T>
4225
     */
4226 17
    public function randomMutable(int $number = null): self
4227
    {
4228 17
        $this->generatorToArray();
4229
4230 17
        if ($this->count() === 0) {
4231
            return static::create(
4232
                [],
4233
                $this->iteratorClass,
4234
                false
4235
            );
4236
        }
4237
4238 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...
4239
            /** @noinspection NonSecureArrayRandUsageInspection */
4240 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4241 7
            $this->array = $arrayRandValue;
4242
4243 7
            return $this;
4244
        }
4245
4246
        /** @noinspection NonSecureShuffleUsageInspection */
4247 11
        \shuffle($this->array);
4248
4249 11
        return $this->firstsMutable($number);
4250
    }
4251
4252
    /**
4253
     * Pick a random value from the values of this array.
4254
     *
4255
     * @return mixed
4256
     *               <p>Get a random value or null if there wasn't a value.</p>
4257
     */
4258 4
    public function randomValue()
4259
    {
4260 4
        $result = $this->randomImmutable();
4261
4262 4
        if (!isset($result[0])) {
4263
            $result[0] = null;
4264
        }
4265
4266 4
        return $result[0];
4267
    }
4268
4269
    /**
4270
     * Pick a given number of random values out of this array.
4271
     *
4272
     * @param int $number
4273
     *
4274
     * @return static
4275
     *                <p>(Mutable)</p>
4276
     *
4277
     * @psalm-return static<TKey,T>
4278
     */
4279 7
    public function randomValues(int $number): self
4280
    {
4281 7
        return $this->randomMutable($number);
4282
    }
4283
4284
    /**
4285
     * Get a random value from an array, with the ability to skew the results.
4286
     *
4287
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4288
     *
4289
     * @param array    $array
4290
     * @param int|null $number <p>How many values you will take?</p>
4291
     *
4292
     * @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...
4293
     *                           <p>(Immutable)</p>
4294
     *
4295
     * @psalm-param  array<mixed,mixed> $array
4296
     * @psalm-return static<int|array-key,T>
4297
     */
4298 9
    public function randomWeighted(array $array, int $number = null): self
4299
    {
4300
        // init
4301 9
        $options = [];
4302
4303 9
        foreach ($array as $option => $weight) {
4304 9
            if ($this->searchIndex($option) !== false) {
4305 9
                for ($i = 0; $i < $weight; ++$i) {
4306 1
                    $options[] = $option;
4307
                }
4308
            }
4309
        }
4310
4311 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4312
    }
4313
4314
    /**
4315
     * Reduce the current array via callable e.g. anonymous-function.
4316
     *
4317
     * @param callable $callable
4318
     * @param mixed    $init
4319
     *
4320
     * @return static
4321
     *                <p>(Immutable)</p>
4322
     *
4323
     * @psalm-return static<TKey,T>
4324
     * @psalm-mutation-free
4325
     */
4326 18
    public function reduce($callable, $init = []): self
4327
    {
4328 18
        if ($this->generator) {
4329 1
            $result = $init;
4330
4331 1
            foreach ($this->getGenerator() as $value) {
4332 1
                $result = $callable($result, $value);
4333
            }
4334
4335 1
            return static::create(
4336 1
                $result,
4337 1
                $this->iteratorClass,
4338 1
                false
4339
            );
4340
        }
4341
4342 18
        $result = \array_reduce($this->array, $callable, $init);
4343
4344 18
        if ($result === null) {
4345
            $this->array = [];
4346
        } else {
4347 18
            $this->array = (array) $result;
4348
        }
4349
4350 18
        return static::create(
4351 18
            $this->array,
4352 18
            $this->iteratorClass,
4353 18
            false
4354
        );
4355
    }
4356
4357
    /**
4358
     * @param bool $unique
4359
     *
4360
     * @return static
4361
     *                <p>(Immutable)</p>
4362
     *
4363
     * @psalm-return static<TKey,T>
4364
     * @psalm-mutation-free
4365
     */
4366 14
    public function reduce_dimension(bool $unique = true): self
4367
    {
4368
        // init
4369 14
        $result = [];
4370
4371 14
        foreach ($this->getGenerator() as $val) {
4372 12
            if (\is_array($val) === true) {
4373 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4374
            } else {
4375 12
                $result[] = [$val];
4376
            }
4377
        }
4378
4379 14
        $result = $result === [] ? [] : \array_merge(...$result);
4380
4381 14
        $resultArrayy = new static($result);
4382
4383
        /**
4384
         * @psalm-suppress ImpureMethodCall - object is already re-created
4385
         * @psalm-suppress InvalidReturnStatement - why?
4386
         */
4387 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4388
    }
4389
4390
    /**
4391
     * Create a numerically re-indexed Arrayy object.
4392
     *
4393
     * @return $this
4394
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4395
     *
4396
     * @psalm-return static<TKey,T>
4397
     */
4398 9
    public function reindex(): self
4399
    {
4400 9
        $this->generatorToArray(false);
4401
4402 9
        $this->array = \array_values($this->array);
4403
4404 9
        return $this;
4405
    }
4406
4407
    /**
4408
     * Return all items that fail the truth test.
4409
     *
4410
     * @param \Closure $closure
4411
     *
4412
     * @return static
4413
     *                <p>(Immutable)</p>
4414
     *
4415
     * @psalm-return static<TKey,T>
4416
     * @psalm-mutation-free
4417
     */
4418 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...
4419
    {
4420
        // init
4421 1
        $filtered = [];
4422
4423 1
        foreach ($this->getGenerator() as $key => $value) {
4424 1
            if (!$closure($value, $key)) {
4425 1
                $filtered[$key] = $value;
4426
            }
4427
        }
4428
4429 1
        return static::create(
4430 1
            $filtered,
4431 1
            $this->iteratorClass,
4432 1
            false
4433
        );
4434
    }
4435
4436
    /**
4437
     * Remove a value from the current array (optional using dot-notation).
4438
     *
4439
     * @param mixed $key
4440
     *
4441
     * @return static
4442
     *                <p>(Mutable)</p>
4443
     *
4444
     * @psalm-param  TKey $key
4445
     * @psalm-return static<TKey,T>
4446
     */
4447 18
    public function remove($key)
4448
    {
4449
        // recursive call
4450 18
        if (\is_array($key) === true) {
4451
            foreach ($key as $k) {
4452
                $this->internalRemove($k);
4453
            }
4454
4455
            return static::create(
4456
                $this->toArray(),
4457
                $this->iteratorClass,
4458
                false
4459
            );
4460
        }
4461
4462 18
        $this->internalRemove($key);
4463
4464 18
        return static::create(
4465 18
            $this->toArray(),
4466 18
            $this->iteratorClass,
4467 18
            false
4468
        );
4469
    }
4470
4471
    /**
4472
     * alias: for "Arrayy->removeValue()"
4473
     *
4474
     * @param mixed $element
4475
     *
4476
     * @return static
4477
     *                <p>(Immutable)</p>
4478
     *
4479
     * @psalm-param  T $element
4480
     * @psalm-return static<TKey,T>
4481
     * @psalm-mutation-free
4482
     */
4483 8
    public function removeElement($element)
4484
    {
4485 8
        return $this->removeValue($element);
4486
    }
4487
4488
    /**
4489
     * Remove the first value from the current array.
4490
     *
4491
     * @return static
4492
     *                <p>(Immutable)</p>
4493
     *
4494
     * @psalm-return static<TKey,T>
4495
     * @psalm-mutation-free
4496
     */
4497 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...
4498
    {
4499 7
        $tmpArray = $this->toArray();
4500
4501 7
        \array_shift($tmpArray);
4502
4503 7
        return static::create(
4504 7
            $tmpArray,
4505 7
            $this->iteratorClass,
4506 7
            false
4507
        );
4508
    }
4509
4510
    /**
4511
     * Remove the last value from the current array.
4512
     *
4513
     * @return static
4514
     *                <p>(Immutable)</p>
4515
     *
4516
     * @psalm-return static<TKey,T>
4517
     * @psalm-mutation-free
4518
     */
4519 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...
4520
    {
4521 7
        $tmpArray = $this->toArray();
4522
4523 7
        \array_pop($tmpArray);
4524
4525 7
        return static::create(
4526 7
            $tmpArray,
4527 7
            $this->iteratorClass,
4528 7
            false
4529
        );
4530
    }
4531
4532
    /**
4533
     * Removes a particular value from an array (numeric or associative).
4534
     *
4535
     * @param mixed $value
4536
     *
4537
     * @return static
4538
     *                <p>(Immutable)</p>
4539
     *
4540
     * @psalm-param  T $value
4541
     * @psalm-return static<TKey,T>
4542
     * @psalm-mutation-free
4543
     */
4544 8
    public function removeValue($value): self
4545
    {
4546 8
        $this->generatorToArray();
4547
4548
        // init
4549 8
        $isSequentialArray = $this->isSequential();
4550
4551 8
        foreach ($this->array as $key => $item) {
4552 7
            if ($item === $value) {
4553 7
                unset($this->array[$key]);
4554
            }
4555
        }
4556
4557 8
        if ($isSequentialArray) {
4558 6
            $this->array = \array_values($this->array);
4559
        }
4560
4561 8
        return static::create(
4562 8
            $this->array,
4563 8
            $this->iteratorClass,
4564 8
            false
4565
        );
4566
    }
4567
4568
    /**
4569
     * Generate array of repeated arrays.
4570
     *
4571
     * @param int $times <p>How many times has to be repeated.</p>
4572
     *
4573
     * @return static
4574
     *                <p>(Immutable)</p>
4575
     *
4576
     * @psalm-return static<TKey,T>
4577
     * @psalm-mutation-free
4578
     */
4579 1
    public function repeat($times): self
4580
    {
4581 1
        if ($times === 0) {
4582 1
            return static::create([], $this->iteratorClass);
4583
        }
4584
4585 1
        return static::create(
4586 1
            \array_fill(0, (int) $times, $this->toArray()),
4587 1
            $this->iteratorClass,
4588 1
            false
4589
        );
4590
    }
4591
4592
    /**
4593
     * Replace a key with a new key/value pair.
4594
     *
4595
     * @param mixed $replace
4596
     * @param mixed $key
4597
     * @param mixed $value
4598
     *
4599
     * @return static
4600
     *                <p>(Immutable)</p>
4601
     *
4602
     * @psalm-return static<TKey,T>
4603
     * @psalm-mutation-free
4604
     */
4605 2
    public function replace($replace, $key, $value): self
4606
    {
4607 2
        $that = clone $this;
4608
4609
        /**
4610
         * @psalm-suppress ImpureMethodCall - object is already cloned
4611
         */
4612 2
        return $that->remove($replace)
4613 2
            ->set($key, $value);
4614
    }
4615
4616
    /**
4617
     * Create an array using the current array as values and the other array as keys.
4618
     *
4619
     * @param array $keys <p>An array of keys.</p>
4620
     *
4621
     * @return static
4622
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4623
     *
4624
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4625
     * @psalm-return static<TKey,T>
4626
     * @psalm-mutation-free
4627
     */
4628 2
    public function replaceAllKeys(array $keys): self
4629
    {
4630 2
        return static::create(
4631 2
            \array_combine($keys, $this->toArray()),
4632 2
            $this->iteratorClass,
4633 2
            false
4634
        );
4635
    }
4636
4637
    /**
4638
     * Create an array using the current array as keys and the other array as values.
4639
     *
4640
     * @param array $array <p>An array o values.</p>
4641
     *
4642
     * @return static
4643
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4644
     *
4645
     * @psalm-param  array<mixed,T> $array
4646
     * @psalm-return static<TKey,T>
4647
     * @psalm-mutation-free
4648
     */
4649 2
    public function replaceAllValues(array $array): self
4650
    {
4651 2
        return static::create(
4652 2
            \array_combine($this->array, $array),
4653 2
            $this->iteratorClass,
4654 2
            false
4655
        );
4656
    }
4657
4658
    /**
4659
     * Replace the keys in an array with another set.
4660
     *
4661
     * @param array $keys <p>An array of keys matching the array's size</p>
4662
     *
4663
     * @return static
4664
     *                <p>(Immutable)</p>
4665
     *
4666
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4667
     * @psalm-return static<TKey,T>
4668
     * @psalm-mutation-free
4669
     */
4670 1
    public function replaceKeys(array $keys): self
4671
    {
4672 1
        $values = \array_values($this->toArray());
4673 1
        $result = \array_combine($keys, $values);
4674
4675 1
        return static::create(
4676 1
            $result,
4677 1
            $this->iteratorClass,
4678 1
            false
4679
        );
4680
    }
4681
4682
    /**
4683
     * Replace the first matched value in an array.
4684
     *
4685
     * @param mixed $search      <p>The value to replace.</p>
4686
     * @param mixed $replacement <p>The value to replace.</p>
4687
     *
4688
     * @return static
4689
     *                <p>(Immutable)</p>
4690
     *
4691
     * @psalm-return static<TKey,T>
4692
     * @psalm-mutation-free
4693
     */
4694 3
    public function replaceOneValue($search, $replacement = ''): self
4695
    {
4696 3
        $array = $this->toArray();
4697 3
        $key = \array_search($search, $array, true);
4698
4699 3
        if ($key !== false) {
4700 3
            $array[$key] = $replacement;
4701
        }
4702
4703 3
        return static::create(
4704 3
            $array,
4705 3
            $this->iteratorClass,
4706 3
            false
4707
        );
4708
    }
4709
4710
    /**
4711
     * Replace values in the current array.
4712
     *
4713
     * @param mixed $search      <p>The value to replace.</p>
4714
     * @param mixed $replacement <p>What to replace it with.</p>
4715
     *
4716
     * @return static
4717
     *                <p>(Immutable)</p>
4718
     *
4719
     * @psalm-return static<TKey,T>
4720
     * @psalm-mutation-free
4721
     */
4722 1
    public function replaceValues($search, $replacement = ''): self
4723
    {
4724
        /**
4725
         * @psalm-suppress MissingClosureReturnType
4726
         * @psalm-suppress MissingClosureParamType
4727
         */
4728 1
        return $this->each(
4729
            static function ($value) use ($search, $replacement) {
4730 1
                return \str_replace($search, $replacement, $value);
4731 1
            }
4732
        );
4733
    }
4734
4735
    /**
4736
     * Get the last elements from index $from until the end of this array.
4737
     *
4738
     * @param int $from
4739
     *
4740
     * @return static
4741
     *                <p>(Immutable)</p>
4742
     *
4743
     * @psalm-return static<TKey,T>
4744
     * @psalm-mutation-free
4745
     */
4746 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...
4747
    {
4748 15
        $tmpArray = $this->toArray();
4749
4750 15
        return static::create(
4751 15
            \array_splice($tmpArray, $from),
4752 15
            $this->iteratorClass,
4753 15
            false
4754
        );
4755
    }
4756
4757
    /**
4758
     * Return the array in the reverse order.
4759
     *
4760
     * @return $this
4761
     *               <p>(Mutable) Return this Arrayy object.</p>
4762
     *
4763
     * @psalm-return static<TKey,T>
4764
     */
4765 9
    public function reverse(): self
4766
    {
4767 9
        $this->generatorToArray();
4768
4769 9
        $this->array = \array_reverse($this->array);
4770
4771 9
        return $this;
4772
    }
4773
4774
    /**
4775
     * Sort an array in reverse order.
4776
     *
4777
     * @param int $sort_flags [optional] <p>
4778
     *                        You may modify the behavior of the sort using the optional
4779
     *                        parameter sort_flags, for details
4780
     *                        see sort.
4781
     *                        </p>
4782
     *
4783
     * @return $this
4784
     *               <p>(Mutable) Return this Arrayy object.</p>
4785
     *
4786
     * @psalm-return static<TKey,T>
4787
     */
4788 4
    public function rsort(int $sort_flags = 0): self
4789
    {
4790 4
        $this->generatorToArray();
4791
4792 4
        \rsort($this->array, $sort_flags);
4793
4794 4
        return $this;
4795
    }
4796
4797
    /**
4798
     * Sort an array in reverse order.
4799
     *
4800
     * @param int $sort_flags [optional] <p>
4801
     *                        You may modify the behavior of the sort using the optional
4802
     *                        parameter sort_flags, for details
4803
     *                        see sort.
4804
     *                        </p>
4805
     *
4806
     * @return $this
4807
     *               <p>(Immutable) Return this Arrayy object.</p>
4808
     *
4809
     * @psalm-return static<TKey,T>
4810
     * @psalm-mutation-free
4811
     */
4812 4
    public function rsortImmutable(int $sort_flags = 0): self
4813
    {
4814 4
        $that = clone $this;
4815
4816
        /**
4817
         * @psalm-suppress ImpureMethodCall - object is already cloned
4818
         */
4819 4
        $that->rsort($sort_flags);
4820
4821 4
        return $that;
4822
    }
4823
4824
    /**
4825
     * Search for the first index of the current array via $value.
4826
     *
4827
     * @param mixed $value
4828
     *
4829
     * @return false|float|int|string
4830
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4831
     * @psalm-mutation-free
4832
     */
4833 21
    public function searchIndex($value)
4834
    {
4835 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
4836 20
            if ($value === $valueFromArray) {
4837 20
                return $keyFromArray;
4838
            }
4839
        }
4840
4841 11
        return false;
4842
    }
4843
4844
    /**
4845
     * Search for the value of the current array via $index.
4846
     *
4847
     * @param mixed $index
4848
     *
4849
     * @return static
4850
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
4851
     *
4852
     * @psalm-return static<TKey,T>
4853
     * @psalm-mutation-free
4854
     */
4855 9
    public function searchValue($index): self
4856
    {
4857 9
        $this->generatorToArray();
4858
4859
        // init
4860 9
        $return = [];
4861
4862 9
        if ($this->array === []) {
4863
            return static::create(
4864
                [],
4865
                $this->iteratorClass,
4866
                false
4867
            );
4868
        }
4869
4870
        // php cast "bool"-index into "int"-index
4871 9
        if ((bool) $index === $index) {
4872 1
            $index = (int) $index;
4873
        }
4874
4875 9
        if ($this->offsetExists($index)) {
4876 7
            $return = [$this->array[$index]];
4877
        }
4878
4879 9
        return static::create(
4880 9
            $return,
4881 9
            $this->iteratorClass,
4882 9
            false
4883
        );
4884
    }
4885
4886
    /**
4887
     * Set a value for the current array (optional using dot-notation).
4888
     *
4889
     * @param string $key   <p>The key to set.</p>
4890
     * @param mixed  $value <p>Its value.</p>
4891
     *
4892
     * @return $this
4893
     *               <p>(Mutable) Return this Arrayy object.</p>
4894
     *
4895
     * @psalm-param  TKey $key
4896
     * @psalm-param  T $value
4897
     * @psalm-return static<TKey,T>
4898
     */
4899 18
    public function set($key, $value): self
4900
    {
4901 18
        $this->internalSet($key, $value);
4902
4903 18
        return $this;
4904
    }
4905
4906
    /**
4907
     * Get a value from a array and set it if it was not.
4908
     *
4909
     * WARNING: this method only set the value, if the $key is not already set
4910
     *
4911
     * @param mixed $key      <p>The key</p>
4912
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
4913
     *
4914
     * @return mixed
4915
     *               <p>(Mutable)</p>
4916
     */
4917 11
    public function setAndGet($key, $fallback = null)
4918
    {
4919 11
        $this->generatorToArray();
4920
4921
        // If the key doesn't exist, set it.
4922 11
        if (!$this->has($key)) {
4923 4
            $this->array = $this->set($key, $fallback)->toArray();
4924
        }
4925
4926 11
        return $this->get($key);
4927
    }
4928
4929
    /**
4930
     * Shifts a specified value off the beginning of array.
4931
     *
4932
     * @return mixed
4933
     *               <p>(Mutable) A shifted element from the current array.</p>
4934
     */
4935 5
    public function shift()
4936
    {
4937 5
        $this->generatorToArray();
4938
4939 5
        return \array_shift($this->array);
4940
    }
4941
4942
    /**
4943
     * Shuffle the current array.
4944
     *
4945
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
4946
     * @param array $array  [optional]
4947
     *
4948
     * @return static
4949
     *                <p>(Immutable)</p>
4950
     *
4951
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4952
     * @psalm-return static<TKey,T>
4953
     *
4954
     * @noinspection BadExceptionsProcessingInspection
4955
     * @noinspection RandomApiMigrationInspection
4956
     * @noinspection NonSecureShuffleUsageInspection
4957
     */
4958 2
    public function shuffle(bool $secure = false, array $array = null): self
4959
    {
4960 2
        if ($array === null) {
4961 2
            $array = $this->toArray(false);
4962
        }
4963
4964 2
        if ($secure !== true) {
4965 2
            \shuffle($array);
4966
        } else {
4967 1
            $size = \count($array, \COUNT_NORMAL);
4968 1
            $keys = \array_keys($array);
4969 1
            for ($i = $size - 1; $i > 0; --$i) {
4970
                try {
4971 1
                    $r = \random_int(0, $i);
4972
                } catch (\Exception $e) {
4973
                    $r = \mt_rand(0, $i);
4974
                }
4975 1
                if ($r !== $i) {
4976 1
                    $temp = $array[$keys[$r]];
4977 1
                    $array[$keys[$r]] = $array[$keys[$i]];
4978 1
                    $array[$keys[$i]] = $temp;
4979
                }
4980
            }
4981
        }
4982
4983 2
        foreach ($array as $key => $value) {
4984
            // check if recursive is needed
4985 2
            if (\is_array($value) === true) {
4986 2
                $array[$key] = $this->shuffle($secure, $value);
4987
            }
4988
        }
4989
4990 2
        return static::create(
4991 2
            $array,
4992 2
            $this->iteratorClass,
4993 2
            false
4994
        );
4995
    }
4996
4997
    /**
4998
     * Count the values from the current array.
4999
     *
5000
     * alias: for "Arrayy->count()"
5001
     *
5002
     * @param int $mode
5003
     *
5004
     * @return int
5005
     */
5006 20
    public function size(int $mode = \COUNT_NORMAL): int
5007
    {
5008 20
        return $this->count($mode);
5009
    }
5010
5011
    /**
5012
     * Checks whether array has exactly $size items.
5013
     *
5014
     * @param int $size
5015
     *
5016
     * @return bool
5017
     */
5018 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...
5019
    {
5020
        // init
5021 1
        $itemsTempCount = 0;
5022
5023 1
        foreach ($this->getGenerator() as $key => $value) {
5024 1
            ++$itemsTempCount;
5025 1
            if ($itemsTempCount > $size) {
5026 1
                return false;
5027
            }
5028
        }
5029
5030 1
        return $itemsTempCount === $size;
5031
    }
5032
5033
    /**
5034
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5035
     * smaller than $fromSize.
5036
     *
5037
     * @param int $fromSize
5038
     * @param int $toSize
5039
     *
5040
     * @return bool
5041
     */
5042 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5043
    {
5044 1
        if ($fromSize > $toSize) {
5045 1
            $tmp = $toSize;
5046 1
            $toSize = $fromSize;
5047 1
            $fromSize = $tmp;
5048
        }
5049
5050
        // init
5051 1
        $itemsTempCount = 0;
5052
5053 1
        foreach ($this->getGenerator() as $key => $value) {
5054 1
            ++$itemsTempCount;
5055 1
            if ($itemsTempCount > $toSize) {
5056 1
                return false;
5057
            }
5058
        }
5059
5060 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5061
    }
5062
5063
    /**
5064
     * Checks whether array has more than $size items.
5065
     *
5066
     * @param int $size
5067
     *
5068
     * @return bool
5069
     */
5070 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...
5071
    {
5072
        // init
5073 1
        $itemsTempCount = 0;
5074
5075 1
        foreach ($this->getGenerator() as $key => $value) {
5076 1
            ++$itemsTempCount;
5077 1
            if ($itemsTempCount > $size) {
5078 1
                return true;
5079
            }
5080
        }
5081
5082 1
        return $itemsTempCount > $size;
5083
    }
5084
5085
    /**
5086
     * Checks whether array has less than $size items.
5087
     *
5088
     * @param int $size
5089
     *
5090
     * @return bool
5091
     */
5092 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...
5093
    {
5094
        // init
5095 1
        $itemsTempCount = 0;
5096
5097 1
        foreach ($this->getGenerator() as $key => $value) {
5098 1
            ++$itemsTempCount;
5099 1
            if ($itemsTempCount > $size) {
5100 1
                return false;
5101
            }
5102
        }
5103
5104 1
        return $itemsTempCount < $size;
5105
    }
5106
5107
    /**
5108
     * Counts all elements in an array, or something in an object.
5109
     *
5110
     * <p>
5111
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5112
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5113
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5114
     * implemented and used in PHP.
5115
     * </p>
5116
     *
5117
     * @return int
5118
     *             <p>
5119
     *             The number of elements in var, which is
5120
     *             typically an array, since anything else will have one
5121
     *             element.
5122
     *             </p>
5123
     *             <p>
5124
     *             If var is not an array or an object with
5125
     *             implemented Countable interface,
5126
     *             1 will be returned.
5127
     *             There is one exception, if var is &null;,
5128
     *             0 will be returned.
5129
     *             </p>
5130
     *             <p>
5131
     *             Caution: count may return 0 for a variable that isn't set,
5132
     *             but it may also return 0 for a variable that has been initialized with an
5133
     *             empty array. Use isset to test if a variable is set.
5134
     *             </p>
5135
     */
5136 10
    public function sizeRecursive(): int
5137
    {
5138 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5139
    }
5140
5141
    /**
5142
     * Extract a slice of the array.
5143
     *
5144
     * @param int      $offset       <p>Slice begin index.</p>
5145
     * @param int|null $length       <p>Length of the slice.</p>
5146
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5147
     *
5148
     * @return static
5149
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5150
     *
5151
     * @psalm-return static<TKey,T>
5152
     * @psalm-mutation-free
5153
     */
5154 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5155
    {
5156 5
        return static::create(
5157 5
            \array_slice(
5158 5
                $this->toArray(),
5159 5
                $offset,
5160 5
                $length,
5161 5
                $preserveKeys
5162
            ),
5163 5
            $this->iteratorClass,
5164 5
            false
5165
        );
5166
    }
5167
5168
    /**
5169
     * Sort the current array and optional you can keep the keys.
5170
     *
5171
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5172
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5173
     *                              <strong>SORT_NATURAL</strong></p>
5174
     * @param bool       $keepKeys
5175
     *
5176
     * @return static
5177
     *                <p>(Mutable) Return this Arrayy object.</p>
5178
     *
5179
     * @psalm-return static<TKey,T>
5180
     */
5181 20
    public function sort(
5182
        $direction = \SORT_ASC,
5183
        int $strategy = \SORT_REGULAR,
5184
        bool $keepKeys = false
5185
    ): self {
5186 20
        $this->generatorToArray();
5187
5188 20
        return $this->sorting(
5189 20
            $this->array,
5190 20
            $direction,
5191 20
            $strategy,
5192 20
            $keepKeys
5193
        );
5194
    }
5195
5196
    /**
5197
     * Sort the current array and optional you can keep the keys.
5198
     *
5199
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5200
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5201
     *                              <strong>SORT_NATURAL</strong></p>
5202
     * @param bool       $keepKeys
5203
     *
5204
     * @return static
5205
     *                <p>(Immutable) Return this Arrayy object.</p>
5206
     *
5207
     * @psalm-return static<TKey,T>
5208
     */
5209 12
    public function sortImmutable(
5210
        $direction = \SORT_ASC,
5211
        int $strategy = \SORT_REGULAR,
5212
        bool $keepKeys = false
5213
    ): self {
5214 12
        $that = clone $this;
5215
5216 12
        $that->generatorToArray();
5217
5218 12
        return $that->sorting(
5219 12
            $that->array,
5220 12
            $direction,
5221 12
            $strategy,
5222 12
            $keepKeys
5223
        );
5224
    }
5225
5226
    /**
5227
     * Sort the current array by key.
5228
     *
5229
     * @see          http://php.net/manual/en/function.ksort.php
5230
     * @see          http://php.net/manual/en/function.krsort.php
5231
     *
5232
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5233
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5234
     *                              <strong>SORT_NATURAL</strong></p>
5235
     *
5236
     * @return $this
5237
     *               <p>(Mutable) Return this Arrayy object.</p>
5238
     *
5239
     * @psalm-return static<TKey,T>
5240
     */
5241 18
    public function sortKeys(
5242
        $direction = \SORT_ASC,
5243
        int $strategy = \SORT_REGULAR
5244
    ): self {
5245 18
        $this->generatorToArray();
5246
5247 18
        $this->sorterKeys($this->array, $direction, $strategy);
5248
5249 18
        return $this;
5250
    }
5251
5252
    /**
5253
     * Sort the current array by key.
5254
     *
5255
     * @see          http://php.net/manual/en/function.ksort.php
5256
     * @see          http://php.net/manual/en/function.krsort.php
5257
     *
5258
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5259
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5260
     *                              <strong>SORT_NATURAL</strong></p>
5261
     *
5262
     * @return $this
5263
     *               <p>(Immutable) Return this Arrayy object.</p>
5264
     *
5265
     * @psalm-return static<TKey,T>
5266
     * @psalm-mutation-free
5267
     */
5268 8
    public function sortKeysImmutable(
5269
        $direction = \SORT_ASC,
5270
        int $strategy = \SORT_REGULAR
5271
    ): self {
5272 8
        $that = clone $this;
5273
5274
        /**
5275
         * @psalm-suppress ImpureMethodCall - object is already cloned
5276
         */
5277 8
        $that->sortKeys($direction, $strategy);
5278
5279 8
        return $that;
5280
    }
5281
5282
    /**
5283
     * Sort the current array by value.
5284
     *
5285
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5286
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5287
     *                              <strong>SORT_NATURAL</strong></p>
5288
     *
5289
     * @return static
5290
     *                <p>(Mutable)</p>
5291
     *
5292
     * @psalm-return static<TKey,T>
5293
     */
5294 1
    public function sortValueKeepIndex(
5295
        $direction = \SORT_ASC,
5296
        int $strategy = \SORT_REGULAR
5297
    ): self {
5298 1
        return $this->sort($direction, $strategy, true);
5299
    }
5300
5301
    /**
5302
     * Sort the current array by value.
5303
     *
5304
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5305
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5306
     *                              <strong>SORT_NATURAL</strong></p>
5307
     *
5308
     * @return static
5309
     *                <p>(Mutable)</p>
5310
     *
5311
     * @psalm-return static<TKey,T>
5312
     */
5313 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5314
    {
5315 1
        return $this->sort($direction, $strategy, false);
5316
    }
5317
5318
    /**
5319
     * Sort a array by value, by a closure or by a property.
5320
     *
5321
     * - If the sorter is null, the array is sorted naturally.
5322
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5323
     *
5324
     * @param callable|string|null $sorter
5325
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5326
     *                                        <strong>SORT_DESC</strong></p>
5327
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5328
     *                                        <strong>SORT_NATURAL</strong></p>
5329
     *
5330
     * @return static
5331
     *                <p>(Immutable)</p>
5332
     *
5333
     * @psalm-return static<TKey,T>
5334
     * @psalm-mutation-free
5335
     */
5336 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5337
    {
5338 1
        $array = $this->toArray();
5339 1
        $direction = $this->getDirection($direction);
5340
5341
        // Transform all values into their results.
5342 1
        if ($sorter) {
5343 1
            $arrayy = static::create(
5344 1
                $array,
5345 1
                $this->iteratorClass,
5346 1
                false
5347
            );
5348
5349
            /**
5350
             * @psalm-suppress MissingClosureReturnType
5351
             * @psalm-suppress MissingClosureParamType
5352
             */
5353 1
            $results = $arrayy->each(
5354
                function ($value) use ($sorter) {
5355 1
                    if (\is_callable($sorter) === true) {
5356 1
                        return $sorter($value);
5357
                    }
5358
5359 1
                    return $this->get($sorter);
5360 1
                }
5361
            );
5362
5363 1
            $results = $results->toArray();
5364
        } else {
5365 1
            $results = $array;
5366
        }
5367
5368
        // Sort by the results and replace by original values
5369 1
        \array_multisort($results, $direction, $strategy, $array);
5370
5371 1
        return static::create(
5372 1
            $array,
5373 1
            $this->iteratorClass,
5374 1
            false
5375
        );
5376
    }
5377
5378
    /**
5379
     * @param int      $offset
5380
     * @param int|null $length
5381
     * @param array    $replacement
5382
     *
5383
     * @return static
5384
     *                <p>(Immutable)</p>
5385
     *
5386
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5387
     * @psalm-return static<TKey,T>
5388
     * @psalm-mutation-free
5389
     */
5390 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5391
    {
5392 1
        $tmpArray = $this->toArray();
5393
5394 1
        \array_splice(
5395 1
            $tmpArray,
5396 1
            $offset,
5397 1
            $length ?? $this->count(),
5398 1
            $replacement
5399
        );
5400
5401 1
        return static::create(
5402 1
            $tmpArray,
5403 1
            $this->iteratorClass,
5404 1
            false
5405
        );
5406
    }
5407
5408
    /**
5409
     * Split an array in the given amount of pieces.
5410
     *
5411
     * @param int  $numberOfPieces
5412
     * @param bool $keepKeys
5413
     *
5414
     * @return static
5415
     *                <p>(Immutable)</p>
5416
     *
5417
     * @psalm-return static<TKey,T>
5418
     * @psalm-mutation-free
5419
     */
5420 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5421
    {
5422 1
        $this->generatorToArray();
5423
5424 1
        $count = $this->count();
5425
5426 1
        if ($count === 0) {
5427 1
            $result = [];
5428
        } else {
5429 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5430 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5431
        }
5432
5433 1
        return static::create(
5434 1
            $result,
5435 1
            $this->iteratorClass,
5436 1
            false
5437
        );
5438
    }
5439
5440
    /**
5441
     * Stripe all empty items.
5442
     *
5443
     * @return static
5444
     *                <p>(Immutable)</p>
5445
     *
5446
     * @psalm-return static<TKey,T>
5447
     * @psalm-mutation-free
5448
     */
5449 1
    public function stripEmpty(): self
5450
    {
5451 1
        return $this->filter(
5452
            static function ($item) {
5453 1
                if ($item === null) {
5454 1
                    return false;
5455
                }
5456
5457 1
                return (bool) \trim((string) $item);
5458 1
            }
5459
        );
5460
    }
5461
5462
    /**
5463
     * Swap two values between positions by key.
5464
     *
5465
     * @param int|string $swapA <p>a key in the array</p>
5466
     * @param int|string $swapB <p>a key in the array</p>
5467
     *
5468
     * @return static
5469
     *                <p>(Immutable)</p>
5470
     *
5471
     * @psalm-return static<TKey,T>
5472
     * @psalm-mutation-free
5473
     */
5474 1
    public function swap($swapA, $swapB): self
5475
    {
5476 1
        $array = $this->toArray();
5477
5478 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5479
5480 1
        return static::create(
5481 1
            $array,
5482 1
            $this->iteratorClass,
5483 1
            false
5484
        );
5485
    }
5486
5487
    /**
5488
     * Get the current array from the "Arrayy"-object.
5489
     * alias for "getArray()"
5490
     *
5491
     * @param bool $convertAllArrayyElements <p>
5492
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5493
     *                                       </p>
5494
     * @param bool $preserveKeys             <p>
5495
     *                                       e.g.: A generator maybe return the same key more then once,
5496
     *                                       so maybe you will ignore the keys.
5497
     *                                       </p>
5498
     *
5499
     * @return array
5500
     *
5501
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5502
     * @psalm-mutation-free
5503
     */
5504 893
    public function toArray(
5505
        bool $convertAllArrayyElements = false,
5506
        bool $preserveKeys = true
5507
    ): array {
5508
        // init
5509 893
        $array = [];
5510
5511 893
        if ($convertAllArrayyElements) {
5512 2
            foreach ($this->getGenerator() as $key => $value) {
5513 2
                if ($value instanceof self) {
5514 1
                    $value = $value->toArray(true);
5515
                }
5516
5517 2
                if ($preserveKeys) {
5518 1
                    $array[$key] = $value;
5519
                } else {
5520 2
                    $array[] = $value;
5521
                }
5522
            }
5523
        } else {
5524 893
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5525
        }
5526
5527 893
        return $array;
5528
    }
5529
5530
    /**
5531
     * Get the current array from the "Arrayy"-object as list.
5532
     *
5533
     * @param bool $convertAllArrayyElements <p>
5534
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5535
     *                                       </p>
5536
     *
5537
     * @return array
5538
     *
5539
     * @psalm-return array<int,mixed>|array<int,T>
5540
     * @psalm-mutation-free
5541
     */
5542 1
    public function toList(bool $convertAllArrayyElements = false): array
5543
    {
5544 1
        return $this->toArray(
5545 1
            $convertAllArrayyElements,
5546 1
            false
5547
        );
5548
    }
5549
5550
    /**
5551
     * Convert the current array to JSON.
5552
     *
5553
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
5554
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
5555
     *
5556
     * @return string
5557
     */
5558 7
    public function toJson(int $options = 0, int $depth = 512): string
5559
    {
5560 7
        $return = \json_encode($this->toArray(), $options, $depth);
5561 7
        if ($return === false) {
5562
            return '';
5563
        }
5564
5565 7
        return $return;
5566
    }
5567
5568
    /**
5569
     * @param string[]|null $items  [optional]
5570
     * @param string[]      $helper [optional]
5571
     *
5572
     * @return static[]|static
5573
     *
5574
     * @psalm-return static<int, static<TKey,T>>
5575
     */
5576 1
    public function toPermutation(array $items = null, array $helper = []): self
5577
    {
5578
        // init
5579 1
        $return = [];
5580
5581 1
        if ($items === null) {
5582 1
            $items = $this->toArray();
5583
        }
5584
5585 1
        if (empty($items)) {
5586 1
            $return[] = $helper;
5587
        } else {
5588 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5589 1
                $new_items = $items;
5590 1
                $new_helper = $helper;
5591 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5592
                /** @noinspection PhpSillyAssignmentInspection */
5593
                /** @var string[] $new_items */
5594 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...
5595 1
                \array_unshift($new_helper, $tmp_helper);
5596
                /** @noinspection SlowArrayOperationsInLoopInspection */
5597 1
                $return = \array_merge(
5598 1
                    $return,
5599 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5600
                );
5601
            }
5602
        }
5603
5604 1
        return static::create(
5605 1
            $return,
5606 1
            $this->iteratorClass,
5607 1
            false
5608
        );
5609
    }
5610
5611
    /**
5612
     * Implodes array to a string with specified separator.
5613
     *
5614
     * @param string $separator [optional] <p>The element's separator.</p>
5615
     *
5616
     * @return string
5617
     *                <p>The string representation of array, separated by ",".</p>
5618
     */
5619 19
    public function toString(string $separator = ','): string
5620
    {
5621 19
        return $this->implode($separator);
5622
    }
5623
5624
    /**
5625
     * Return a duplicate free copy of the current array.
5626
     *
5627
     * @return $this
5628
     *               <p>(Mutable)</p>
5629
     *
5630
     * @psalm-return static<TKey,T>
5631
     */
5632 13
    public function unique(): self
5633
    {
5634
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5635
5636
        /**
5637
         * @psalm-suppress MissingClosureReturnType
5638
         * @psalm-suppress MissingClosureParamType
5639
         */
5640 13
        $this->array = $this->reduce(
5641
            static function ($resultArray, $value) {
5642 12
                if (!\in_array($value, $resultArray, true)) {
5643 12
                    $resultArray[] = $value;
5644
                }
5645
5646 12
                return $resultArray;
5647 13
            },
5648 13
            []
5649 13
        )->toArray();
5650 13
        $this->generator = null;
5651
5652 13
        return $this;
5653
    }
5654
5655
    /**
5656
     * Return a duplicate free copy of the current array. (with the old keys)
5657
     *
5658
     * @return $this
5659
     *               <p>(Mutable)</p>
5660
     *
5661
     * @psalm-return static<TKey,T>
5662
     */
5663 11
    public function uniqueKeepIndex(): self
5664
    {
5665
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5666
5667
        // init
5668 11
        $array = $this->toArray();
5669
5670
        /**
5671
         * @psalm-suppress MissingClosureReturnType
5672
         * @psalm-suppress MissingClosureParamType
5673
         */
5674 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...
5675 11
            \array_keys($array),
5676
            static function ($resultArray, $key) use ($array) {
5677 10
                if (!\in_array($array[$key], $resultArray, true)) {
5678 10
                    $resultArray[$key] = $array[$key];
5679
                }
5680
5681 10
                return $resultArray;
5682 11
            },
5683 11
            []
5684
        );
5685 11
        $this->generator = null;
5686
5687 11
        return $this;
5688
    }
5689
5690
    /**
5691
     * alias: for "Arrayy->unique()"
5692
     *
5693
     * @return static
5694
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
5695
     *
5696
     * @see          Arrayy::unique()
5697
     *
5698
     * @psalm-return static<TKey,T>
5699
     */
5700 10
    public function uniqueNewIndex(): self
5701
    {
5702 10
        return $this->unique();
5703
    }
5704
5705
    /**
5706
     * Prepends one or more values to the beginning of array at once.
5707
     *
5708
     * @param array ...$args
5709
     *
5710
     * @return $this
5711
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
5712
     *
5713
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
5714
     * @psalm-return static<TKey,T>
5715
     */
5716 4
    public function unshift(...$args): self
5717
    {
5718 4
        $this->generatorToArray();
5719
5720 4
        \array_unshift($this->array, ...$args);
5721
5722 4
        return $this;
5723
    }
5724
5725
    /**
5726
     * Tests whether the given closure return something valid for all elements of this array.
5727
     *
5728
     * @param \Closure $closure the predicate
5729
     *
5730
     * @return bool
5731
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
5732
     */
5733 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...
5734
    {
5735 1
        foreach ($this->getGenerator() as $key => $value) {
5736 1
            if (!$closure($value, $key)) {
5737 1
                return false;
5738
            }
5739
        }
5740
5741 1
        return true;
5742
    }
5743
5744
    /**
5745
     * Get all values from a array.
5746
     *
5747
     * @return static
5748
     *                <p>(Immutable)</p>
5749
     *
5750
     * @psalm-return static<TKey,T>
5751
     * @psalm-mutation-free
5752
     */
5753 2
    public function values(): self
5754
    {
5755 2
        return static::create(
5756
            function () {
5757
                /** @noinspection YieldFromCanBeUsedInspection */
5758 2
                foreach ($this->getGenerator() as $value) {
5759 2
                    yield $value;
5760
                }
5761 2
            },
5762 2
            $this->iteratorClass,
5763 2
            false
5764
        );
5765
    }
5766
5767
    /**
5768
     * Apply the given function to every element in the array, discarding the results.
5769
     *
5770
     * @param callable $callable
5771
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
5772
     *
5773
     * @return $this
5774
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
5775
     *
5776
     * @psalm-return static<TKey,T>
5777
     */
5778 12
    public function walk($callable, bool $recursive = false): self
5779
    {
5780 12
        $this->generatorToArray();
5781
5782 12
        if ($recursive === true) {
5783 6
            \array_walk_recursive($this->array, $callable);
5784
        } else {
5785 6
            \array_walk($this->array, $callable);
5786
        }
5787
5788 12
        return $this;
5789
    }
5790
5791
    /**
5792
     * Returns a collection of matching items.
5793
     *
5794
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
5795
     * @param mixed  $value                 the value to match
5796
     *
5797
     * @throws \InvalidArgumentException if property or method is not defined
5798
     *
5799
     * @return static
5800
     *
5801
     * @psalm-param  T $value
5802
     * @psalm-return static<TKey,T>
5803
     */
5804 1
    public function where(string $keyOrPropertyOrMethod, $value): self
5805
    {
5806 1
        return $this->filter(
5807
            function ($item) use ($keyOrPropertyOrMethod, $value) {
5808 1
                $accessorValue = $this->extractValue(
5809 1
                    $item,
5810 1
                    $keyOrPropertyOrMethod
5811
                );
5812
5813 1
                return $accessorValue === $value;
5814 1
            }
5815
        );
5816
    }
5817
5818
    /**
5819
     * Convert an array into a object.
5820
     *
5821
     * @param array $array
5822
     *
5823
     * @return \stdClass
5824
     *
5825
     * @psalm-param array<mixed,mixed> $array
5826
     */
5827 4
    final protected static function arrayToObject(array $array = []): \stdClass
5828
    {
5829
        // init
5830 4
        $object = new \stdClass();
5831
5832 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
5833 1
            return $object;
5834
        }
5835
5836 3
        foreach ($array as $name => $value) {
5837 3
            if (\is_array($value) === true) {
5838 1
                $object->{$name} = static::arrayToObject($value);
5839
            } else {
5840 3
                $object->{$name} = $value;
5841
            }
5842
        }
5843
5844 3
        return $object;
5845
    }
5846
5847
    /**
5848
     * @param array|\Generator|null $input         <p>
5849
     *                                             An array containing keys to return.
5850
     *                                             </p>
5851
     * @param mixed|null            $search_values [optional] <p>
5852
     *                                             If specified, then only keys containing these values are returned.
5853
     *                                             </p>
5854
     * @param bool                  $strict        [optional] <p>
5855
     *                                             Determines if strict comparison (===) should be used during the
5856
     *                                             search.
5857
     *                                             </p>
5858
     *
5859
     * @return array
5860
     *               <p>an array of all the keys in input</p>
5861
     *
5862
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
5863
     * @psalm-return array<TKey|mixed>
5864
     * @psalm-mutation-free
5865
     */
5866 11
    protected function array_keys_recursive(
5867
        $input = null,
5868
        $search_values = null,
5869
        bool $strict = true
5870
    ): array {
5871
        // init
5872 11
        $keys = [];
5873 11
        $keysTmp = [];
5874
5875 11
        if ($input === null) {
5876 4
            $input = $this->getGenerator();
5877
        }
5878
5879 11
        if ($search_values === null) {
5880 11
            foreach ($input as $key => $value) {
5881 11
                $keys[] = $key;
5882
5883
                // check if recursive is needed
5884 11
                if (\is_array($value) === true) {
5885 11
                    $keysTmp[] = $this->array_keys_recursive($value);
5886
                }
5887
            }
5888
        } else {
5889 1
            $is_array_tmp = \is_array($search_values);
5890
5891 1
            foreach ($input as $key => $value) {
5892 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...
5893
                    (
5894 1
                        $is_array_tmp === false
5895
                        &&
5896 1
                        $strict === true
5897
                        &&
5898 1
                        $search_values === $value
5899
                    )
5900
                    ||
5901
                    (
5902 1
                        $is_array_tmp === false
5903
                        &&
5904 1
                        $strict === false
5905
                        &&
5906 1
                        $search_values == $value
5907
                    )
5908
                    ||
5909
                    (
5910 1
                        $is_array_tmp === true
5911
                        &&
5912 1
                        \in_array($value, $search_values, $strict)
5913
                    )
5914
                ) {
5915 1
                    $keys[] = $key;
5916
                }
5917
5918
                // check if recursive is needed
5919 1
                if (\is_array($value) === true) {
5920 1
                    $keysTmp[] = $this->array_keys_recursive($value);
5921
                }
5922
            }
5923
        }
5924
5925 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
5926
    }
5927
5928
    /**
5929
     * @param mixed      $path
5930
     * @param callable   $callable
5931
     * @param array|null $currentOffset
5932
     *
5933
     * @return void
5934
     *
5935
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
5936
     * @psalm-mutation-free
5937
     */
5938 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
5939
    {
5940 4
        $this->generatorToArray();
5941
5942 4
        if ($currentOffset === null) {
5943 4
            $currentOffset = &$this->array;
5944
        }
5945
5946 4
        $explodedPath = \explode($this->pathSeparator, $path);
5947 4
        if ($explodedPath === false) {
5948
            return;
5949
        }
5950
5951 4
        $nextPath = \array_shift($explodedPath);
5952
5953 4
        if (!isset($currentOffset[$nextPath])) {
5954
            return;
5955
        }
5956
5957 4
        if (!empty($explodedPath)) {
5958 1
            $this->callAtPath(
5959 1
                \implode($this->pathSeparator, $explodedPath),
5960 1
                $callable,
5961 1
                $currentOffset[$nextPath]
5962
            );
5963
        } else {
5964 4
            $callable($currentOffset[$nextPath]);
5965
        }
5966 4
    }
5967
5968
    /**
5969
     * Extracts the value of the given property or method from the object.
5970
     *
5971
     * @param static $object                <p>The object to extract the value from.</p>
5972
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
5973
     *                                      value should be extracted.</p>
5974
     *
5975
     * @throws \InvalidArgumentException if the method or property is not defined
5976
     *
5977
     * @return mixed
5978
     *               <p>The value extracted from the specified property or method.</p>
5979
     *
5980
     * @psalm-param self<TKey,T> $object
5981
     */
5982 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
5983
    {
5984 2
        if (isset($object[$keyOrPropertyOrMethod])) {
5985 2
            $return = $object->get($keyOrPropertyOrMethod);
5986
5987 2
            if ($return instanceof self) {
5988 1
                return $return->toArray();
5989
            }
5990
5991 1
            return $return;
5992
        }
5993
5994
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
5995
            return $object->{$keyOrPropertyOrMethod};
5996
        }
5997
5998
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
5999
            return $object->{$keyOrPropertyOrMethod}();
6000
        }
6001
6002
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6003
    }
6004
6005
    /**
6006
     * create a fallback for array
6007
     *
6008
     * 1. use the current array, if it's a array
6009
     * 2. fallback to empty array, if there is nothing
6010
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6011
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6012
     * 5. call "__toArray()" on object, if the method exists
6013
     * 6. cast a string or object with "__toString()" into an array
6014
     * 7. throw a "InvalidArgumentException"-Exception
6015
     *
6016
     * @param mixed $data
6017
     *
6018
     * @throws \InvalidArgumentException
6019
     *
6020
     * @return array
6021
     *
6022
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6023
     */
6024 1100
    protected function fallbackForArray(&$data): array
6025
    {
6026 1100
        $data = $this->internalGetArray($data);
6027
6028 1100
        if ($data === null) {
6029 2
            throw new \InvalidArgumentException('Passed value should be a array');
6030
        }
6031
6032 1098
        return $data;
6033
    }
6034
6035
    /**
6036
     * @param bool $preserveKeys <p>
6037
     *                           e.g.: A generator maybe return the same key more then once,
6038
     *                           so maybe you will ignore the keys.
6039
     *                           </p>
6040
     *
6041
     * @return bool
6042
     *
6043
     * @noinspection ReturnTypeCanBeDeclaredInspection
6044
     * @psalm-mutation-free :/
6045
     */
6046 1014
    protected function generatorToArray(bool $preserveKeys = true)
6047
    {
6048 1014
        if ($this->generator) {
6049 2
            $this->array = $this->toArray(false, $preserveKeys);
6050 2
            $this->generator = null;
6051
6052 2
            return true;
6053
        }
6054
6055 1014
        return false;
6056
    }
6057
6058
    /**
6059
     * Get correct PHP constant for direction.
6060
     *
6061
     * @param int|string $direction
6062
     *
6063
     * @return int
6064
     * @psalm-mutation-free
6065
     */
6066 43
    protected function getDirection($direction): int
6067
    {
6068 43
        if ((string) $direction === $direction) {
6069 10
            $direction = \strtolower($direction);
6070
6071 10
            if ($direction === 'desc') {
6072 2
                $direction = \SORT_DESC;
6073
            } else {
6074 8
                $direction = \SORT_ASC;
6075
            }
6076
        }
6077
6078
        if (
6079 43
            $direction !== \SORT_DESC
6080
            &&
6081 43
            $direction !== \SORT_ASC
6082
        ) {
6083
            $direction = \SORT_ASC;
6084
        }
6085
6086 43
        return $direction;
6087
    }
6088
6089
    /**
6090
     * @return TypeCheckPhpDoc[]
6091
     *
6092
     * @noinspection ReturnTypeCanBeDeclaredInspection
6093
     */
6094 16
    protected function getPropertiesFromPhpDoc()
6095
    {
6096 16
        static $PROPERTY_CACHE = [];
6097 16
        $cacheKey = 'Class::' . static::class;
6098
6099 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6100 15
            return $PROPERTY_CACHE[$cacheKey];
6101
        }
6102
6103
        // init
6104 2
        $properties = [];
6105
6106 2
        $reflector = new \ReflectionClass($this);
6107 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6108 2
        $docComment = $reflector->getDocComment();
6109 2
        if ($docComment) {
6110 2
            $docblock = $factory->create($docComment);
6111
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6112 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6113 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
6114
            }
6115
        }
6116
6117 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6118
    }
6119
6120
    /**
6121
     * @param mixed $glue
6122
     * @param mixed $pieces
6123
     * @param bool  $useKeys
6124
     *
6125
     * @return string
6126
     * @psalm-mutation-free
6127
     */
6128 36
    protected function implode_recursive(
6129
        $glue = '',
6130
        $pieces = [],
6131
        bool $useKeys = false
6132
    ): string {
6133 36
        if ($pieces instanceof self) {
6134 1
            $pieces = $pieces->toArray();
6135
        }
6136
6137 36
        if (\is_array($pieces) === true) {
6138 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6139 36
            $pieces_count_not_zero = $pieces_count > 0;
6140
6141 36
            return \implode(
6142 36
                $glue,
6143 36
                \array_map(
6144 36
                    [$this, 'implode_recursive'],
6145 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6146 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6147
                )
6148
            );
6149
        }
6150
6151
        if (
6152 36
            \is_scalar($pieces) === true
6153
            ||
6154 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6155
        ) {
6156 32
            return (string) $pieces;
6157
        }
6158
6159 8
        return '';
6160
    }
6161
6162
    /**
6163
     * @param mixed                 $needle   <p>
6164
     *                                        The searched value.
6165
     *                                        </p>
6166
     *                                        <p>
6167
     *                                        If needle is a string, the comparison is done
6168
     *                                        in a case-sensitive manner.
6169
     *                                        </p>
6170
     * @param array|\Generator|null $haystack <p>
6171
     *                                        The array.
6172
     *                                        </p>
6173
     * @param bool                  $strict   [optional] <p>
6174
     *                                        If the third parameter strict is set to true
6175
     *                                        then the in_array function will also check the
6176
     *                                        types of the
6177
     *                                        needle in the haystack.
6178
     *                                        </p>
6179
     *
6180
     * @return bool
6181
     *              <p>true if needle is found in the array, false otherwise</p>
6182
     *
6183
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6184
     * @psalm-mutation-free
6185
     */
6186 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6187
    {
6188 18
        if ($haystack === null) {
6189
            $haystack = $this->getGenerator();
6190
        }
6191
6192 18
        foreach ($haystack as $item) {
6193 14
            if (\is_array($item) === true) {
6194 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6195
            } else {
6196
                /** @noinspection NestedPositiveIfStatementsInspection */
6197 14
                if ($strict === true) {
6198 14
                    $returnTmp = $item === $needle;
6199
                } else {
6200
                    $returnTmp = $item == $needle;
6201
                }
6202
            }
6203
6204 14
            if ($returnTmp === true) {
6205 14
                return true;
6206
            }
6207
        }
6208
6209 8
        return false;
6210
    }
6211
6212
    /**
6213
     * @param mixed $data
6214
     *
6215
     * @return array|null
6216
     *
6217
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6218
     */
6219 1100
    protected function internalGetArray(&$data)
6220
    {
6221 1100
        if (\is_array($data) === true) {
6222 1095
            return $data;
6223
        }
6224
6225 54
        if (!$data) {
6226 6
            return [];
6227
        }
6228
6229 53
        if (\is_object($data) === true) {
6230 48
            if ($data instanceof \ArrayObject) {
6231 4
                return $data->getArrayCopy();
6232
            }
6233
6234 45
            if ($data instanceof \Generator) {
6235
                return static::createFromGeneratorImmutable($data)->toArray();
6236
            }
6237
6238 45
            if ($data instanceof \Traversable) {
6239
                return static::createFromObject($data)->toArray();
6240
            }
6241
6242 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...
6243
                return (array) $data->jsonSerialize();
6244
            }
6245
6246 45
            if (\method_exists($data, '__toArray')) {
6247
                return (array) $data->__toArray();
6248
            }
6249
6250 45
            if (\method_exists($data, '__toString')) {
6251
                return [(string) $data];
6252
            }
6253
        }
6254
6255 50
        if (\is_callable($data)) {
6256
            /**
6257
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6258
             */
6259 43
            $this->generator = new ArrayyRewindableGenerator($data);
6260
6261 43
            return [];
6262
        }
6263
6264 9
        if (\is_scalar($data)) {
6265 7
            return [$data];
6266
        }
6267
6268 2
        return null;
6269
    }
6270
6271
    /**
6272
     * Internal mechanics of remove method.
6273
     *
6274
     * @param mixed $key
6275
     *
6276
     * @return bool
6277
     */
6278 18
    protected function internalRemove($key): bool
6279
    {
6280 18
        $this->generatorToArray();
6281
6282
        if (
6283 18
            $this->pathSeparator
6284
            &&
6285 18
            (string) $key === $key
6286
            &&
6287 18
            \strpos($key, $this->pathSeparator) !== false
6288
        ) {
6289
            $path = \explode($this->pathSeparator, (string) $key);
6290
6291
            if ($path !== false) {
6292
                // crawl though the keys
6293
                while (\count($path, \COUNT_NORMAL) > 1) {
6294
                    $key = \array_shift($path);
6295
6296
                    if (!$this->has($key)) {
6297
                        return false;
6298
                    }
6299
6300
                    $this->array = &$this->array[$key];
6301
                }
6302
6303
                $key = \array_shift($path);
6304
            }
6305
        }
6306
6307 18
        unset($this->array[$key]);
6308
6309 18
        return true;
6310
    }
6311
6312
    /**
6313
     * Internal mechanic of set method.
6314
     *
6315
     * @param int|string|null $key
6316
     * @param mixed           $value
6317
     * @param bool            $checkProperties
6318
     *
6319
     * @return bool
6320
     */
6321 962
    protected function internalSet(
6322
        $key,
6323
        &$value,
6324
        bool $checkProperties = true
6325
    ): bool {
6326
        if (
6327 962
            $checkProperties === true
6328
            &&
6329 962
            $this->properties !== []
6330
        ) {
6331 87
            $this->checkType($key, $value);
6332
        }
6333
6334 960
        if ($key === null) {
6335
            return false;
6336
        }
6337
6338 960
        $this->generatorToArray();
6339
6340
        /** @var array<int|string,mixed> $array */
6341 960
        $array = &$this->array;
6342
6343
        /**
6344
         * https://github.com/vimeo/psalm/issues/2536
6345
         *
6346
         * @psalm-suppress PossiblyInvalidArgument
6347
         * @psalm-suppress InvalidScalarArgument
6348
         */
6349
        if (
6350 960
            $this->pathSeparator
6351
            &&
6352 960
            (string) $key === $key
6353
            &&
6354 960
            \strpos($key, $this->pathSeparator) !== false
6355
        ) {
6356 3
            $path = \explode($this->pathSeparator, (string) $key);
6357
6358 3
            if ($path !== false) {
6359
                // crawl through the keys
6360 3
                while (\count($path, \COUNT_NORMAL) > 1) {
6361 3
                    $key = \array_shift($path);
6362
6363 3
                    $array = &$array[$key];
6364
                }
6365
6366 3
                $key = \array_shift($path);
6367
            }
6368
        }
6369
6370 960
        $array[$key] = $value;
6371
6372 960
        return true;
6373
    }
6374
6375
    /**
6376
     * Convert a object into an array.
6377
     *
6378
     * @param mixed|object $object
6379
     *
6380
     * @return array|mixed
6381
     *
6382
     * @psalm-mutation-free
6383
     */
6384 5
    protected static function objectToArray($object)
6385
    {
6386 5
        if (!\is_object($object)) {
6387 4
            return $object;
6388
        }
6389
6390 5
        $object = \get_object_vars($object);
6391
6392
        /**
6393
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6394
         */
6395 5
        return \array_map(['static', 'objectToArray'], $object);
6396
    }
6397
6398
    /**
6399
     * @param array $data
6400
     * @param bool  $checkPropertiesInConstructor
6401
     *
6402
     * @return void
6403
     *
6404
     * @psalm-param array<mixed,T> $data
6405
     */
6406 1098
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6407
    {
6408 1098
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6409
                                        &&
6410 1098
                                        $checkPropertiesInConstructor === true;
6411
6412 1098
        if ($this->properties !== []) {
6413 78
            foreach ($data as $key => &$valueInner) {
6414 78
                $this->internalSet(
6415 78
                    $key,
6416 78
                    $valueInner,
6417 78
                    $checkPropertiesInConstructor
6418
                );
6419
            }
6420
        } else {
6421
            if (
6422 1031
                $this->checkPropertyTypes === true
6423
                ||
6424 1031
                $checkPropertiesInConstructor === true
6425
            ) {
6426 16
                $this->properties = $this->getPropertiesFromPhpDoc();
6427
            }
6428
6429
            /** @var TypeCheckInterface[] $properties */
6430 1031
            $properties = $this->properties;
6431
6432
            if (
6433 1031
                $this->checkPropertiesMismatchInConstructor === true
6434
                &&
6435 1031
                \count($data) !== 0
6436
                &&
6437 1031
                \count(\array_diff_key($properties, $data)) > 0
6438
            ) {
6439 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...
6440
            }
6441
6442 1030
            foreach ($data as $key => &$valueInner) {
6443 888
                $this->internalSet(
6444 888
                    $key,
6445 888
                    $valueInner,
6446 888
                    $checkPropertiesInConstructor
6447
                );
6448
            }
6449
        }
6450 1091
    }
6451
6452
    /**
6453
     * sorting keys
6454
     *
6455
     * @param array      $elements
6456
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6457
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6458
     *                              <strong>SORT_NATURAL</strong></p>
6459
     *
6460
     * @return $this
6461
     *               <p>(Mutable) Return this Arrayy object.</p>
6462
     *
6463
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6464
     * @psalm-return static<TKey,T>
6465
     */
6466 18
    protected function sorterKeys(
6467
        array &$elements,
6468
        $direction = \SORT_ASC,
6469
        int $strategy = \SORT_REGULAR
6470
    ): self {
6471 18
        $direction = $this->getDirection($direction);
6472
6473
        switch ($direction) {
6474 18
            case 'desc':
6475 18
            case \SORT_DESC:
6476 6
                \krsort($elements, $strategy);
6477
6478 6
                break;
6479 13
            case 'asc':
6480 13
            case \SORT_ASC:
6481
            default:
6482 13
                \ksort($elements, $strategy);
6483
        }
6484
6485 18
        return $this;
6486
    }
6487
6488
    /**
6489
     * @param array      $elements  <p>Warning: used as reference</p>
6490
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6491
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6492
     *                              <strong>SORT_NATURAL</strong></p>
6493
     * @param bool       $keepKeys
6494
     *
6495
     * @return $this
6496
     *               <p>(Mutable) Return this Arrayy object.</p>
6497
     *
6498
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6499
     * @psalm-return static<TKey,T>
6500
     */
6501 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6502
    {
6503 24
        $direction = $this->getDirection($direction);
6504
6505 24
        if (!$strategy) {
6506 24
            $strategy = \SORT_REGULAR;
6507
        }
6508
6509
        switch ($direction) {
6510 24
            case 'desc':
6511 24
            case \SORT_DESC:
6512 13
                if ($keepKeys) {
6513 9
                    \arsort($elements, $strategy);
6514
                } else {
6515 4
                    \rsort($elements, $strategy);
6516
                }
6517
6518 13
                break;
6519 11
            case 'asc':
6520 11
            case \SORT_ASC:
6521
            default:
6522 11
                if ($keepKeys) {
6523 4
                    \asort($elements, $strategy);
6524
                } else {
6525 7
                    \sort($elements, $strategy);
6526
                }
6527
        }
6528
6529 24
        return $this;
6530
    }
6531
6532
    /**
6533
     * @param int|string|null $key
6534
     * @param mixed           $value
6535
     *
6536
     * @return void
6537
     */
6538 87
    private function checkType($key, $value)
6539
    {
6540
        if (
6541 87
            $key !== null
6542
            &&
6543 87
            isset($this->properties[$key]) === false
6544
            &&
6545 87
            $this->checkPropertiesMismatch === true
6546
        ) {
6547
            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...
6548
        }
6549
6550 87
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6551 76
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6552 17
        } elseif ($key !== null && isset($this->properties[$key])) {
6553 17
            $this->properties[$key]->checkType($value);
6554
        }
6555 85
    }
6556
}
6557