Completed
Push — master ( 2ca203...503784 )
by Lars
01:54
created

Arrayy::appendToEachKey()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19

Duplication

Lines 19
Ratio 100 %

Code Coverage

Tests 7
CRAP Score 4.7691

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 1
dl 19
loc 19
ccs 7
cts 11
cp 0.6364
crap 4.7691
rs 9.6333
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
    const ARRAYY_HELPER_WALK = '!!!!Arrayy_Helper_Walk!!!!';
32
33
    /**
34
     * @var array
35
     *
36
     * @psalm-var array<mixed,mixed>|array<TKey,T>
37
     */
38
    protected $array = [];
39
40
    /**
41
     * @var \Arrayy\ArrayyRewindableGenerator|null
42
     *
43
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
44
     */
45
    protected $generator;
46
47
    /**
48
     * @var string
49
     *
50
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
51
     */
52
    protected $iteratorClass = ArrayyIterator::class;
53
54
    /**
55
     * @var string
56
     */
57
    protected $pathSeparator = '.';
58
59
    /**
60
     * @var bool
61
     */
62
    protected $checkPropertyTypes = false;
63
64
    /**
65
     * @var bool
66
     */
67
    protected $checkForMissingPropertiesInConstructor = false;
68
69
    /**
70
     * @var bool
71
     */
72
    protected $checkPropertiesMismatchInConstructor = false;
73
74
    /**
75
     * @var bool
76
     */
77
    protected $checkPropertiesMismatch = true;
78
79
    /**
80
     * @var array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
81
     */
82
    protected $properties = [];
83
84
    /**
85
     * Initializes
86
     *
87
     * @param mixed  $data                         <p>
88
     *                                             Should be an array or a generator, otherwise it will try
89
     *                                             to convert it into an array.
90
     *                                             </p>
91
     * @param string $iteratorClass                optional <p>
92
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
93
     *                                             need this option.
94
     *                                             </p>
95
     * @param bool   $checkPropertiesInConstructor optional <p>
96
     *                                             You need to extend the "Arrayy"-class and you need to set
97
     *                                             the $checkPropertiesMismatchInConstructor class property
98
     *                                             to
99
     *                                             true, otherwise this option didn't not work anyway.
100
     *                                             </p>
101
     *
102
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
103
     */
104 1198
    public function __construct(
105
        $data = [],
106
        string $iteratorClass = ArrayyIterator::class,
107
        bool $checkPropertiesInConstructor = true
108
    ) {
109 1198
        $data = $this->fallbackForArray($data);
110
111
        // used only for serialize + unserialize, all other methods are overwritten
112
        /**
113
         * @psalm-suppress InvalidArgument - why?
114
         */
115 1196
        parent::__construct([], 0, $iteratorClass);
116
117 1196
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
118
119 1189
        $this->setIteratorClass($iteratorClass);
120 1189
    }
121
122
    /**
123
     * @return void
124
     */
125 51
    public function __clone()
126
    {
127 51
        if (!\is_array($this->properties)) {
128
            $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...
129
        }
130
131 51
        if ($this->generator !== null) {
132
            $this->generator = clone $this->generator;
133
        }
134 51
    }
135
136
    /**
137
     * Call object as function.
138
     *
139
     * @param mixed $key
140
     *
141
     * @return mixed
142
     */
143 1
    public function __invoke($key = null)
144
    {
145 1
        if ($key !== null) {
146 1
            $this->generatorToArray();
147
148 1
            return $this->array[$key] ?? false;
149
        }
150
151
        return $this->toArray();
152
    }
153
154
    /**
155
     * Whether or not an element exists by key.
156
     *
157
     * @param mixed $key
158
     *
159
     * @return bool
160
     *              <p>True is the key/index exists, otherwise false.</p>
161
     */
162
    public function __isset($key): bool
163
    {
164
        return $this->offsetExists($key);
165
    }
166
167
    /**
168
     * Assigns a value to the specified element.
169
     *
170
     * @param mixed $key
171
     * @param mixed $value
172
     *
173
     * @return void
174
     */
175 3
    public function __set($key, $value)
176
    {
177 3
        $this->internalSet($key, $value);
178 3
    }
179
180
    /**
181
     * magic to string
182
     *
183
     * @return string
184
     */
185 15
    public function __toString(): string
186
    {
187 15
        return $this->toString();
188
    }
189
190
    /**
191
     * Unset element by key.
192
     *
193
     * @param mixed $key
194
     */
195
    public function __unset($key)
196
    {
197
        $this->internalRemove($key);
198
    }
199
200
    /**
201
     * Get a value by key.
202
     *
203
     * @param mixed $key
204
     *
205
     * @return mixed
206
     *               <p>Get a Value from the current array.</p>
207
     */
208 126
    public function &__get($key)
209
    {
210 126
        $return = $this->get($key, null, null, true);
211
212 126
        if (\is_array($return) === true) {
213
            $return = static::create($return, $this->iteratorClass, false);
214
        }
215
216 126
        return $return;
217
    }
218
219
    /**
220
     * Add new values (optional using dot-notation).
221
     *
222
     * @param mixed           $value
223
     * @param int|string|null $key
224
     *
225
     * @return static
226
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
227
     *
228
     * @psalm-param  T $value
229
     * @psalm-return static<TKey,T>
230
     */
231 13
    public function add($value, $key = null)
232
    {
233 13
        if ($key !== null) {
234 5
            $get = $this->get($key);
235 5
            if ($get !== null) {
236 1
                $value = \array_merge_recursive(
237 1
                    !$get instanceof self ? [$get] : $get->getArray(),
238 1
                    !\is_array($value) ? [$value] : $value
239
                );
240
            }
241
242 5
            $this->internalSet($key, $value);
243
244 4
            return $this;
245
        }
246
247 8
        return $this->append($value);
248
    }
249
250
    /**
251
     * Append a (key) + value to the current array.
252
     *
253
     * EXAMPLE: <code>
254
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
255
     * </code>
256
     *
257
     * @param mixed $value
258
     * @param mixed $key
259
     *
260
     * @return $this
261
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
262
     *
263
     * @psalm-return static<TKey,T>
264
     */
265 20
    public function append($value, $key = null): self
266
    {
267 20
        $this->generatorToArray();
268
269 20
        if ($this->properties !== []) {
270 6
            $this->checkType($key, $value);
271
        }
272
273 19
        if ($key !== null) {
274
            if (
275 2
                isset($this->array[$key])
276
                &&
277 2
                \is_array($this->array[$key]) === true
278
            ) {
279
                $this->array[$key][] = $value;
280
            } else {
281 2
                $this->array[$key] = $value;
282
            }
283
        } else {
284 17
            $this->array[] = $value;
285
        }
286
287 19
        return $this;
288
    }
289
290
    /**
291
     * Sort the entries by value.
292
     *
293
     * @param int $sort_flags [optional] <p>
294
     *                        You may modify the behavior of the sort using the optional
295
     *                        parameter sort_flags, for details
296
     *                        see sort.
297
     *                        </p>
298
     *
299
     * @return $this
300
     *               <p>(Mutable) Return this Arrayy object.</p>
301
     *
302
     * @psalm-return static<TKey,T>
303
     */
304 4
    public function asort(int $sort_flags = 0): self
305
    {
306 4
        $this->generatorToArray();
307
308 4
        \asort($this->array, $sort_flags);
309
310 4
        return $this;
311
    }
312
313
    /**
314
     * Sort the entries by value.
315
     *
316
     * @param int $sort_flags [optional] <p>
317
     *                        You may modify the behavior of the sort using the optional
318
     *                        parameter sort_flags, for details
319
     *                        see sort.
320
     *                        </p>
321
     *
322
     * @return $this
323
     *               <p>(Immutable) Return this Arrayy object.</p>
324
     *
325
     * @psalm-return static<TKey,T>
326
     * @psalm-mutation-free
327
     */
328 4
    public function asortImmutable(int $sort_flags = 0): self
329
    {
330 4
        $that = clone $this;
331
332
        /**
333
         * @psalm-suppress ImpureMethodCall - object is already cloned
334
         */
335 4
        $that->asort($sort_flags);
336
337 4
        return $that;
338
    }
339
340
    /**
341
     * Counts all elements in an array, or something in an object.
342
     *
343
     * <p>
344
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
345
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
346
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
347
     * implemented and used in PHP.
348
     * </p>
349
     *
350
     * @see http://php.net/manual/en/function.count.php
351
     *
352
     * @param int $mode [optional] If the optional mode parameter is set to
353
     *                  COUNT_RECURSIVE (or 1), count
354
     *                  will recursively count the array. This is particularly useful for
355
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
356
     *
357
     * @return int
358
     *             <p>
359
     *             The number of elements in var, which is
360
     *             typically an array, since anything else will have one
361
     *             element.
362
     *             </p>
363
     *             <p>
364
     *             If var is not an array or an object with
365
     *             implemented Countable interface,
366
     *             1 will be returned.
367
     *             There is one exception, if var is &null;,
368
     *             0 will be returned.
369
     *             </p>
370
     *             <p>
371
     *             Caution: count may return 0 for a variable that isn't set,
372
     *             but it may also return 0 for a variable that has been initialized with an
373
     *             empty array. Use isset to test if a variable is set.
374
     *             </p>
375
     * @psalm-mutation-free
376
     */
377 148
    public function count(int $mode = \COUNT_NORMAL): int
378
    {
379
        if (
380 148
            $this->generator
381
            &&
382 148
            $mode === \COUNT_NORMAL
383
        ) {
384 4
            return \iterator_count($this->generator);
385
        }
386
387 144
        return \count($this->toArray(), $mode);
388
    }
389
390
    /**
391
     * Exchange the array for another one.
392
     *
393
     * @param array|static $data
394
     *
395
     * @return array
396
     *
397
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
398
     * @psalm-return array<mixed,mixed>|array<TKey,T>
399
     */
400 1
    public function exchangeArray($data): array
401
    {
402 1
        $this->array = $this->fallbackForArray($data);
403
404 1
        return $this->array;
405
    }
406
407
    /**
408
     * Creates a copy of the ArrayyObject.
409
     *
410
     * @return array
411
     *
412
     * @psalm-return array<mixed,mixed>|array<TKey,T>
413
     */
414 6
    public function getArrayCopy(): array
415
    {
416 6
        $this->generatorToArray();
417
418 6
        return $this->array;
419
    }
420
421
    /**
422
     * Returns a new iterator, thus implementing the \Iterator interface.
423
     *
424
     * @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...
425
     *                          <p>An iterator for the values in the array.</p>
426
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
427
     */
428 27
    public function getIterator(): \Iterator
429
    {
430 27
        if ($this->generator instanceof ArrayyRewindableGenerator) {
431 1
            return $this->generator;
432
        }
433
434 26
        $iterator = $this->getIteratorClass();
435
436 26
        if ($iterator === ArrayyIterator::class) {
437 26
            return new $iterator($this->toArray(), 0, static::class);
438
        }
439
440
        $return = new $iterator($this->toArray());
441
        \assert($return instanceof \Iterator);
442
443
        return $return;
444
    }
445
446
    /**
447
     * Gets the iterator classname for the ArrayObject.
448
     *
449
     * @return string
450
     *
451
     * @psalm-return class-string
452
     */
453 26
    public function getIteratorClass(): string
454
    {
455 26
        return $this->iteratorClass;
456
    }
457
458
    /**
459
     * Sort the entries by key.
460
     *
461
     * @param int $sort_flags [optional] <p>
462
     *                        You may modify the behavior of the sort using the optional
463
     *                        parameter sort_flags, for details
464
     *                        see sort.
465
     *                        </p>
466
     *
467
     * @return $this
468
     *               <p>(Mutable) Return this Arrayy object.</p>
469
     *
470
     * @psalm-return static<TKey,T>
471
     */
472 4
    public function ksort(int $sort_flags = 0): self
473
    {
474 4
        $this->generatorToArray();
475
476 4
        \ksort($this->array, $sort_flags);
477
478 4
        return $this;
479
    }
480
481
    /**
482
     * Sort the entries by key.
483
     *
484
     * @param int $sort_flags [optional] <p>
485
     *                        You may modify the behavior of the sort using the optional
486
     *                        parameter sort_flags, for details
487
     *                        see sort.
488
     *                        </p>
489
     *
490
     * @return $this
491
     *               <p>(Immutable) Return this Arrayy object.</p>
492
     *
493
     * @psalm-return static<TKey,T>
494
     */
495 4
    public function ksortImmutable(int $sort_flags = 0): self
496
    {
497 4
        $that = clone $this;
498
499
        /**
500
         * @psalm-suppress ImpureMethodCall - object is already cloned
501
         */
502 4
        $that->ksort($sort_flags);
503
504 4
        return $that;
505
    }
506
507
    /**
508
     * Sort an array using a case insensitive "natural order" algorithm.
509
     *
510
     * @return $this
511
     *               <p>(Mutable) Return this Arrayy object.</p>
512
     *
513
     * @psalm-return static<TKey,T>
514
     */
515 8
    public function natcasesort(): self
516
    {
517 8
        $this->generatorToArray();
518
519 8
        \natcasesort($this->array);
520
521 8
        return $this;
522
    }
523
524
    /**
525
     * Sort an array using a case insensitive "natural order" algorithm.
526
     *
527
     * @return $this
528
     *               <p>(Immutable) Return this Arrayy object.</p>
529
     *
530
     * @psalm-return static<TKey,T>
531
     * @psalm-mutation-free
532
     */
533 4
    public function natcasesortImmutable(): self
534
    {
535 4
        $that = clone $this;
536
537
        /**
538
         * @psalm-suppress ImpureMethodCall - object is already cloned
539
         */
540 4
        $that->natcasesort();
541
542 4
        return $that;
543
    }
544
545
    /**
546
     * Sort entries using a "natural order" algorithm.
547
     *
548
     * @return $this
549
     *               <p>(Mutable) Return this Arrayy object.</p>
550
     *
551
     * @psalm-return static<TKey,T>
552
     */
553 10
    public function natsort(): self
554
    {
555 10
        $this->generatorToArray();
556
557 10
        \natsort($this->array);
558
559 10
        return $this;
560
    }
561
562
    /**
563
     * Sort entries using a "natural order" algorithm.
564
     *
565
     * @return $this
566
     *               <p>(Immutable) Return this Arrayy object.</p>
567
     *
568
     * @psalm-return static<TKey,T>
569
     * @psalm-mutation-free
570
     */
571 4
    public function natsortImmutable(): self
572
    {
573 4
        $that = clone $this;
574
575
        /**
576
         * @psalm-suppress ImpureMethodCall - object is already cloned
577
         */
578 4
        $that->natsort();
579
580 4
        return $that;
581
    }
582
583
    /**
584
     * Whether or not an offset exists.
585
     *
586
     * @param bool|int|string $offset
587
     *
588
     * @return bool
589
     *
590
     * @noinspection PhpSillyAssignmentInspection
591
     *
592
     * @psalm-mutation-free
593
     */
594 157
    public function offsetExists($offset): bool
595
    {
596 157
        $this->generatorToArray();
597
598 157
        if ($this->array === []) {
599 8
            return false;
600
        }
601
602
        // php cast "bool"-index into "int"-index
603 151
        if ((bool) $offset === $offset) {
604 1
            $offset = (int) $offset;
605
        }
606
607
        /** @var int|string $offset - hint for phpstan */
608 151
        $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...
609
610 151
        $tmpReturn = $this->keyExists($offset);
611
612
        if (
613 151
            $tmpReturn === true
614
            ||
615 151
            \strpos((string) $offset, $this->pathSeparator) === false
616
        ) {
617 148
            return $tmpReturn;
618
        }
619
620 4
        $offsetExists = false;
621
622
        /**
623
         * https://github.com/vimeo/psalm/issues/2536
624
         *
625
         * @psalm-suppress PossiblyInvalidArgument
626
         * @psalm-suppress InvalidScalarArgument
627
         */
628 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...
629 4
            $this->pathSeparator
630
            &&
631 4
            (string) $offset === $offset
632
            &&
633 4
            \strpos($offset, $this->pathSeparator) !== false
634
        ) {
635 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
636 4
            if ($explodedPath !== false) {
637
                /** @var string $lastOffset - helper for phpstan */
638 4
                $lastOffset = \array_pop($explodedPath);
639 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
640
641
                /**
642
                 * @psalm-suppress MissingClosureReturnType
643
                 * @psalm-suppress MissingClosureParamType
644
                 */
645 4
                $this->callAtPath(
646 4
                    $containerPath,
647 4
                    static function ($container) use ($lastOffset, &$offsetExists) {
648 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
649 4
                    }
650
                );
651
            }
652
        }
653
654 4
        return $offsetExists;
655
    }
656
657
    /**
658
     * Returns the value at specified offset.
659
     *
660
     * @param int|string $offset
661
     *
662
     * @return mixed
663
     *               <p>Will return null if the offset did not exists.</p>
664
     */
665 126
    public function &offsetGet($offset)
666
    {
667
        // init
668 126
        $value = null;
669
670 126
        if ($this->offsetExists($offset)) {
671 124
            $value = &$this->__get($offset);
672
        }
673
674 126
        return $value;
675
    }
676
677
    /**
678
     * Assigns a value to the specified offset + check the type.
679
     *
680
     * @param int|string|null $offset
681
     * @param mixed           $value
682
     *
683
     * @return void
684
     */
685 27
    public function offsetSet($offset, $value)
686
    {
687 27
        $this->generatorToArray();
688
689 27
        if ($offset === null) {
690 6
            if ($this->properties !== []) {
691 1
                $this->checkType(null, $value);
692
            }
693
694 5
            $this->array[] = $value;
695
        } else {
696 21
            $this->internalSet(
697 21
                $offset,
698 21
                $value,
699 21
                true
700
            );
701
        }
702 26
    }
703
704
    /**
705
     * Unset an offset.
706
     *
707
     * @param int|string $offset
708
     *
709
     * @return void
710
     *              <p>(Mutable) Return nothing.</p>
711
     */
712 25
    public function offsetUnset($offset)
713
    {
714 25
        $this->generatorToArray();
715
716 25
        if ($this->array === []) {
717 6
            return;
718
        }
719
720 20
        if ($this->keyExists($offset)) {
721 13
            unset($this->array[$offset]);
722
723 13
            return;
724
        }
725
726
        /**
727
         * https://github.com/vimeo/psalm/issues/2536
728
         *
729
         * @psalm-suppress PossiblyInvalidArgument
730
         * @psalm-suppress InvalidScalarArgument
731
         */
732 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...
733 10
            $this->pathSeparator
734
            &&
735 10
            (string) $offset === $offset
736
            &&
737 10
            \strpos($offset, $this->pathSeparator) !== false
738
        ) {
739 7
            $path = \explode($this->pathSeparator, (string) $offset);
740
741 7
            if ($path !== false) {
742 7
                $pathToUnset = \array_pop($path);
743
744
                /**
745
                 * @psalm-suppress MissingClosureReturnType
746
                 * @psalm-suppress MissingClosureParamType
747
                 */
748 7
                $this->callAtPath(
749 7
                    \implode($this->pathSeparator, $path),
750 7
                    static function (&$offset) use ($pathToUnset) {
751 6
                        if (\is_array($offset)) {
752 5
                            unset($offset[$pathToUnset]);
753
                        } else {
754 1
                            $offset = null;
755
                        }
756 7
                    }
757
                );
758
            }
759
        }
760
761 10
        unset($this->array[$offset]);
762 10
    }
763
764
    /**
765
     * Serialize the current "Arrayy"-object.
766
     *
767
     * @return string
768
     */
769 2
    public function serialize(): string
770
    {
771 2
        $this->generatorToArray();
772
773 2
        if (\PHP_VERSION_ID < 70400) {
774 2
            return parent::serialize();
775
        }
776
777
        return \serialize($this);
778
    }
779
780
    /**
781
     * Sets the iterator classname for the current "Arrayy"-object.
782
     *
783
     * @param string $iteratorClass
784
     *
785
     * @throws \InvalidArgumentException
786
     *
787
     * @return void
788
     *
789
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
790
     */
791 1189
    public function setIteratorClass($iteratorClass)
792
    {
793 1189
        if (\class_exists($iteratorClass)) {
794 1189
            $this->iteratorClass = $iteratorClass;
795
796 1189
            return;
797
        }
798
799
        if (\strpos($iteratorClass, '\\') === 0) {
800
            $iteratorClass = '\\' . $iteratorClass;
801
            if (\class_exists($iteratorClass)) {
802
                /**
803
                 * @psalm-suppress PropertyTypeCoercion
804
                 */
805
                $this->iteratorClass = $iteratorClass;
806
807
                return;
808
            }
809
        }
810
811
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
812
    }
813
814
    /**
815
     * Sort the entries with a user-defined comparison function and maintain key association.
816
     *
817
     * @param callable $function
818
     *
819
     * @throws \InvalidArgumentException
820
     *
821
     * @return $this
822
     *               <p>(Mutable) Return this Arrayy object.</p>
823
     *
824
     * @psalm-return static<TKey,T>
825
     */
826 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...
827
    {
828 8
        if (!\is_callable($function)) {
829
            throw new \InvalidArgumentException('Passed function must be callable');
830
        }
831
832 8
        $this->generatorToArray();
833
834 8
        \uasort($this->array, $function);
835
836 8
        return $this;
837
    }
838
839
    /**
840
     * Sort the entries with a user-defined comparison function and maintain key association.
841
     *
842
     * @param callable $function
843
     *
844
     * @throws \InvalidArgumentException
845
     *
846
     * @return $this
847
     *               <p>(Immutable) Return this Arrayy object.</p>
848
     *
849
     * @psalm-return static<TKey,T>
850
     * @psalm-mutation-free
851
     */
852 4
    public function uasortImmutable($function): self
853
    {
854 4
        $that = clone $this;
855
856
        /**
857
         * @psalm-suppress ImpureMethodCall - object is already cloned
858
         */
859 4
        $that->uasort($function);
860
861 4
        return $that;
862
    }
863
864
    /**
865
     * Sort the entries by keys using a user-defined comparison function.
866
     *
867
     * @param callable $function
868
     *
869
     * @throws \InvalidArgumentException
870
     *
871
     * @return static
872
     *                <p>(Mutable) Return this Arrayy object.</p>
873
     *
874
     * @psalm-return static<TKey,T>
875
     */
876 5
    public function uksort($function): self
877
    {
878 5
        return $this->customSortKeys($function);
879
    }
880
881
    /**
882
     * Sort the entries by keys using a user-defined comparison function.
883
     *
884
     * @param callable $function
885
     *
886
     * @throws \InvalidArgumentException
887
     *
888
     * @return static
889
     *                <p>(Immutable) Return this Arrayy object.</p>
890
     *
891
     * @psalm-return static<TKey,T>
892
     * @psalm-mutation-free
893
     */
894 1
    public function uksortImmutable($function): self
895
    {
896 1
        return $this->customSortKeysImmutable($function);
897
    }
898
899
    /**
900
     * Unserialize an string and return the instance of the "Arrayy"-class.
901
     *
902
     * @param string $string
903
     *
904
     * @return $this
905
     *
906
     * @psalm-return static<TKey,T>
907
     */
908 2
    public function unserialize($string): self
909
    {
910 2
        if (\PHP_VERSION_ID < 70400) {
911 2
            parent::unserialize($string);
912
913 2
            return $this;
914
        }
915
916
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
917
    }
918
919
    /**
920
     * Append a (key) + values to the current array.
921
     *
922
     * EXAMPLE: <code>
923
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
924
     * </code>
925
     *
926
     * @param array $values
927
     * @param mixed $key
928
     *
929
     * @return $this
930
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
931
     *
932
     * @psalm-param  array<mixed,T> $values
933
     * @psalm-param  TKey|null $key
934
     * @psalm-return static<TKey,T>
935
     */
936 1
    public function appendArrayValues(array $values, $key = null)
937
    {
938 1
        $this->generatorToArray();
939
940 1
        if ($key !== null) {
941
            if (
942 1
                isset($this->array[$key])
943
                &&
944 1
                \is_array($this->array[$key]) === true
945
            ) {
946 1
                foreach ($values as $value) {
947 1
                    $this->array[$key][] = $value;
948
                }
949
            } else {
950
                foreach ($values as $value) {
951 1
                    $this->array[$key] = $value;
952
                }
953
            }
954
        } else {
955
            foreach ($values as $value) {
956
                $this->array[] = $value;
957
            }
958
        }
959
960 1
        return $this;
961
    }
962
963
    /**
964
     * Add a suffix to each key.
965
     *
966
     * @param mixed $prefix
967
     *
968
     * @return static
969
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
970
     *
971
     * @psalm-return static<TKey,T>
972
     * @psalm-mutation-free
973
     */
974 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...
975
    {
976
        // init
977 10
        $result = [];
978
979 10
        foreach ($this->getGenerator() as $key => $item) {
980 9
            if ($item instanceof self) {
981
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
982 9
            } elseif (\is_array($item) === true) {
983
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
984
                    ->appendToEachKey($prefix)
985
                    ->toArray();
986
            } else {
987 9
                $result[$prefix . $key] = $item;
988
            }
989
        }
990
991 10
        return self::create($result, $this->iteratorClass, false);
992
    }
993
994
    /**
995
     * Add a prefix to each value.
996
     *
997
     * @param mixed $prefix
998
     *
999
     * @return static
1000
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1001
     *
1002
     * @psalm-return static<TKey,T>
1003
     * @psalm-mutation-free
1004
     */
1005 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...
1006
    {
1007
        // init
1008 10
        $result = [];
1009
1010 10
        foreach ($this->getGenerator() as $key => $item) {
1011 9
            if ($item instanceof self) {
1012
                $result[$key] = $item->appendToEachValue($prefix);
1013 9
            } elseif (\is_array($item) === true) {
1014
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1015 9
            } elseif (\is_object($item) === true) {
1016 1
                $result[$key] = $item;
1017
            } else {
1018 9
                $result[$key] = $prefix . $item;
1019
            }
1020
        }
1021
1022 10
        return self::create($result, $this->iteratorClass, false);
1023
    }
1024
1025
    /**
1026
     * Sort an array in reverse order and maintain index association.
1027
     *
1028
     * @return $this
1029
     *               <p>(Mutable) Return this Arrayy object.</p>
1030
     *
1031
     * @psalm-return static<TKey,T>
1032
     */
1033 4
    public function arsort(): self
1034
    {
1035 4
        $this->generatorToArray();
1036
1037 4
        \arsort($this->array);
1038
1039 4
        return $this;
1040
    }
1041
1042
    /**
1043
     * Sort an array in reverse order and maintain index association.
1044
     *
1045
     * @return $this
1046
     *               <p>(Immutable) Return this Arrayy object.</p>
1047
     *
1048
     * @psalm-return static<TKey,T>
1049
     * @psalm-mutation-free
1050
     */
1051 10
    public function arsortImmutable(): self
1052
    {
1053 10
        $that = clone $this;
1054
1055 10
        $that->generatorToArray();
1056
1057 10
        \arsort($that->array);
1058
1059 10
        return $that;
1060
    }
1061
1062
    /**
1063
     * Iterate over the current array and execute a callback for each loop.
1064
     *
1065
     * EXAMPLE: <code>
1066
     * $result = A::create();
1067
     * $closure = function ($value, $key) use ($result) {
1068
     *     $result[$key] = ':' . $value . ':';
1069
     * };
1070
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1071
     * </code>
1072
     *
1073
     * @param \Closure $closure
1074
     *
1075
     * @return static
1076
     *                <p>(Immutable)</p>
1077
     *
1078
     * @psalm-return static<TKey,T>
1079
     * @psalm-mutation-free
1080
     */
1081 3
    public function at(\Closure $closure): self
1082
    {
1083 3
        $that = clone $this;
1084
1085 3
        foreach ($that->getGenerator() as $key => $value) {
1086 3
            $closure($value, $key);
1087
        }
1088
1089 3
        return static::create(
1090 3
            $that->toArray(),
1091 3
            $this->iteratorClass,
1092 3
            false
1093
        );
1094
    }
1095
1096
    /**
1097
     * Returns the average value of the current array.
1098
     *
1099
     * EXAMPLE: <code>
1100
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1101
     * </code>
1102
     *
1103
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1104
     *
1105
     * @return float|int
1106
     *                   <p>The average value.</p>
1107
     * @psalm-mutation-free
1108
     */
1109 10
    public function average($decimals = 0)
1110
    {
1111 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1112
1113 10
        if (!$count) {
1114 2
            return 0;
1115
        }
1116
1117 8
        if ((int) $decimals !== $decimals) {
1118 3
            $decimals = 0;
1119
        }
1120
1121 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1122
    }
1123
1124
    /**
1125
     * Changes all keys in an array.
1126
     *
1127
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1128
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1129
     *
1130
     * @return static
1131
     *                <p>(Immutable)</p>
1132
     *
1133
     * @psalm-return static<TKey,T>
1134
     * @psalm-mutation-free
1135
     */
1136 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1137
    {
1138
        if (
1139 1
            $case !== \CASE_LOWER
1140
            &&
1141 1
            $case !== \CASE_UPPER
1142
        ) {
1143
            $case = \CASE_LOWER;
1144
        }
1145
1146 1
        $return = [];
1147 1
        foreach ($this->getGenerator() as $key => $value) {
1148 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1149
1150 1
            if ($case === \CASE_LOWER) {
1151 1
                $key = \mb_strtolower((string) $key);
1152
            } else {
1153 1
                $key = \mb_strtoupper((string) $key);
1154
            }
1155
1156 1
            $return[$key] = $value;
1157
        }
1158
1159 1
        return static::create(
1160 1
            $return,
1161 1
            $this->iteratorClass,
1162 1
            false
1163
        );
1164
    }
1165
1166
    /**
1167
     * Change the path separator of the array wrapper.
1168
     *
1169
     * By default, the separator is: "."
1170
     *
1171
     * @param string $separator <p>Separator to set.</p>
1172
     *
1173
     * @return $this
1174
     *               <p>(Mutable) Return this Arrayy object.</p>
1175
     *
1176
     * @psalm-return static<TKey,T>
1177
     */
1178 11
    public function changeSeparator($separator): self
1179
    {
1180 11
        $this->pathSeparator = $separator;
1181
1182 11
        return $this;
1183
    }
1184
1185
    /**
1186
     * Create a chunked version of the current array.
1187
     *
1188
     * EXAMPLE: <code>
1189
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1190
     * </code>
1191
     *
1192
     * @param int  $size         <p>Size of each chunk.</p>
1193
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1194
     *
1195
     * @return static
1196
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1197
     *
1198
     * @psalm-return static<TKey,T>
1199
     * @psalm-mutation-free
1200
     */
1201 5
    public function chunk($size, $preserveKeys = false): self
1202
    {
1203 5
        return static::create(
1204 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1205 5
            $this->iteratorClass,
1206 5
            false
1207
        );
1208
    }
1209
1210
    /**
1211
     * Clean all falsy values from the current array.
1212
     *
1213
     * EXAMPLE: <code>
1214
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1215
     * </code>
1216
     *
1217
     * @return static
1218
     *                <p>(Immutable)</p>
1219
     *
1220
     * @psalm-return static<TKey,T>
1221
     * @psalm-mutation-free
1222
     */
1223 8
    public function clean(): self
1224
    {
1225 8
        return $this->filter(
1226 8
            static function ($value) {
1227 7
                return (bool) $value;
1228 8
            }
1229
        );
1230
    }
1231
1232
    /**
1233
     * WARNING!!! -> Clear the current full array or a $key of it.
1234
     *
1235
     * EXAMPLE: <code>
1236
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1237
     * </code>
1238
     *
1239
     * @param int|int[]|string|string[]|null $key
1240
     *
1241
     * @return $this
1242
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1243
     *
1244
     * @psalm-return static<TKey,T>
1245
     */
1246 10
    public function clear($key = null): self
1247
    {
1248 10
        if ($key !== null) {
1249 3
            if (\is_array($key)) {
1250 1
                foreach ($key as $keyTmp) {
1251 1
                    $this->offsetUnset($keyTmp);
1252
                }
1253
            } else {
1254 2
                $this->offsetUnset($key);
1255
            }
1256
1257 3
            return $this;
1258
        }
1259
1260 7
        $this->array = [];
1261 7
        $this->generator = null;
1262
1263 7
        return $this;
1264
    }
1265
1266
    /**
1267
     * Check if an item is in the current array.
1268
     *
1269
     * EXAMPLE: <code>
1270
     * a([1, true])->contains(true); // true
1271
     * </code>
1272
     *
1273
     * @param float|int|string $value
1274
     * @param bool             $recursive
1275
     * @param bool             $strict
1276
     *
1277
     * @return bool
1278
     * @psalm-mutation-free
1279
     */
1280 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1281
    {
1282 23
        if ($recursive === true) {
1283 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1284
        }
1285
1286
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1287 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1288 11
            if ($strict) {
1289 11
                if ($value === $valueFromArray) {
1290 11
                    return true;
1291
                }
1292
            } else {
1293
                /** @noinspection NestedPositiveIfStatementsInspection */
1294
                if ($value == $valueFromArray) {
1295 7
                    return true;
1296
                }
1297
            }
1298
        }
1299
1300 7
        return false;
1301
    }
1302
1303
    /**
1304
     * Check if an (case-insensitive) string is in the current array.
1305
     *
1306
     * EXAMPLE: <code>
1307
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1308
     * </code>
1309
     *
1310
     * @param mixed $value
1311
     * @param bool  $recursive
1312
     *
1313
     * @return bool
1314
     * @psalm-mutation-free
1315
     *
1316
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1317
     */
1318 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1319
    {
1320 26
        if ($value === null) {
1321 2
            return false;
1322
        }
1323
1324 24
        if ($recursive === true) {
1325
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1326 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1327 22
                if (\is_array($valueTmp) === true) {
1328 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1329 5
                    if ($return === true) {
1330 5
                        return $return;
1331
                    }
1332 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1333 22
                    return true;
1334
                }
1335
            }
1336
1337 8
            return false;
1338
        }
1339
1340
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1341 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1342 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1343 11
                return true;
1344
            }
1345
        }
1346
1347 4
        return false;
1348
    }
1349
1350
    /**
1351
     * Check if the given key/index exists in the array.
1352
     *
1353
     * EXAMPLE: <code>
1354
     * a([1 => true])->containsKey(1); // true
1355
     * </code>
1356
     *
1357
     * @param int|string $key <p>key/index to search for</p>
1358
     *
1359
     * @return bool
1360
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1361
     *
1362
     * @psalm-mutation-free
1363
     */
1364 4
    public function containsKey($key): bool
1365
    {
1366 4
        return $this->offsetExists($key);
1367
    }
1368
1369
    /**
1370
     * Check if all given needles are present in the array as key/index.
1371
     *
1372
     * EXAMPLE: <code>
1373
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1374
     * </code>
1375
     *
1376
     * @param array $needles   <p>The keys you are searching for.</p>
1377
     * @param bool  $recursive
1378
     *
1379
     * @return bool
1380
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1381
     *
1382
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1383
     * @psalm-mutation-free
1384
     */
1385 2
    public function containsKeys(array $needles, $recursive = false): bool
1386
    {
1387 2
        if ($recursive === true) {
1388
            return
1389 2
                \count(
1390 2
                    \array_intersect(
1391 2
                        $needles,
1392 2
                        $this->keys(true)->toArray()
1393
                    ),
1394 2
                    \COUNT_RECURSIVE
1395
                )
1396
                ===
1397 2
                \count(
1398 2
                    $needles,
1399 2
                    \COUNT_RECURSIVE
1400
                );
1401
        }
1402
1403 1
        return \count(
1404 1
            \array_intersect($needles, $this->keys()->toArray()),
1405 1
            \COUNT_NORMAL
1406
        )
1407
               ===
1408 1
               \count(
1409 1
                   $needles,
1410 1
                   \COUNT_NORMAL
1411
               );
1412
    }
1413
1414
    /**
1415
     * Check if all given needles are present in the array as key/index.
1416
     *
1417
     * @param array $needles <p>The keys you are searching for.</p>
1418
     *
1419
     * @return bool
1420
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1421
     *
1422
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1423
     * @psalm-mutation-free
1424
     */
1425 1
    public function containsKeysRecursive(array $needles): bool
1426
    {
1427 1
        return $this->containsKeys($needles, true);
1428
    }
1429
1430
    /**
1431
     * alias: for "Arrayy->contains()"
1432
     *
1433
     * @param float|int|string $value
1434
     *
1435
     * @return bool
1436
     *
1437
     * @see Arrayy::contains()
1438
     * @psalm-mutation-free
1439
     */
1440 9
    public function containsValue($value): bool
1441
    {
1442 9
        return $this->contains($value);
1443
    }
1444
1445
    /**
1446
     * alias: for "Arrayy->contains($value, true)"
1447
     *
1448
     * @param float|int|string $value
1449
     *
1450
     * @return bool
1451
     *
1452
     * @see Arrayy::contains()
1453
     * @psalm-mutation-free
1454
     */
1455 18
    public function containsValueRecursive($value): bool
1456
    {
1457 18
        return $this->contains($value, true);
1458
    }
1459
1460
    /**
1461
     * Check if all given needles are present in the array.
1462
     *
1463
     * EXAMPLE: <code>
1464
     * a([1, true])->containsValues(array(1, true)); // true
1465
     * </code>
1466
     *
1467
     * @param array $needles
1468
     *
1469
     * @return bool
1470
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1471
     *
1472
     * @psalm-param array<mixed>|array<T> $needles
1473
     * @psalm-mutation-free
1474
     */
1475 1
    public function containsValues(array $needles): bool
1476
    {
1477 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1478
               ===
1479 1
               \count($needles, \COUNT_NORMAL);
1480
    }
1481
1482
    /**
1483
     * Counts all the values of an array
1484
     *
1485
     * @see          http://php.net/manual/en/function.array-count-values.php
1486
     *
1487
     * @return static
1488
     *                <p>
1489
     *                (Immutable)
1490
     *                An associative Arrayy-object of values from input as
1491
     *                keys and their count as value.
1492
     *                </p>
1493
     *
1494
     * @psalm-return static<TKey,T>
1495
     * @psalm-mutation-free
1496
     */
1497 7
    public function countValues(): self
1498
    {
1499 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1500
    }
1501
1502
    /**
1503
     * Creates an Arrayy object.
1504
     *
1505
     * @param mixed  $data
1506
     * @param string $iteratorClass
1507
     * @param bool   $checkPropertiesInConstructor
1508
     *
1509
     * @return static
1510
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1511
     *
1512
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1513
     *
1514
     * @psalm-mutation-free
1515
     */
1516 717
    public static function create(
1517
        $data = [],
1518
        string $iteratorClass = ArrayyIterator::class,
1519
        bool $checkPropertiesInConstructor = true
1520
    ) {
1521 717
        return new static(
1522 717
            $data,
1523 717
            $iteratorClass,
1524 717
            $checkPropertiesInConstructor
1525
        );
1526
    }
1527
1528
    /**
1529
     * Flatten an array with the given character as a key delimiter
1530
     *
1531
     * @param string     $delimiter
1532
     * @param string     $prepend
1533
     * @param array|null $items
1534
     *
1535
     * @return array
1536
     */
1537 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1538
    {
1539
        // init
1540 2
        $flatten = [];
1541
1542 2
        if ($items === null) {
1543 2
            $items = $this->array;
1544
        }
1545
1546 2
        foreach ($items as $key => $value) {
1547 2
            if (\is_array($value) && !empty($value)) {
1548 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1549
            } else {
1550 2
                $flatten[] = [$prepend . $key => $value];
1551
            }
1552
        }
1553
1554 2
        if (\count($flatten) === 0) {
1555
            return [];
1556
        }
1557
1558 2
        return \array_merge_recursive([], ...$flatten);
1559
    }
1560
1561
    /**
1562
     * WARNING: Creates an Arrayy object by reference.
1563
     *
1564
     * @param array $array
1565
     *
1566
     * @return $this
1567
     *               <p>(Mutable) Return this Arrayy object.</p>
1568
     *
1569
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1570
     */
1571 27
    public function createByReference(array &$array = []): self
1572
    {
1573 27
        $array = $this->fallbackForArray($array);
1574
1575 27
        $this->array = &$array;
1576
1577 27
        return $this;
1578
    }
1579
1580
    /**
1581
     * Create an new instance from a callable function which will return an Generator.
1582
     *
1583
     * @param callable $generatorFunction
1584
     *
1585
     * @return static
1586
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1587
     *
1588
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1589
     *
1590
     * @psalm-mutation-free
1591
     */
1592 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1593
    {
1594 7
        return self::create($generatorFunction);
1595
    }
1596
1597
    /**
1598
     * Create an new instance filled with a copy of values from a "Generator"-object.
1599
     *
1600
     * @param \Generator $generator
1601
     *
1602
     * @return static
1603
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1604
     *
1605
     * @psalm-param \Generator<array-key,mixed> $generator
1606
     *
1607
     * @psalm-mutation-free
1608
     */
1609 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1610
    {
1611 4
        return self::create(\iterator_to_array($generator, true));
1612
    }
1613
1614
    /**
1615
     * Create an new Arrayy object via JSON.
1616
     *
1617
     * @param string $json
1618
     *
1619
     * @return static
1620
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1621
     *
1622
     * @psalm-mutation-free
1623
     */
1624 5
    public static function createFromJson(string $json): self
1625
    {
1626 5
        return static::create(\json_decode($json, true));
1627
    }
1628
1629
    /**
1630
     * Create an new Arrayy object via JSON.
1631
     *
1632
     * @param array $array
1633
     *
1634
     * @return static
1635
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1636
     *
1637
     * @psalm-mutation-free
1638
     */
1639 1
    public static function createFromArray(array $array): self
1640
    {
1641 1
        return static::create($array);
1642
    }
1643
1644
    /**
1645
     * Create an new instance filled with values from an object that is iterable.
1646
     *
1647
     * @param \Traversable $object <p>iterable object</p>
1648
     *
1649
     * @return static
1650
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1651
     *
1652
     * @psalm-param \Traversable<array-key,mixed> $object
1653
     *
1654
     * @psalm-mutation-free
1655
     */
1656 4
    public static function createFromObject(\Traversable $object): self
1657
    {
1658
        // init
1659 4
        $arrayy = new static();
1660
1661 4
        if ($object instanceof self) {
1662 4
            $objectArray = $object->getGenerator();
1663
        } else {
1664
            $objectArray = $object;
1665
        }
1666
1667 4
        foreach ($objectArray as $key => $value) {
1668
            /**
1669
             * @psalm-suppress ImpureMethodCall - object is already re-created
1670
             */
1671 3
            $arrayy->internalSet($key, $value);
1672
        }
1673
1674 4
        return $arrayy;
1675
    }
1676
1677
    /**
1678
     * Create an new instance filled with values from an object.
1679
     *
1680
     * @param object $object
1681
     *
1682
     * @return static
1683
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1684
     *
1685
     * @psalm-mutation-free
1686
     */
1687 5
    public static function createFromObjectVars($object): self
1688
    {
1689 5
        return self::create(self::objectToArray($object));
1690
    }
1691
1692
    /**
1693
     * Create an new Arrayy object via string.
1694
     *
1695
     * @param string      $str       <p>The input string.</p>
1696
     * @param string|null $delimiter <p>The boundary string.</p>
1697
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1698
     *                               used.</p>
1699
     *
1700
     * @return static
1701
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1702
     *
1703
     * @psalm-mutation-free
1704
     */
1705 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1706
    {
1707 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...
1708 1
            \preg_match_all($regEx, $str, $array);
1709
1710 1
            if (!empty($array)) {
1711 1
                $array = $array[0];
1712
            }
1713
        } else {
1714
            /** @noinspection NestedPositiveIfStatementsInspection */
1715 9
            if ($delimiter !== null) {
1716 7
                $array = \explode($delimiter, $str);
1717
            } else {
1718 2
                $array = [$str];
1719
            }
1720
        }
1721
1722
        // trim all string in the array
1723
        /**
1724
         * @psalm-suppress MissingClosureParamType
1725
         */
1726 10
        \array_walk(
1727 10
            $array,
1728 10
            static function (&$val) {
1729 10
                if ((string) $val === $val) {
1730 10
                    $val = \trim($val);
1731
                }
1732 10
            }
1733
        );
1734
1735 10
        return static::create($array);
1736
    }
1737
1738
    /**
1739
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1740
     *
1741
     * @param \Traversable $traversable
1742
     *
1743
     * @return static
1744
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1745
     *
1746
     * @psalm-param \Traversable<array-key,mixed> $traversable
1747
     *
1748
     * @psalm-mutation-free
1749
     */
1750 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1751
    {
1752 1
        return self::create(\iterator_to_array($traversable, true));
1753
    }
1754
1755
    /**
1756
     * Create an new instance containing a range of elements.
1757
     *
1758
     * @param float|int|string $low  <p>First value of the sequence.</p>
1759
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1760
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1761
     *
1762
     * @return static
1763
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1764
     *
1765
     * @psalm-mutation-free
1766
     */
1767 2
    public static function createWithRange($low, $high, $step = 1): self
1768
    {
1769 2
        return static::create(\range($low, $high, $step));
1770
    }
1771
1772
    /**
1773
     * Gets the element of the array at the current internal iterator position.
1774
     *
1775
     * @return false|mixed
1776
     */
1777
    public function current()
1778
    {
1779
        return \current($this->array);
1780
    }
1781
1782
    /**
1783
     * Custom sort by index via "uksort".
1784
     *
1785
     * EXAMPLE: <code>
1786
     * $callable = function ($a, $b) {
1787
     *     if ($a == $b) {
1788
     *         return 0;
1789
     *     }
1790
     *     return ($a > $b) ? 1 : -1;
1791
     * };
1792
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1793
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1794
     * </code>
1795
     *
1796
     * @see          http://php.net/manual/en/function.uksort.php
1797
     *
1798
     * @param callable $function
1799
     *
1800
     * @throws \InvalidArgumentException
1801
     *
1802
     * @return $this
1803
     *               <p>(Mutable) Return this Arrayy object.</p>
1804
     *
1805
     * @psalm-return static<TKey,T>
1806
     */
1807 5
    public function customSortKeys(callable $function): self
1808
    {
1809 5
        $this->generatorToArray();
1810
1811 5
        \uksort($this->array, $function);
1812
1813 5
        return $this;
1814
    }
1815
1816
    /**
1817
     * Custom sort by index via "uksort".
1818
     *
1819
     * @see          http://php.net/manual/en/function.uksort.php
1820
     *
1821
     * @param callable $function
1822
     *
1823
     * @throws \InvalidArgumentException
1824
     *
1825
     * @return $this
1826
     *               <p>(Immutable) Return this Arrayy object.</p>
1827
     *
1828
     * @psalm-return static<TKey,T>
1829
     * @psalm-mutation-free
1830
     */
1831 1
    public function customSortKeysImmutable(callable $function): self
1832
    {
1833 1
        $that = clone $this;
1834
1835 1
        $that->generatorToArray();
1836
1837
        /**
1838
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1839
         */
1840 1
        \uksort($that->array, $function);
1841
1842 1
        return $that;
1843
    }
1844
1845
    /**
1846
     * Custom sort by value via "usort".
1847
     *
1848
     * EXAMPLE: <code>
1849
     * $callable = function ($a, $b) {
1850
     *     if ($a == $b) {
1851
     *         return 0;
1852
     *     }
1853
     *     return ($a > $b) ? 1 : -1;
1854
     * };
1855
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1856
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
1857
     * </code>
1858
     *
1859
     * @see          http://php.net/manual/en/function.usort.php
1860
     *
1861
     * @param callable $function
1862
     *
1863
     * @throws \InvalidArgumentException
1864
     *
1865
     * @return $this
1866
     *               <p>(Mutable) Return this Arrayy object.</p>
1867
     *
1868
     * @psalm-return static<TKey,T>
1869
     */
1870 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...
1871
    {
1872 10
        if (\is_callable($function) === false) {
1873
            throw new \InvalidArgumentException('Passed function must be callable');
1874
        }
1875
1876 10
        $this->generatorToArray();
1877
1878 10
        \usort($this->array, $function);
1879
1880 10
        return $this;
1881
    }
1882
1883
    /**
1884
     * Custom sort by value via "usort".
1885
     *
1886
     * @see          http://php.net/manual/en/function.usort.php
1887
     *
1888
     * @param callable $function
1889
     *
1890
     * @throws \InvalidArgumentException
1891
     *
1892
     * @return $this
1893
     *               <p>(Immutable) Return this Arrayy object.</p>
1894
     *
1895
     * @psalm-return static<TKey,T>
1896
     * @psalm-mutation-free
1897
     */
1898 4
    public function customSortValuesImmutable($function): self
1899
    {
1900 4
        $that = clone $this;
1901
1902
        /**
1903
         * @psalm-suppress ImpureMethodCall - object is already cloned
1904
         */
1905 4
        $that->customSortValues($function);
1906
1907 4
        return $that;
1908
    }
1909
1910
    /**
1911
     * Delete the given key or keys.
1912
     *
1913
     * @param int|int[]|string|string[] $keyOrKeys
1914
     *
1915
     * @return void
1916
     */
1917 9
    public function delete($keyOrKeys)
1918
    {
1919 9
        $keyOrKeys = (array) $keyOrKeys;
1920
1921 9
        foreach ($keyOrKeys as $key) {
1922 9
            $this->offsetUnset($key);
1923
        }
1924 9
    }
1925
1926
    /**
1927
     * Return values that are only in the current array.
1928
     *
1929
     * EXAMPLE: <code>
1930
     * a([1 => 1, 2 => 2])->diff([1 => 1]); // Arrayy[2 => 2]
1931
     * </code>
1932
     *
1933
     * @param array ...$array
1934
     *
1935
     * @return static
1936
     *                <p>(Immutable)</p>
1937
     *
1938
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1939
     * @psalm-return static<TKey,T>
1940
     * @psalm-mutation-free
1941
     */
1942 13
    public function diff(...$array): self
1943
    {
1944 13
        return static::create(
1945 13
            \array_diff($this->toArray(), ...$array),
1946 13
            $this->iteratorClass,
1947 13
            false
1948
        );
1949
    }
1950
1951
    /**
1952
     * Return values that are only in the current array.
1953
     *
1954
     * @param array ...$array
1955
     *
1956
     * @return static
1957
     *                <p>(Immutable)</p>
1958
     *
1959
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1960
     * @psalm-return static<TKey,T>
1961
     * @psalm-mutation-free
1962
     */
1963 8
    public function diffKey(...$array): self
1964
    {
1965 8
        return static::create(
1966 8
            \array_diff_key($this->toArray(), ...$array),
1967 8
            $this->iteratorClass,
1968 8
            false
1969
        );
1970
    }
1971
1972
    /**
1973
     * Return values and Keys that are only in the current array.
1974
     *
1975
     * @param array $array
1976
     *
1977
     * @return static
1978
     *                <p>(Immutable)</p>
1979
     *
1980
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1981
     * @psalm-return static<TKey,T>
1982
     * @psalm-mutation-free
1983
     */
1984 8
    public function diffKeyAndValue(array $array = []): self
1985
    {
1986 8
        return static::create(
1987 8
            \array_diff_assoc($this->toArray(), $array),
1988 8
            $this->iteratorClass,
1989 8
            false
1990
        );
1991
    }
1992
1993
    /**
1994
     * Return values that are only in the current multi-dimensional array.
1995
     *
1996
     * @param array                 $array
1997
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
1998
     *
1999
     * @return static
2000
     *                <p>(Immutable)</p>
2001
     *
2002
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2003
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2004
     * @psalm-return static<TKey,T>
2005
     * @psalm-mutation-free
2006
     */
2007 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2008
    {
2009
        // init
2010 1
        $result = [];
2011
2012
        if (
2013 1
            $helperVariableForRecursion !== null
2014
            &&
2015 1
            \is_array($helperVariableForRecursion) === true
2016
        ) {
2017
            $arrayForTheLoop = $helperVariableForRecursion;
2018
        } else {
2019 1
            $arrayForTheLoop = $this->getGenerator();
2020
        }
2021
2022 1
        foreach ($arrayForTheLoop as $key => $value) {
2023 1
            if ($value instanceof self) {
2024
                $value = $value->toArray();
2025
            }
2026
2027 1
            if (\array_key_exists($key, $array)) {
2028 1
                if ($value !== $array[$key]) {
2029 1
                    $result[$key] = $value;
2030
                }
2031
            } else {
2032 1
                $result[$key] = $value;
2033
            }
2034
        }
2035
2036 1
        return static::create(
2037 1
            $result,
2038 1
            $this->iteratorClass,
2039 1
            false
2040
        );
2041
    }
2042
2043
    /**
2044
     * Return values that are only in the new $array.
2045
     *
2046
     * @param array $array
2047
     *
2048
     * @return static
2049
     *                <p>(Immutable)</p>
2050
     *
2051
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2052
     * @psalm-return static<TKey,T>
2053
     * @psalm-mutation-free
2054
     */
2055 8
    public function diffReverse(array $array = []): self
2056
    {
2057 8
        return static::create(
2058 8
            \array_diff($array, $this->toArray()),
2059 8
            $this->iteratorClass,
2060 8
            false
2061
        );
2062
    }
2063
2064
    /**
2065
     * Divide an array into two arrays. One with keys and the other with values.
2066
     *
2067
     * @return static
2068
     *                <p>(Immutable)</p>
2069
     *
2070
     * @psalm-return static<TKey,T>
2071
     * @psalm-mutation-free
2072
     */
2073 1
    public function divide(): self
2074
    {
2075 1
        return static::create(
2076
            [
2077 1
                $this->keys(),
2078 1
                $this->values(),
2079
            ],
2080 1
            $this->iteratorClass,
2081 1
            false
2082
        );
2083
    }
2084
2085
    /**
2086
     * Iterate over the current array and modify the array's value.
2087
     *
2088
     * @param \Closure $closure
2089
     *
2090
     * @return static
2091
     *                <p>(Immutable)</p>
2092
     *
2093
     * @psalm-return static<TKey,T>
2094
     * @psalm-mutation-free
2095
     */
2096 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...
2097
    {
2098
        // init
2099 5
        $array = [];
2100
2101 5
        foreach ($this->getGenerator() as $key => $value) {
2102 5
            $array[$key] = $closure($value, $key);
2103
        }
2104
2105 5
        return static::create(
2106 5
            $array,
2107 5
            $this->iteratorClass,
2108 5
            false
2109
        );
2110
    }
2111
2112
    /**
2113
     * Sets the internal iterator to the last element in the array and returns this element.
2114
     *
2115
     * @return mixed
2116
     */
2117
    public function end()
2118
    {
2119
        return \end($this->array);
2120
    }
2121
2122
    /**
2123
     * Check if a value is in the current array using a closure.
2124
     *
2125
     * @param \Closure $closure
2126
     *
2127
     * @return bool
2128
     *              <p>Returns true if the given value is found, false otherwise.</p>
2129
     */
2130 4
    public function exists(\Closure $closure): bool
2131
    {
2132
        // init
2133 4
        $isExists = false;
2134
2135 4
        foreach ($this->getGenerator() as $key => $value) {
2136 3
            if ($closure($value, $key)) {
2137 1
                $isExists = true;
2138
2139 3
                break;
2140
            }
2141
        }
2142
2143 4
        return $isExists;
2144
    }
2145
2146
    /**
2147
     * Fill the array until "$num" with "$default" values.
2148
     *
2149
     * @param int   $num
2150
     * @param mixed $default
2151
     *
2152
     * @return static
2153
     *                <p>(Immutable)</p>
2154
     *
2155
     * @psalm-return static<TKey,T>
2156
     * @psalm-mutation-free
2157
     */
2158 8
    public function fillWithDefaults(int $num, $default = null): self
2159
    {
2160 8
        if ($num < 0) {
2161 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2162
        }
2163
2164 7
        $this->generatorToArray();
2165
2166 7
        $tmpArray = $this->array;
2167
2168 7
        $count = \count($tmpArray);
2169
2170 7
        while ($count < $num) {
2171 4
            $tmpArray[] = $default;
2172 4
            ++$count;
2173
        }
2174
2175 7
        return static::create(
2176 7
            $tmpArray,
2177 7
            $this->iteratorClass,
2178 7
            false
2179
        );
2180
    }
2181
2182
    /**
2183
     * Find all items in an array that pass the truth test.
2184
     *
2185
     * @param \Closure|null $closure [optional] <p>
2186
     *                               The callback function to use
2187
     *                               </p>
2188
     *                               <p>
2189
     *                               If no callback is supplied, all entries of
2190
     *                               input equal to false (see
2191
     *                               converting to
2192
     *                               boolean) will be removed.
2193
     *                               </p>
2194
     * @param int           $flag    [optional] <p>
2195
     *                               Flag determining what arguments are sent to <i>callback</i>:
2196
     *                               </p><ul>
2197
     *                               <li>
2198
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
2199
     *                               to <i>callback</i> instead of the value</span>
2200
     *                               </li>
2201
     *                               <li>
2202
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
2203
     *                               arguments to <i>callback</i> instead of the value</span>
2204
     *                               </li>
2205
     *                               </ul>
2206
     *
2207
     * @return static
2208
     *                <p>(Immutable)</p>
2209
     *
2210
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
2211
     * @psalm-return static<TKey,T>
2212
     * @psalm-mutation-free
2213
     */
2214 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2215
    {
2216 12
        if (!$closure) {
2217 1
            return $this->clean();
2218
        }
2219
2220 12
        return static::create(
2221 12
            \array_filter($this->toArray(), $closure, $flag),
2222 12
            $this->iteratorClass,
2223 12
            false
2224
        );
2225
    }
2226
2227
    /**
2228
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2229
     * property within that.
2230
     *
2231
     * @param string          $property
2232
     * @param string|string[] $value
2233
     * @param string          $comparisonOp
2234
     *                                      <p>
2235
     *                                      'eq' (equals),<br />
2236
     *                                      'gt' (greater),<br />
2237
     *                                      'gte' || 'ge' (greater or equals),<br />
2238
     *                                      'lt' (less),<br />
2239
     *                                      'lte' || 'le' (less or equals),<br />
2240
     *                                      'ne' (not equals),<br />
2241
     *                                      'contains',<br />
2242
     *                                      'notContains',<br />
2243
     *                                      'newer' (via strtotime),<br />
2244
     *                                      'older' (via strtotime),<br />
2245
     *                                      </p>
2246
     *
2247
     * @return static
2248
     *                <p>(Immutable)</p>
2249
     *
2250
     * @psalm-return static<TKey,T>
2251
     * @psalm-mutation-free
2252
     *
2253
     * @psalm-suppress MissingClosureReturnType
2254
     * @psalm-suppress MissingClosureParamType
2255
     */
2256 1
    public function filterBy(
2257
        string $property,
2258
        $value,
2259
        string $comparisonOp = null
2260
    ): self {
2261 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...
2262 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2263
        }
2264
2265
        $ops = [
2266 1
            'eq' => static function ($item, $prop, $value): bool {
2267 1
                return $item[$prop] === $value;
2268 1
            },
2269 1
            'gt' => static function ($item, $prop, $value): bool {
2270
                return $item[$prop] > $value;
2271 1
            },
2272 1
            'ge' => static function ($item, $prop, $value): bool {
2273
                return $item[$prop] >= $value;
2274 1
            },
2275 1
            'gte' => static function ($item, $prop, $value): bool {
2276
                return $item[$prop] >= $value;
2277 1
            },
2278 1
            'lt' => static function ($item, $prop, $value): bool {
2279 1
                return $item[$prop] < $value;
2280 1
            },
2281 1
            'le' => static function ($item, $prop, $value): bool {
2282
                return $item[$prop] <= $value;
2283 1
            },
2284 1
            'lte' => static function ($item, $prop, $value): bool {
2285
                return $item[$prop] <= $value;
2286 1
            },
2287 1
            'ne' => static function ($item, $prop, $value): bool {
2288
                return $item[$prop] !== $value;
2289 1
            },
2290 1
            'contains' => static function ($item, $prop, $value): bool {
2291 1
                return \in_array($item[$prop], (array) $value, true);
2292 1
            },
2293 1
            'notContains' => static function ($item, $prop, $value): bool {
2294
                return !\in_array($item[$prop], (array) $value, true);
2295 1
            },
2296 1
            'newer' => static function ($item, $prop, $value): bool {
2297
                return \strtotime($item[$prop]) > \strtotime($value);
2298 1
            },
2299 1
            'older' => static function ($item, $prop, $value): bool {
2300
                return \strtotime($item[$prop]) < \strtotime($value);
2301 1
            },
2302
        ];
2303
2304 1
        $result = \array_values(
2305 1
            \array_filter(
2306 1
                $this->toArray(false, true),
2307 1
                static function ($item) use (
2308 1
                    $property,
2309 1
                    $value,
2310 1
                    $ops,
2311 1
                    $comparisonOp
2312
                ) {
2313 1
                    $item = (array) $item;
2314 1
                    $itemArrayy = static::create($item);
2315 1
                    $item[$property] = $itemArrayy->get($property, []);
2316
2317 1
                    return $ops[$comparisonOp]($item, $property, $value);
2318 1
                }
2319
            )
2320
        );
2321
2322 1
        return static::create(
2323 1
            $result,
2324 1
            $this->iteratorClass,
2325 1
            false
2326
        );
2327
    }
2328
2329
    /**
2330
     * Find the first item in an array that passes the truth test,
2331
     *  otherwise return false
2332
     *
2333
     * @param \Closure $closure
2334
     *
2335
     * @return false|mixed
2336
     *                     <p>Return false if we did not find the value.</p>
2337
     */
2338 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...
2339
    {
2340 8
        foreach ($this->getGenerator() as $key => $value) {
2341 6
            if ($closure($value, $key)) {
2342 6
                return $value;
2343
            }
2344
        }
2345
2346 3
        return false;
2347
    }
2348
2349
    /**
2350
     * find by ...
2351
     *
2352
     * @param string          $property
2353
     * @param string|string[] $value
2354
     * @param string          $comparisonOp
2355
     *
2356
     * @return static
2357
     *                <p>(Immutable)</p>
2358
     *
2359
     * @psalm-return static<TKey,T>
2360
     * @psalm-mutation-free
2361
     */
2362 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2363
    {
2364 1
        return $this->filterBy($property, $value, $comparisonOp);
2365
    }
2366
2367
    /**
2368
     * Get the first value from the current array.
2369
     *
2370
     * @return mixed
2371
     *               <p>Return null if there wasn't a element.</p>
2372
     */
2373 22
    public function first()
2374
    {
2375 22
        $key_first = $this->firstKey();
2376 22
        if ($key_first === null) {
2377 3
            return null;
2378
        }
2379
2380 19
        return $this->get($key_first);
2381
    }
2382
2383
    /**
2384
     * Get the first key from the current array.
2385
     *
2386
     * @return mixed
2387
     *               <p>Return null if there wasn't a element.</p>
2388
     * @psalm-mutation-free
2389
     */
2390 29
    public function firstKey()
2391
    {
2392 29
        $this->generatorToArray();
2393
2394 29
        return \array_key_first($this->array);
2395
    }
2396
2397
    /**
2398
     * Get the first value(s) from the current array.
2399
     * And will return an empty array if there was no first entry.
2400
     *
2401
     * @param int|null $number <p>How many values you will take?</p>
2402
     *
2403
     * @return static
2404
     *                <p>(Immutable)</p>
2405
     *
2406
     * @psalm-return static<TKey,T>
2407
     * @psalm-mutation-free
2408
     */
2409 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...
2410
    {
2411 37
        $arrayTmp = $this->toArray();
2412
2413 37
        if ($number === null) {
2414 14
            $array = (array) \array_shift($arrayTmp);
2415
        } else {
2416 23
            $array = \array_splice($arrayTmp, 0, $number);
2417
        }
2418
2419 37
        return static::create(
2420 37
            $array,
2421 37
            $this->iteratorClass,
2422 37
            false
2423
        );
2424
    }
2425
2426
    /**
2427
     * Get the first value(s) from the current array.
2428
     * And will return an empty array if there was no first entry.
2429
     *
2430
     * @param int|null $number <p>How many values you will take?</p>
2431
     *
2432
     * @return static
2433
     *                <p>(Immutable)</p>
2434
     *
2435
     * @psalm-return static<TKey,T>
2436
     * @psalm-mutation-free
2437
     */
2438 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...
2439
    {
2440 3
        $arrayTmp = $this->keys()->toArray();
2441
2442 3
        if ($number === null) {
2443
            $array = (array) \array_shift($arrayTmp);
2444
        } else {
2445 3
            $array = \array_splice($arrayTmp, 0, $number);
2446
        }
2447
2448 3
        return static::create(
2449 3
            $array,
2450 3
            $this->iteratorClass,
2451 3
            false
2452
        );
2453
    }
2454
2455
    /**
2456
     * Get and rmove the first value(s) from the current array.
2457
     * And will return an empty array if there was no first entry.
2458
     *
2459
     * @param int|null $number <p>How many values you will take?</p>
2460
     *
2461
     * @return $this
2462
     *               <p>(Mutable)</p>
2463
     *
2464
     * @psalm-return static<TKey,T>
2465
     */
2466 34
    public function firstsMutable(int $number = null): self
2467
    {
2468 34
        $this->generatorToArray();
2469
2470 34
        if ($number === null) {
2471 19
            $this->array = (array) \array_shift($this->array);
2472
        } else {
2473 15
            $this->array = \array_splice($this->array, 0, $number);
2474
        }
2475
2476 34
        return $this;
2477
    }
2478
2479
    /**
2480
     * Exchanges all keys with their associated values in an array.
2481
     *
2482
     * @return static
2483
     *                <p>(Immutable)</p>
2484
     *
2485
     * @psalm-return static<TKey,T>
2486
     * @psalm-mutation-free
2487
     */
2488 1
    public function flip(): self
2489
    {
2490 1
        return static::create(
2491 1
            \array_flip($this->toArray()),
2492 1
            $this->iteratorClass,
2493 1
            false
2494
        );
2495
    }
2496
2497
    /**
2498
     * Get a value from an array (optional using dot-notation).
2499
     *
2500
     * @param mixed $key            <p>The key to look for.</p>
2501
     * @param mixed $fallback       <p>Value to fallback to.</p>
2502
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2503
     *                              class.</p>
2504
     * @param bool  $useByReference
2505
     *
2506
     * @return mixed|static
2507
     *
2508
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2509
     * @psalm-mutation-free
2510
     */
2511 242
    public function get(
2512
        $key,
2513
        $fallback = null,
2514
        array $array = null,
2515
        bool $useByReference = false
2516
    ) {
2517 242
        if ($array !== null) {
2518 4
            if ($useByReference) {
2519
                $usedArray = &$array;
2520
            } else {
2521 4
                $usedArray = $array;
2522
            }
2523
        } else {
2524 239
            $this->generatorToArray();
2525
2526 239
            if ($useByReference) {
2527 126
                $usedArray = &$this->array;
2528
            } else {
2529 131
                $usedArray = $this->array;
2530
            }
2531
        }
2532
2533 242
        if ($key === null) {
2534 1
            return static::create(
2535 1
                [],
2536 1
                $this->iteratorClass,
2537 1
                false
2538 1
            )->createByReference($usedArray);
2539
        }
2540
2541
        // php cast "bool"-index into "int"-index
2542 242
        if ((bool) $key === $key) {
2543 3
            $key = (int) $key;
2544
        }
2545
2546 242
        if (\array_key_exists($key, $usedArray) === true) {
2547 204 View Code Duplication
            if (\is_array($usedArray[$key]) === true) {
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...
2548 19
                return static::create(
2549 19
                    [],
2550 19
                    $this->iteratorClass,
2551 19
                    false
2552 19
                )->createByReference($usedArray[$key]);
2553
            }
2554
2555 190
            return $usedArray[$key];
2556
        }
2557
2558
        // crawl through array, get key according to object or not
2559 61
        $usePath = false;
2560
        if (
2561 61
            $this->pathSeparator
2562
            &&
2563 61
            (string) $key === $key
2564
            &&
2565 61
            \strpos($key, $this->pathSeparator) !== false
2566
        ) {
2567 31
            $segments = \explode($this->pathSeparator, (string) $key);
2568 31
            if ($segments !== false) {
2569 31
                $usePath = true;
2570 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2571
2572 31
                foreach ($segments as $segment) {
2573
                    if (
2574
                        (
2575 31
                            \is_array($usedArrayTmp) === true
2576
                            ||
2577 31
                            $usedArrayTmp instanceof \ArrayAccess
2578
                        )
2579
                        &&
2580 31
                        isset($usedArrayTmp[$segment])
2581
                    ) {
2582 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2583
2584 30
                        continue;
2585
                    }
2586
2587
                    if (
2588 14
                        \is_object($usedArrayTmp) === true
2589
                        &&
2590 14
                        \property_exists($usedArrayTmp, $segment)
2591
                    ) {
2592 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2593
2594 1
                        continue;
2595
                    }
2596
2597 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2598 1
                        $segmentsTmp = $segments;
2599 1
                        unset($segmentsTmp[0]);
2600 1
                        $keyTmp = \implode('.', $segmentsTmp);
2601 1
                        $returnTmp = static::create(
2602 1
                            [],
2603 1
                            $this->iteratorClass,
2604 1
                            false
2605
                        );
2606 1
                        foreach ($this->getAll() as $dataTmp) {
2607 1
                            if ($dataTmp instanceof self) {
2608
                                $returnTmp->add($dataTmp->get($keyTmp));
2609
2610
                                continue;
2611
                            }
2612
2613
                            if (
2614
                                (
2615 1
                                    \is_array($dataTmp) === true
2616
                                    ||
2617 1
                                    $dataTmp instanceof \ArrayAccess
2618
                                )
2619
                                &&
2620 1
                                isset($dataTmp[$keyTmp])
2621
                            ) {
2622
                                $returnTmp->add($dataTmp[$keyTmp]);
2623
2624
                                continue;
2625
                            }
2626
2627
                            if (
2628 1
                                \is_object($dataTmp) === true
2629
                                &&
2630 1
                                \property_exists($dataTmp, $keyTmp)
2631
                            ) {
2632 1
                                $returnTmp->add($dataTmp->{$keyTmp});
2633
2634
                                /** @noinspection UnnecessaryContinueInspection */
2635 1
                                continue;
2636
                            }
2637
                        }
2638
2639 1
                        if ($returnTmp->count() > 0) {
2640 1
                            return $returnTmp;
2641
                        }
2642
                    }
2643
2644 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2645
                }
2646
            }
2647
        }
2648
2649 58
        if (isset($usedArrayTmp)) {
2650 28 View Code Duplication
            if (!$usePath && !isset($usedArrayTmp[$key])) {
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...
2651
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2652
            }
2653
2654 28
            if (\is_array($usedArrayTmp) === true) {
2655 6
                return static::create(
2656 6
                    [],
2657 6
                    $this->iteratorClass,
2658 6
                    false
2659 6
                )->createByReference($usedArrayTmp);
2660
            }
2661
2662 28
            return $usedArrayTmp;
2663
2664
        }
2665
2666 30 View Code Duplication
        if (!$usePath && !isset($usedArray[$key])) {
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...
2667 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2668
        }
2669
2670 View Code Duplication
        if (\is_array($usedArray) === true) {
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...
2671
            return static::create(
2672
                [],
2673
                $this->iteratorClass,
2674
                false
2675
            )->createByReference($usedArray);
2676
        }
2677
2678
        return $usedArray;
2679
    }
2680
2681
    /**
2682
     * alias: for "Arrayy->toArray()"
2683
     *
2684
     * @return array
2685
     *
2686
     * @see          Arrayy::getArray()
2687
     *
2688
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2689
     */
2690 15
    public function getAll(): array
2691
    {
2692 15
        return $this->toArray();
2693
    }
2694
2695
    /**
2696
     * Get the current array from the "Arrayy"-object.
2697
     *
2698
     * alias for "toArray()"
2699
     *
2700
     * @param bool $convertAllArrayyElements <p>
2701
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2702
     *                                       </p>
2703
     * @param bool $preserveKeys             <p>
2704
     *                                       e.g.: A generator maybe return the same key more then once,
2705
     *                                       so maybe you will ignore the keys.
2706
     *                                       </p>
2707
     *
2708
     * @return array
2709
     *
2710
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2711
     * @psalm-mutation-free
2712
     *
2713
     * @see Arrayy::toArray()
2714
     */
2715 500
    public function getArray(
2716
        bool $convertAllArrayyElements = false,
2717
        bool $preserveKeys = true
2718
    ): array {
2719 500
        return $this->toArray(
2720 500
            $convertAllArrayyElements,
2721 500
            $preserveKeys
2722
        );
2723
    }
2724
2725
    /**
2726
     * @param string $json
2727
     *
2728
     * @return $this
2729
     */
2730 3
    public static function createFromJsonMapper(string $json)
2731
    {
2732
        // init
2733 3
        $class = static::create();
2734
2735 3
        $jsonObject = \json_decode($json, false);
2736
2737 3
        $mapper = new \Arrayy\Mapper\Json();
2738 View Code Duplication
        $mapper->undefinedPropertyHandler = static function ($object, $key, $jsonValue) use ($class) {
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...
2739
            if ($class->checkPropertiesMismatchInConstructor) {
2740
                throw new \TypeError('Property mismatch - input: ' . \print_r(['key' => $key, 'jsonValue' => $jsonValue], true) . ' for object: ' . \get_class($object));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...' . \get_class($object).

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...
2741
            }
2742
        };
2743
2744 3
        return $mapper->map($jsonObject, $class);
2745
    }
2746
2747
    /**
2748
     * @return array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
0 ignored issues
show
Documentation introduced by
The doc-type array<int|string,TypeChe...nterface>|TypeInterface could not be parsed: Expected "|" or "end of type", but got "<" at position 57. (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...
2749
     *
2750
     * @internal
2751
     */
2752 6
    public function getPhpDocPropertiesFromClass()
2753
    {
2754 6
        if ($this->properties === []) {
2755 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2756
        }
2757
2758 6
        return $this->properties;
2759
    }
2760
2761
    /**
2762
     * Get the current array from the "Arrayy"-object as list.
2763
     *
2764
     * alias for "toList()"
2765
     *
2766
     * @param bool $convertAllArrayyElements <p>
2767
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2768
     *                                       </p>
2769
     *
2770
     * @return array
2771
     *
2772
     * @psalm-return array<int,mixed>|array<int,T>
2773
     * @psalm-mutation-free
2774
     *
2775
     * @see Arrayy::toList()
2776
     */
2777 1
    public function getList(bool $convertAllArrayyElements = false): array
2778
    {
2779 1
        return $this->toList($convertAllArrayyElements);
2780
    }
2781
2782
    /**
2783
     * Returns the values from a single column of the input array, identified by
2784
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2785
     *
2786
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2787
     * array by the values from the $indexKey column in the input array.
2788
     *
2789
     * @param mixed $columnKey
2790
     * @param mixed $indexKey
2791
     *
2792
     * @return static
2793
     *                <p>(Immutable)</p>
2794
     *
2795
     * @psalm-return static<TKey,T>
2796
     * @psalm-mutation-free
2797
     */
2798 1
    public function getColumn($columnKey = null, $indexKey = null): self
2799
    {
2800 1
        return static::create(
2801 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2802 1
            $this->iteratorClass,
2803 1
            false
2804
        );
2805
    }
2806
2807
    /**
2808
     * Get the current array from the "Arrayy"-object as generator by reference.
2809
     *
2810
     * @return \Generator
2811
     *
2812
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2813
     */
2814 75
    public function &getGeneratorByReference(): \Generator
2815
    {
2816 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2817
            // -> false-positive -> see "&" from method
2818
            /** @noinspection YieldFromCanBeUsedInspection */
2819 17
            foreach ($this->generator as $key => $value) {
2820 17
                yield $key => $value;
2821
            }
2822
2823 5
            return;
2824
        }
2825
2826
        // -> false-positive -> see "&$value"
2827
        /** @noinspection YieldFromCanBeUsedInspection */
2828
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
2829 59
        foreach ($this->array as $key => &$value) {
2830 54
            yield $key => $value;
2831
        }
2832 35
    }
2833
2834
    /**
2835
     * Get the current array from the "Arrayy"-object as generator.
2836
     *
2837
     * @return \Generator
2838
     *
2839
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2840
     * @psalm-mutation-free
2841
     */
2842 996
    public function getGenerator(): \Generator
2843
    {
2844 996
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2845 25
            yield from $this->generator;
2846
2847 25
            return;
2848
        }
2849
2850 994
        yield from $this->array;
2851 968
    }
2852
2853
    /**
2854
     * alias: for "Arrayy->keys()"
2855
     *
2856
     * @return static
2857
     *                <p>(Immutable)</p>
2858
     *
2859
     * @see          Arrayy::keys()
2860
     *
2861
     * @psalm-return static<array-key,TKey>
2862
     * @psalm-mutation-free
2863
     */
2864 2
    public function getKeys()
2865
    {
2866 2
        return $this->keys();
2867
    }
2868
2869
    /**
2870
     * Get the current array from the "Arrayy"-object as object.
2871
     *
2872
     * @return \stdClass
2873
     */
2874 4
    public function getObject(): \stdClass
2875
    {
2876 4
        return self::arrayToObject($this->toArray());
2877
    }
2878
2879
    /**
2880
     * alias: for "Arrayy->randomImmutable()"
2881
     *
2882
     * @return static
2883
     *                <p>(Immutable)</p>
2884
     *
2885
     * @see          Arrayy::randomImmutable()
2886
     *
2887
     * @psalm-return static<int|array-key,T>
2888
     */
2889 4
    public function getRandom(): self
2890
    {
2891 4
        return $this->randomImmutable();
2892
    }
2893
2894
    /**
2895
     * alias: for "Arrayy->randomKey()"
2896
     *
2897
     * @return mixed
2898
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2899
     *
2900
     * @see Arrayy::randomKey()
2901
     */
2902 3
    public function getRandomKey()
2903
    {
2904 3
        return $this->randomKey();
2905
    }
2906
2907
    /**
2908
     * alias: for "Arrayy->randomKeys()"
2909
     *
2910
     * @param int $number
2911
     *
2912
     * @return static
2913
     *                <p>(Immutable)</p>
2914
     *
2915
     * @see          Arrayy::randomKeys()
2916
     *
2917
     * @psalm-return static<TKey,T>
2918
     */
2919 8
    public function getRandomKeys(int $number): self
2920
    {
2921 8
        return $this->randomKeys($number);
2922
    }
2923
2924
    /**
2925
     * alias: for "Arrayy->randomValue()"
2926
     *
2927
     * @return mixed
2928
     *               <p>Get a random value or null if there wasn't a value.</p>
2929
     *
2930
     * @see Arrayy::randomValue()
2931
     */
2932 3
    public function getRandomValue()
2933
    {
2934 3
        return $this->randomValue();
2935
    }
2936
2937
    /**
2938
     * alias: for "Arrayy->randomValues()"
2939
     *
2940
     * @param int $number
2941
     *
2942
     * @return static
2943
     *                <p>(Immutable)</p>
2944
     *
2945
     * @see          Arrayy::randomValues()
2946
     *
2947
     * @psalm-return static<TKey,T>
2948
     */
2949 6
    public function getRandomValues(int $number): self
2950
    {
2951 6
        return $this->randomValues($number);
2952
    }
2953
2954
    /**
2955
     * Gets all values.
2956
     *
2957
     * @return static
2958
     *                <p>The values of all elements in this array, in the order they
2959
     *                appear in the array.</p>
2960
     *
2961
     * @psalm-return static<TKey,T>
2962
     */
2963 4
    public function getValues()
2964
    {
2965 4
        $this->generatorToArray(false);
2966
2967 4
        return static::create(
2968 4
            \array_values($this->array),
2969 4
            $this->iteratorClass,
2970 4
            false
2971
        );
2972
    }
2973
2974
    /**
2975
     * Gets all values via Generator.
2976
     *
2977
     * @return \Generator
2978
     *                    <p>The values of all elements in this array, in the order they
2979
     *                    appear in the array as Generator.</p>
2980
     *
2981
     * @psalm-return \Generator<TKey,T>
2982
     */
2983 4
    public function getValuesYield(): \Generator
2984
    {
2985 4
        yield from $this->getGenerator();
2986 4
    }
2987
2988
    /**
2989
     * Group values from a array according to the results of a closure.
2990
     *
2991
     * @param callable|string $grouper  <p>A callable function name.</p>
2992
     * @param bool            $saveKeys
2993
     *
2994
     * @return static
2995
     *                <p>(Immutable)</p>
2996
     *
2997
     * @psalm-return static<TKey,T>
2998
     * @psalm-mutation-free
2999
     */
3000 4
    public function group($grouper, bool $saveKeys = false): self
3001
    {
3002
        // init
3003 4
        $result = [];
3004
3005
        // Iterate over values, group by property/results from closure.
3006 4
        foreach ($this->getGenerator() as $key => $value) {
3007 4
            if (\is_callable($grouper) === true) {
3008 3
                $groupKey = $grouper($value, $key);
3009
            } else {
3010 1
                $groupKey = $this->get($grouper);
3011
            }
3012
3013 4
            $newValue = $this->get($groupKey, null, $result);
3014
3015 4
            if ($groupKey instanceof self) {
3016
                $groupKey = $groupKey->toArray();
3017
            }
3018
3019 4
            if ($newValue instanceof self) {
3020 4
                $newValue = $newValue->toArray();
3021
            }
3022
3023
            // Add to results.
3024 4
            if ($groupKey !== null) {
3025 3
                if ($saveKeys) {
3026 2
                    $result[$groupKey] = $newValue;
3027 2
                    $result[$groupKey][$key] = $value;
3028
                } else {
3029 1
                    $result[$groupKey] = $newValue;
3030 4
                    $result[$groupKey][] = $value;
3031
                }
3032
            }
3033
        }
3034
3035 4
        return static::create(
3036 4
            $result,
3037 4
            $this->iteratorClass,
3038 4
            false
3039
        );
3040
    }
3041
3042
    /**
3043
     * Check if an array has a given key.
3044
     *
3045
     * @param mixed $key
3046
     *
3047
     * @return bool
3048
     */
3049 30
    public function has($key): bool
3050
    {
3051 30
        static $UN_FOUND = null;
3052
3053 30
        if ($UN_FOUND === null) {
3054
            // Generate unique string to use as marker.
3055 1
            $UN_FOUND = \uniqid('arrayy', true);
3056
        }
3057
3058 30
        if (\is_array($key)) {
3059 1
            if ($key === []) {
3060
                return false;
3061
            }
3062
3063 1
            foreach ($key as $keyTmp) {
3064 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3065 1
                if ($found === false) {
3066 1
                    return false;
3067
                }
3068
            }
3069
3070 1
            return true;
3071
        }
3072
3073 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3074
    }
3075
3076
    /**
3077
     * Check if an array has a given value.
3078
     *
3079
     * INFO: if you need to search recursive please use ```contains()```
3080
     *
3081
     * @param mixed $value
3082
     *
3083
     * @return bool
3084
     */
3085 1
    public function hasValue($value): bool
3086
    {
3087 1
        return $this->contains($value);
3088
    }
3089
3090
    /**
3091
     * Implodes the values of this array.
3092
     *
3093
     * @param string $glue
3094
     *
3095
     * @return string
3096
     * @psalm-mutation-free
3097
     */
3098 28
    public function implode(string $glue = ''): string
3099
    {
3100 28
        return $this->implode_recursive($glue, $this->toArray(), false);
3101
    }
3102
3103
    /**
3104
     * Implodes the keys of this array.
3105
     *
3106
     * @param string $glue
3107
     *
3108
     * @return string
3109
     * @psalm-mutation-free
3110
     */
3111 8
    public function implodeKeys(string $glue = ''): string
3112
    {
3113 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3114
    }
3115
3116
    /**
3117
     * Given a list and an iterate-function that returns
3118
     * a key for each element in the list (or a property name),
3119
     * returns an object with an index of each item.
3120
     *
3121
     * @param mixed $key
3122
     *
3123
     * @return static
3124
     *                <p>(Immutable)</p>
3125
     *
3126
     * @psalm-return static<TKey,T>
3127
     * @psalm-mutation-free
3128
     */
3129 4
    public function indexBy($key): self
3130
    {
3131
        // init
3132 4
        $results = [];
3133
3134 4
        foreach ($this->getGenerator() as $a) {
3135 4
            if (\array_key_exists($key, $a) === true) {
3136 4
                $results[$a[$key]] = $a;
3137
            }
3138
        }
3139
3140 4
        return static::create(
3141 4
            $results,
3142 4
            $this->iteratorClass,
3143 4
            false
3144
        );
3145
    }
3146
3147
    /**
3148
     * alias: for "Arrayy->searchIndex()"
3149
     *
3150
     * @param mixed $value <p>The value to search for.</p>
3151
     *
3152
     * @return false|mixed
3153
     *
3154
     * @see Arrayy::searchIndex()
3155
     */
3156 4
    public function indexOf($value)
3157
    {
3158 4
        return $this->searchIndex($value);
3159
    }
3160
3161
    /**
3162
     * Get everything but the last..$to items.
3163
     *
3164
     * @param int $to
3165
     *
3166
     * @return static
3167
     *                <p>(Immutable)</p>
3168
     *
3169
     * @psalm-return static<TKey,T>
3170
     * @psalm-mutation-free
3171
     */
3172 12
    public function initial(int $to = 1): self
3173
    {
3174 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3175
    }
3176
3177
    /**
3178
     * Return an array with all elements found in input array.
3179
     *
3180
     * @param array $search
3181
     * @param bool  $keepKeys
3182
     *
3183
     * @return static
3184
     *                <p>(Immutable)</p>
3185
     *
3186
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3187
     * @psalm-return static<TKey,T>
3188
     * @psalm-mutation-free
3189
     */
3190 4
    public function intersection(array $search, bool $keepKeys = false): self
3191
    {
3192 4
        if ($keepKeys) {
3193
            /**
3194
             * @psalm-suppress MissingClosureReturnType
3195
             * @psalm-suppress MissingClosureParamType
3196
             */
3197 1
            return static::create(
3198 1
                \array_uintersect(
3199 1
                    $this->toArray(),
3200 1
                    $search,
3201 1
                    static function ($a, $b) {
3202 1
                        return $a === $b ? 0 : -1;
3203 1
                    }
3204
                ),
3205 1
                $this->iteratorClass,
3206 1
                false
3207
            );
3208
        }
3209
3210 3
        return static::create(
3211 3
            \array_values(\array_intersect($this->toArray(), $search)),
3212 3
            $this->iteratorClass,
3213 3
            false
3214
        );
3215
    }
3216
3217
    /**
3218
     * Return an array with all elements found in input array.
3219
     *
3220
     * @param array ...$array
3221
     *
3222
     * @return static
3223
     *                <p>(Immutable)</p>
3224
     *
3225
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3226
     * @psalm-return static<TKey,T>
3227
     * @psalm-mutation-free
3228
     */
3229 1
    public function intersectionMulti(...$array): self
3230
    {
3231 1
        return static::create(
3232 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3233 1
            $this->iteratorClass,
3234 1
            false
3235
        );
3236
    }
3237
3238
    /**
3239
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3240
     *
3241
     * @param array $search
3242
     *
3243
     * @return bool
3244
     *
3245
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3246
     */
3247 1
    public function intersects(array $search): bool
3248
    {
3249 1
        return $this->intersection($search)->count() > 0;
3250
    }
3251
3252
    /**
3253
     * Invoke a function on all of an array's values.
3254
     *
3255
     * @param callable $callable
3256
     * @param mixed    $arguments
3257
     *
3258
     * @return static
3259
     *                <p>(Immutable)</p>
3260
     *
3261
     * @psalm-param  callable(T=,mixed):mixed $callable
3262
     * @psalm-return static<TKey,T>
3263
     * @psalm-mutation-free
3264
     */
3265 1 View Code Duplication
    public function invoke($callable, $arguments = []): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3266
    {
3267
        // If one argument given for each iteration, create an array for it.
3268 1
        if (\is_array($arguments) === false) {
3269 1
            $arguments = \array_fill(
3270 1
                0,
3271 1
                $this->count(),
3272 1
                $arguments
3273
            );
3274
        }
3275
3276
        // If the callable has arguments, pass them.
3277 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...
3278 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3279
        } else {
3280 1
            $array = $this->map($callable);
3281
        }
3282
3283 1
        return static::create(
3284 1
            $array,
3285 1
            $this->iteratorClass,
3286 1
            false
3287
        );
3288
    }
3289
3290
    /**
3291
     * Check whether array is associative or not.
3292
     *
3293
     * @param bool $recursive
3294
     *
3295
     * @return bool
3296
     *              <p>Returns true if associative, false otherwise.</p>
3297
     */
3298 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...
3299
    {
3300 15
        if ($this->isEmpty()) {
3301 3
            return false;
3302
        }
3303
3304
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3305 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3306 13
            if ((string) $key !== $key) {
3307 13
                return false;
3308
            }
3309
        }
3310
3311 3
        return true;
3312
    }
3313
3314
    /**
3315
     * Check if a given key or keys are empty.
3316
     *
3317
     * @param int|int[]|string|string[]|null $keys
3318
     *
3319
     * @return bool
3320
     *              <p>Returns true if empty, false otherwise.</p>
3321
     * @psalm-mutation-free
3322
     */
3323 45
    public function isEmpty($keys = null): bool
3324
    {
3325 45
        if ($this->generator) {
3326
            return $this->toArray() === [];
3327
        }
3328
3329 45
        if ($keys === null) {
3330 43
            return $this->array === [];
3331
        }
3332
3333 2
        foreach ((array) $keys as $key) {
3334 2
            if (!empty($this->get($key))) {
3335 2
                return false;
3336
            }
3337
        }
3338
3339 2
        return true;
3340
    }
3341
3342
    /**
3343
     * Check if the current array is equal to the given "$array" or not.
3344
     *
3345
     * @param array $array
3346
     *
3347
     * @return bool
3348
     *
3349
     * @psalm-param array<mixed,mixed> $array
3350
     */
3351 1
    public function isEqual(array $array): bool
3352
    {
3353 1
        return $this->toArray() === $array;
3354
    }
3355
3356
    /**
3357
     * Check if the current array is a multi-array.
3358
     *
3359
     * @return bool
3360
     */
3361 22
    public function isMultiArray(): bool
3362
    {
3363
        return !(
3364 22
            \count($this->toArray(), \COUNT_NORMAL)
3365
            ===
3366 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3367
        );
3368
    }
3369
3370
    /**
3371
     * Check whether array is numeric or not.
3372
     *
3373
     * @return bool
3374
     *              <p>Returns true if numeric, false otherwise.</p>
3375
     */
3376 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...
3377
    {
3378 5
        if ($this->isEmpty()) {
3379 2
            return false;
3380
        }
3381
3382
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3383 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3384 4
            if ((int) $key !== $key) {
3385 4
                return false;
3386
            }
3387
        }
3388
3389 2
        return true;
3390
    }
3391
3392
    /**
3393
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3394
     *
3395
     * @param bool $recursive
3396
     *
3397
     * @return bool
3398
     * @psalm-mutation-free
3399
     */
3400 9
    public function isSequential(bool $recursive = false): bool
3401
    {
3402
3403
        // recursive
3404
3405 9
        if ($recursive === true) {
3406
            return $this->array_keys_recursive($this->toArray())
3407
                   ===
3408
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3409
        }
3410
3411
        // non recursive
3412
3413 9
        return \array_keys($this->toArray())
3414
               ===
3415 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3416
    }
3417
3418
    /**
3419
     * @return array
3420
     *
3421
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3422
     */
3423 2
    public function jsonSerialize(): array
3424
    {
3425 2
        return $this->toArray();
3426
    }
3427
3428
    /**
3429
     * Gets the key/index of the element at the current internal iterator position.
3430
     *
3431
     * @return int|string|null
3432
     */
3433
    public function key()
3434
    {
3435
        return \key($this->array);
3436
    }
3437
3438
    /**
3439
     * Checks if the given key exists in the provided array.
3440
     *
3441
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3442
     *       then you need to use "Arrayy->offsetExists()".
3443
     *
3444
     * @param int|string $key the key to look for
3445
     *
3446
     * @return bool
3447
     * @psalm-mutation-free
3448
     */
3449 162
    public function keyExists($key): bool
3450
    {
3451 162
        return \array_key_exists($key, $this->array);
3452
    }
3453
3454
    /**
3455
     * Get all keys from the current array.
3456
     *
3457
     * @param bool       $recursive     [optional] <p>
3458
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3459
     *                                  </p>
3460
     * @param mixed|null $search_values [optional] <p>
3461
     *                                  If specified, then only keys containing these values are returned.
3462
     *                                  </p>
3463
     * @param bool       $strict        [optional] <p>
3464
     *                                  Determines if strict comparison (===) should be used during the search.
3465
     *                                  </p>
3466
     *
3467
     * @return static
3468
     *                <p>(Immutable) An array of all the keys in input.</p>
3469
     *
3470
     * @psalm-return static<array-key,TKey>
3471
     * @psalm-mutation-free
3472
     */
3473 29
    public function keys(
3474
        bool $recursive = false,
3475
        $search_values = null,
3476
        bool $strict = true
3477
    ): self {
3478
3479
        // recursive
3480
3481 29
        if ($recursive === true) {
3482 4
            $array = $this->array_keys_recursive(
3483 4
                null,
3484 4
                $search_values,
3485 4
                $strict
3486
            );
3487
3488 4
            return static::create(
3489 4
                $array,
3490 4
                $this->iteratorClass,
3491 4
                false
3492
            );
3493
        }
3494
3495
        // non recursive
3496
3497 28
        if ($search_values === null) {
3498 28
            $arrayFunction = function (): \Generator {
3499 28
                foreach ($this->getGenerator() as $key => $value) {
3500 26
                    yield $key;
3501
                }
3502 28
            };
3503
        } else {
3504 1
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3505 1
                $is_array_tmp = \is_array($search_values);
3506
3507
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3508 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3509 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...
3510
                        (
3511 1
                            $is_array_tmp === false
3512
                            &&
3513 1
                            $strict === true
3514
                            &&
3515 1
                            $search_values === $value
3516
                        )
3517
                        ||
3518
                        (
3519 1
                            $is_array_tmp === false
3520
                            &&
3521 1
                            $strict === false
3522
                            &&
3523 1
                            $search_values == $value
3524
                        )
3525
                        ||
3526
                        (
3527 1
                            $is_array_tmp === true
3528
                            &&
3529 1
                            \in_array($value, $search_values, $strict)
3530
                        )
3531
                    ) {
3532 1
                        yield $key;
3533
                    }
3534
                }
3535 1
            };
3536
        }
3537
3538 28
        return static::create(
3539 28
            $arrayFunction,
3540 28
            $this->iteratorClass,
3541 28
            false
3542
        );
3543
    }
3544
3545
    /**
3546
     * Sort an array by key in reverse order.
3547
     *
3548
     * @param int $sort_flags [optional] <p>
3549
     *                        You may modify the behavior of the sort using the optional
3550
     *                        parameter sort_flags, for details
3551
     *                        see sort.
3552
     *                        </p>
3553
     *
3554
     * @return $this
3555
     *               <p>(Mutable) Return this Arrayy object.</p>
3556
     *
3557
     * @psalm-return static<TKey,T>
3558
     */
3559 4
    public function krsort(int $sort_flags = 0): self
3560
    {
3561 4
        $this->generatorToArray();
3562
3563 4
        \krsort($this->array, $sort_flags);
3564
3565 4
        return $this;
3566
    }
3567
3568
    /**
3569
     * Sort an array by key in reverse order.
3570
     *
3571
     * @param int $sort_flags [optional] <p>
3572
     *                        You may modify the behavior of the sort using the optional
3573
     *                        parameter sort_flags, for details
3574
     *                        see sort.
3575
     *                        </p>
3576
     *
3577
     * @return $this
3578
     *               <p>(Immutable) Return this Arrayy object.</p>
3579
     *
3580
     * @psalm-return static<TKey,T>
3581
     * @psalm-mutation-free
3582
     */
3583 4
    public function krsortImmutable(int $sort_flags = 0): self
3584
    {
3585 4
        $that = clone $this;
3586
3587
        /**
3588
         * @psalm-suppress ImpureMethodCall - object is already cloned
3589
         */
3590 4
        $that->krsort($sort_flags);
3591
3592 4
        return $that;
3593
    }
3594
3595
    /**
3596
     * Get the last value from the current array.
3597
     *
3598
     * @return mixed|null
3599
     *                    <p>Return null if there wasn't a element.</p>
3600
     * @psalm-mutation-free
3601
     */
3602 17
    public function last()
3603
    {
3604 17
        $key_last = $this->lastKey();
3605 17
        if ($key_last === null) {
3606 2
            return null;
3607
        }
3608
3609 15
        return $this->get($key_last);
3610
    }
3611
3612
    /**
3613
     * Get the last key from the current array.
3614
     *
3615
     * @return mixed|null
3616
     *                    <p>Return null if there wasn't a element.</p>
3617
     * @psalm-mutation-free
3618
     */
3619 21
    public function lastKey()
3620
    {
3621 21
        $this->generatorToArray();
3622
3623 21
        return \array_key_last($this->array);
3624
    }
3625
3626
    /**
3627
     * Get the last value(s) from the current array.
3628
     *
3629
     * @param int|null $number
3630
     *
3631
     * @return static
3632
     *                <p>(Immutable)</p>
3633
     *
3634
     * @psalm-return static<TKey,T>
3635
     * @psalm-mutation-free
3636
     */
3637 13
    public function lastsImmutable(int $number = null): self
3638
    {
3639 13
        if ($this->isEmpty()) {
3640 1
            return static::create(
3641 1
                [],
3642 1
                $this->iteratorClass,
3643 1
                false
3644
            );
3645
        }
3646
3647 12
        if ($number === null) {
3648 8
            $poppedValue = $this->last();
3649
3650 8
            if ($poppedValue === null) {
3651 1
                $poppedValue = [$poppedValue];
3652
            } else {
3653 7
                $poppedValue = (array) $poppedValue;
3654
            }
3655
3656 8
            $arrayy = static::create(
3657 8
                $poppedValue,
3658 8
                $this->iteratorClass,
3659 8
                false
3660
            );
3661
        } else {
3662 4
            $arrayy = $this->rest(-$number);
3663
        }
3664
3665 12
        return $arrayy;
3666
    }
3667
3668
    /**
3669
     * Get the last value(s) from the current array.
3670
     *
3671
     * @param int|null $number
3672
     *
3673
     * @return $this
3674
     *               <p>(Mutable)</p>
3675
     *
3676
     * @psalm-return static<TKey,T>
3677
     */
3678 13
    public function lastsMutable(int $number = null): self
3679
    {
3680 13
        if ($this->isEmpty()) {
3681 1
            return $this;
3682
        }
3683
3684 12
        if ($number === null) {
3685 8
            $poppedValue = $this->last();
3686
3687 8
            if ($poppedValue === null) {
3688 1
                $poppedValue = [$poppedValue];
3689
            } else {
3690 7
                $poppedValue = (array) $poppedValue;
3691
            }
3692
3693 8
            $this->array = static::create(
3694 8
                $poppedValue,
3695 8
                $this->iteratorClass,
3696 8
                false
3697 8
            )->toArray();
3698
        } else {
3699 4
            $this->array = $this->rest(-$number)->toArray();
3700
        }
3701
3702 12
        $this->generator = null;
3703
3704 12
        return $this;
3705
    }
3706
3707
    /**
3708
     * Count the values from the current array.
3709
     *
3710
     * alias: for "Arrayy->count()"
3711
     *
3712
     * @param int $mode
3713
     *
3714
     * @return int
3715
     *
3716
     * @see Arrayy::count()
3717
     */
3718 20
    public function length(int $mode = \COUNT_NORMAL): int
3719
    {
3720 20
        return $this->count($mode);
3721
    }
3722
3723
    /**
3724
     * Apply the given function to the every element of the array,
3725
     * collecting the results.
3726
     *
3727
     * @param callable $callable
3728
     * @param bool     $useKeyAsSecondParameter
3729
     * @param mixed    ...$arguments
3730
     *
3731
     * @return static
3732
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3733
     *
3734
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3735
     * @psalm-return static<TKey,T>
3736
     * @psalm-mutation-free
3737
     */
3738 5
    public function map(
3739
        callable $callable,
3740
        bool $useKeyAsSecondParameter = false,
3741
        ...$arguments
3742
    ) {
3743
        /**
3744
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3745
         */
3746 5
        $useArguments = \func_num_args() > 2;
3747
3748 5
        return static::create(
3749 5
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3750 5
                foreach ($this->getGenerator() as $key => $value) {
3751 4
                    if ($useArguments) {
3752 3
                        if ($useKeyAsSecondParameter) {
3753
                            yield $key => $callable($value, $key, ...$arguments);
3754
                        } else {
3755 3
                            yield $key => $callable($value, ...$arguments);
3756
                        }
3757
                    } else {
3758
                        /** @noinspection NestedPositiveIfStatementsInspection */
3759 4
                        if ($useKeyAsSecondParameter) {
3760
                            yield $key => $callable($value, $key);
3761
                        } else {
3762 4
                            yield $key => $callable($value);
3763
                        }
3764
                    }
3765
                }
3766 5
            },
3767 5
            $this->iteratorClass,
3768 5
            false
3769
        );
3770
    }
3771
3772
    /**
3773
     * Check if all items in current array match a truth test.
3774
     *
3775
     * @param \Closure $closure
3776
     *
3777
     * @return bool
3778
     */
3779 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...
3780
    {
3781 15
        if ($this->count() === 0) {
3782 2
            return false;
3783
        }
3784
3785 13
        foreach ($this->getGenerator() as $key => $value) {
3786 13
            $value = $closure($value, $key);
3787
3788 13
            if ($value === false) {
3789 13
                return false;
3790
            }
3791
        }
3792
3793 7
        return true;
3794
    }
3795
3796
    /**
3797
     * Check if any item in the current array matches a truth test.
3798
     *
3799
     * @param \Closure $closure
3800
     *
3801
     * @return bool
3802
     */
3803 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...
3804
    {
3805 14
        if ($this->count() === 0) {
3806 2
            return false;
3807
        }
3808
3809 12
        foreach ($this->getGenerator() as $key => $value) {
3810 12
            $value = $closure($value, $key);
3811
3812 12
            if ($value === true) {
3813 12
                return true;
3814
            }
3815
        }
3816
3817 4
        return false;
3818
    }
3819
3820
    /**
3821
     * Get the max value from an array.
3822
     *
3823
     * @return false|mixed
3824
     *                     <p>Will return false if there are no values.</p>
3825
     */
3826 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...
3827
    {
3828 10
        if ($this->count() === 0) {
3829 1
            return false;
3830
        }
3831
3832 9
        $max = false;
3833
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3834 9
        foreach ($this->getGeneratorByReference() as &$value) {
3835
            if (
3836 9
                $max === false
3837
                ||
3838 9
                $value > $max
3839
            ) {
3840 9
                $max = $value;
3841
            }
3842
        }
3843
3844 9
        return $max;
3845
    }
3846
3847
    /**
3848
     * Merge the new $array into the current array.
3849
     *
3850
     * - keep key,value from the current array, also if the index is in the new $array
3851
     *
3852
     * @param array $array
3853
     * @param bool  $recursive
3854
     *
3855
     * @return static
3856
     *                <p>(Immutable)</p>
3857
     *
3858
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3859
     * @psalm-return static<int|TKey,T>
3860
     * @psalm-mutation-free
3861
     */
3862 32 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3863
    {
3864 32
        if ($recursive === true) {
3865 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
3866 9
            $result = \array_replace_recursive($this->toArray(), $array);
3867
        } else {
3868 23
            $result = \array_replace($this->toArray(), $array);
3869
        }
3870
3871 32
        return static::create(
3872 32
            $result,
3873 32
            $this->iteratorClass,
3874 32
            false
3875
        );
3876
    }
3877
3878
    /**
3879
     * Merge the new $array into the current array.
3880
     *
3881
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3882
     * - create new indexes
3883
     *
3884
     * @param array $array
3885
     * @param bool  $recursive
3886
     *
3887
     * @return static
3888
     *                <p>(Immutable)</p>
3889
     *
3890
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3891
     * @psalm-return static<TKey,T>
3892
     * @psalm-mutation-free
3893
     */
3894 19 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3895
    {
3896 19
        if ($recursive === true) {
3897 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
3898 5
            $result = \array_merge_recursive($this->toArray(), $array);
3899
        } else {
3900 14
            $result = \array_merge($this->toArray(), $array);
3901
        }
3902
3903 19
        return static::create(
3904 19
            $result,
3905 19
            $this->iteratorClass,
3906 19
            false
3907
        );
3908
    }
3909
3910
    /**
3911
     * Merge the the current array into the $array.
3912
     *
3913
     * - use key,value from the new $array, also if the index is in the current array
3914
     *
3915
     * @param array $array
3916
     * @param bool  $recursive
3917
     *
3918
     * @return static
3919
     *                <p>(Immutable)</p>
3920
     *
3921
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3922
     * @psalm-return static<TKey,T>
3923
     * @psalm-mutation-free
3924
     */
3925 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...
3926
    {
3927 16
        if ($recursive === true) {
3928 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
3929 4
            $result = \array_replace_recursive($array, $this->toArray());
3930
        } else {
3931 12
            $result = \array_replace($array, $this->toArray());
3932
        }
3933
3934 16
        return static::create(
3935 16
            $result,
3936 16
            $this->iteratorClass,
3937 16
            false
3938
        );
3939
    }
3940
3941
    /**
3942
     * Merge the current array into the new $array.
3943
     *
3944
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3945
     * - create new indexes
3946
     *
3947
     * @param array $array
3948
     * @param bool  $recursive
3949
     *
3950
     * @return static
3951
     *                <p>(Immutable)</p>
3952
     *
3953
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3954
     * @psalm-return static<TKey,T>
3955
     * @psalm-mutation-free
3956
     */
3957 20 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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