Completed
Push — master ( 06ece5...76d9e4 )
by Lars
01:33
created

Arrayy::internalSet()   C

Complexity

Conditions 11
Paths 20

Size

Total Lines 59

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 11.0099

Importance

Changes 0
Metric Value
cc 11
nc 20
nop 3
dl 0
loc 59
ccs 22
cts 23
cp 0.9565
crap 11.0099
rs 6.7478
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

Loading history...
127
        }
128
129 50
        if ($this->generator !== null) {
130
            $this->generator = clone $this->generator;
131
        }
132 50
    }
133
134
    /**
135
     * Call object as function.
136
     *
137
     * @param mixed $key
138
     *
139
     * @return mixed
140
     */
141 1
    public function __invoke($key = null)
142
    {
143 1
        if ($key !== null) {
144 1
            $this->generatorToArray();
145
146 1
            return $this->array[$key] ?? false;
147
        }
148
149
        return $this->toArray();
150
    }
151
152
    /**
153
     * Whether or not an element exists by key.
154
     *
155
     * @param mixed $key
156
     *
157
     * @return bool
158
     *              <p>True is the key/index exists, otherwise false.</p>
159
     */
160
    public function __isset($key): bool
161
    {
162
        return $this->offsetExists($key);
163
    }
164
165
    /**
166
     * Assigns a value to the specified element.
167
     *
168
     * @param mixed $key
169
     * @param mixed $value
170
     *
171
     * @return void
172
     */
173 2
    public function __set($key, $value)
174
    {
175 2
        $this->internalSet($key, $value);
176 2
    }
177
178
    /**
179
     * magic to string
180
     *
181
     * @return string
182
     */
183 15
    public function __toString(): string
184
    {
185 15
        return $this->toString();
186
    }
187
188
    /**
189
     * Unset element by key.
190
     *
191
     * @param mixed $key
192
     */
193
    public function __unset($key)
194
    {
195
        $this->internalRemove($key);
196
    }
197
198
    /**
199
     * Get a value by key.
200
     *
201
     * @param mixed $key
202
     *
203
     * @return mixed
204
     *               <p>Get a Value from the current array.</p>
205
     */
206 4
    public function &__get($key)
207
    {
208 4
        $return = $this->get($key);
209
210 4
        if (\is_array($return) === true) {
211
            return static::create($return, $this->iteratorClass, false);
212
        }
213
214 4
        return $return;
215
    }
216
217
    /**
218
     * Add new values (optional using dot-notation).
219
     *
220
     * @param mixed $value
221
     * @param int|string|null $key
222
     *
223
     * @return static
224
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
225
     *
226
     * @psalm-param  T $value
227
     * @psalm-return static<TKey,T>
228
     */
229 6
    public function add($value, $key = null)
230
    {
231 6
        if ($key !== null) {
232 1
            $value = !\is_array($value) ? [$value] : $value;
233 1
            $get = $this[$key];
234 1
            $get = !$get instanceof self ? [$get] : $get->getArray();
235 1
            $values = \array_merge_recursive($get, $value);
236
237 1
            $this->internalSet($key, $values);
238
239 1
            return $this;
240
        }
241
242 5
        return $this->append($value);
243
    }
244
245
    /**
246
     * Append a (key) + value to the current array.
247
     *
248
     * @param mixed $value
249
     * @param mixed $key
250
     *
251
     * @return $this
252
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
253
     *
254
     * @psalm-return static<TKey,T>
255
     */
256 17
    public function append($value, $key = null): self
257
    {
258 17
        $this->generatorToArray();
259
260 17
        if ($this->properties !== []) {
261 4
            $this->checkType($key, $value);
262
        }
263
264 16
        if ($key !== null) {
265
            if (
266 2
                isset($this->array[$key])
267
                &&
268 2
                \is_array($this->array[$key]) === true
269
            ) {
270
                $this->array[$key][] = $value;
271
            } else {
272 2
                $this->array[$key] = $value;
273
            }
274
        } else {
275 14
            $this->array[] = $value;
276
        }
277
278 16
        return $this;
279
    }
280
281
    /**
282
     * Sort the entries by value.
283
     *
284
     * @param int $sort_flags [optional] <p>
285
     *                        You may modify the behavior of the sort using the optional
286
     *                        parameter sort_flags, for details
287
     *                        see sort.
288
     *                        </p>
289
     *
290
     * @return $this
291
     *               <p>(Mutable) Return this Arrayy object.</p>
292
     *
293
     * @psalm-return static<TKey,T>
294
     */
295 4
    public function asort(int $sort_flags = 0): self
296
    {
297 4
        $this->generatorToArray();
298
299 4
        \asort($this->array, $sort_flags);
300
301 4
        return $this;
302
    }
303
304
    /**
305
     * Sort the entries by value.
306
     *
307
     * @param int $sort_flags [optional] <p>
308
     *                        You may modify the behavior of the sort using the optional
309
     *                        parameter sort_flags, for details
310
     *                        see sort.
311
     *                        </p>
312
     *
313
     * @return $this
314
     *               <p>(Immutable) Return this Arrayy object.</p>
315
     *
316
     * @psalm-return static<TKey,T>
317
     * @psalm-mutation-free
318
     */
319 4
    public function asortImmutable(int $sort_flags = 0): self
320
    {
321 4
        $that = clone $this;
322
323
        /**
324
         * @psalm-suppress ImpureMethodCall - object is already cloned
325
         */
326 4
        $that->asort($sort_flags);
327
328 4
        return $that;
329
    }
330
331
    /**
332
     * Counts all elements in an array, or something in an object.
333
     *
334
     * <p>
335
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
336
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
337
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
338
     * implemented and used in PHP.
339
     * </p>
340
     *
341
     * @see http://php.net/manual/en/function.count.php
342
     *
343
     * @param int $mode [optional] If the optional mode parameter is set to
344
     *                  COUNT_RECURSIVE (or 1), count
345
     *                  will recursively count the array. This is particularly useful for
346
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
347
     *
348
     * @return int
349
     *             <p>
350
     *             The number of elements in var, which is
351
     *             typically an array, since anything else will have one
352
     *             element.
353
     *             </p>
354
     *             <p>
355
     *             If var is not an array or an object with
356
     *             implemented Countable interface,
357
     *             1 will be returned.
358
     *             There is one exception, if var is &null;,
359
     *             0 will be returned.
360
     *             </p>
361
     *             <p>
362
     *             Caution: count may return 0 for a variable that isn't set,
363
     *             but it may also return 0 for a variable that has been initialized with an
364
     *             empty array. Use isset to test if a variable is set.
365
     *             </p>
366
     * @psalm-mutation-free
367
     */
368 147
    public function count(int $mode = \COUNT_NORMAL): int
369
    {
370
        if (
371 147
            $this->generator
372
            &&
373 147
            $mode === \COUNT_NORMAL
374
        ) {
375 4
            return \iterator_count($this->generator);
376
        }
377
378 143
        return \count($this->toArray(), $mode);
379
    }
380
381
    /**
382
     * Exchange the array for another one.
383
     *
384
     * @param array|static $data
385
     *
386
     * @return array
387
     *
388
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
389
     * @psalm-return array<mixed,mixed>|array<TKey,T>
390
     */
391 1
    public function exchangeArray($data): array
392
    {
393 1
        $this->array = $this->fallbackForArray($data);
394
395 1
        return $this->array;
396
    }
397
398
    /**
399
     * Creates a copy of the ArrayyObject.
400
     *
401
     * @return array
402
     *
403
     * @psalm-return array<mixed,mixed>|array<TKey,T>
404
     */
405 6
    public function getArrayCopy(): array
406
    {
407 6
        $this->generatorToArray();
408
409 6
        return $this->array;
410
    }
411
412
    /**
413
     * Returns a new iterator, thus implementing the \Iterator interface.
414
     *
415
     * @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...
416
     *                          <p>An iterator for the values in the array.</p>
417
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
418
     */
419 26
    public function getIterator(): \Iterator
420
    {
421 26
        if ($this->generator instanceof ArrayyRewindableGenerator) {
422 1
            return $this->generator;
423
        }
424
425 25
        $iterator = $this->getIteratorClass();
426
427 25
        if ($iterator === ArrayyIterator::class) {
428 25
            return new $iterator($this->toArray(), 0, static::class);
429
        }
430
431
        $return = new $iterator($this->toArray());
432
        \assert($return instanceof \Iterator);
433
434
        return $return;
435
    }
436
437
    /**
438
     * Gets the iterator classname for the ArrayObject.
439
     *
440
     * @return string
441
     *
442
     * @psalm-return class-string
443
     */
444 25
    public function getIteratorClass(): string
445
    {
446 25
        return $this->iteratorClass;
447
    }
448
449
    /**
450
     * Sort the entries by key.
451
     *
452
     * @param int $sort_flags [optional] <p>
453
     *                        You may modify the behavior of the sort using the optional
454
     *                        parameter sort_flags, for details
455
     *                        see sort.
456
     *                        </p>
457
     *
458
     * @return $this
459
     *               <p>(Mutable) Return this Arrayy object.</p>
460
     *
461
     * @psalm-return static<TKey,T>
462
     */
463 4
    public function ksort(int $sort_flags = 0): self
464
    {
465 4
        $this->generatorToArray();
466
467 4
        \ksort($this->array, $sort_flags);
468
469 4
        return $this;
470
    }
471
472
    /**
473
     * Sort the entries by key.
474
     *
475
     * @param int $sort_flags [optional] <p>
476
     *                        You may modify the behavior of the sort using the optional
477
     *                        parameter sort_flags, for details
478
     *                        see sort.
479
     *                        </p>
480
     *
481
     * @return $this
482
     *               <p>(Immutable) Return this Arrayy object.</p>
483
     *
484
     * @psalm-return static<TKey,T>
485
     */
486 4
    public function ksortImmutable(int $sort_flags = 0): self
487
    {
488 4
        $that = clone $this;
489
490
        /**
491
         * @psalm-suppress ImpureMethodCall - object is already cloned
492
         */
493 4
        $that->ksort($sort_flags);
494
495 4
        return $that;
496
    }
497
498
    /**
499
     * Sort an array using a case insensitive "natural order" algorithm.
500
     *
501
     * @return $this
502
     *               <p>(Mutable) Return this Arrayy object.</p>
503
     *
504
     * @psalm-return static<TKey,T>
505
     */
506 8
    public function natcasesort(): self
507
    {
508 8
        $this->generatorToArray();
509
510 8
        \natcasesort($this->array);
511
512 8
        return $this;
513
    }
514
515
    /**
516
     * Sort an array using a case insensitive "natural order" algorithm.
517
     *
518
     * @return $this
519
     *               <p>(Immutable) Return this Arrayy object.</p>
520
     *
521
     * @psalm-return static<TKey,T>
522
     * @psalm-mutation-free
523
     */
524 4
    public function natcasesortImmutable(): self
525
    {
526 4
        $that = clone $this;
527
528
        /**
529
         * @psalm-suppress ImpureMethodCall - object is already cloned
530
         */
531 4
        $that->natcasesort();
532
533 4
        return $that;
534
    }
535
536
    /**
537
     * Sort entries using a "natural order" algorithm.
538
     *
539
     * @return $this
540
     *               <p>(Mutable) Return this Arrayy object.</p>
541
     *
542
     * @psalm-return static<TKey,T>
543
     */
544 9
    public function natsort(): self
545
    {
546 9
        $this->generatorToArray();
547
548 9
        \natsort($this->array);
549
550 9
        return $this;
551
    }
552
553
    /**
554
     * Sort entries using a "natural order" algorithm.
555
     *
556
     * @return $this
557
     *               <p>(Immutable) Return this Arrayy object.</p>
558
     *
559
     * @psalm-return static<TKey,T>
560
     * @psalm-mutation-free
561
     */
562 4
    public function natsortImmutable(): self
563
    {
564 4
        $that = clone $this;
565
566
        /**
567
         * @psalm-suppress ImpureMethodCall - object is already cloned
568
         */
569 4
        $that->natsort();
570
571 4
        return $that;
572
    }
573
574
    /**
575
     * Whether or not an offset exists.
576
     *
577
     * @param bool|int|string $offset
578
     *
579
     * @return bool
580
     *
581
     * @noinspection PhpSillyAssignmentInspection
582
     *
583
     * @psalm-mutation-free
584
     */
585 137
    public function offsetExists($offset): bool
586
    {
587 137
        $this->generatorToArray();
588
589 137
        if ($this->array === []) {
590 7
            return false;
591
        }
592
593
        // php cast "bool"-index into "int"-index
594 131
        if ((bool) $offset === $offset) {
595 1
            $offset = (int) $offset;
596
        }
597
598
        /** @var int|string $offset - hint for phpstan */
599 131
        $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...
600
601 131
        $tmpReturn = $this->keyExists($offset);
602
603
        if (
604 131
            $tmpReturn === true
605
            ||
606 131
            \strpos((string) $offset, $this->pathSeparator) === false
607
        ) {
608 127
            return $tmpReturn;
609
        }
610
611 5
        $offsetExists = false;
612
613
        /**
614
         * https://github.com/vimeo/psalm/issues/2536
615
         *
616
         * @psalm-suppress PossiblyInvalidArgument
617
         * @psalm-suppress InvalidScalarArgument
618
         */
619 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...
620 5
            $this->pathSeparator
621
            &&
622 5
            (string) $offset === $offset
623
            &&
624 5
            \strpos($offset, $this->pathSeparator) !== false
625
        ) {
626 5
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
627 5
            if ($explodedPath !== false) {
628
                /** @var string $lastOffset - helper for phpstan */
629 5
                $lastOffset = \array_pop($explodedPath);
630 5
                $containerPath = \implode($this->pathSeparator, $explodedPath);
631
632
                /**
633
                 * @psalm-suppress MissingClosureReturnType
634
                 * @psalm-suppress MissingClosureParamType
635
                 */
636 5
                $this->callAtPath(
637 5
                    $containerPath,
638 5
                    static function ($container) use ($lastOffset, &$offsetExists) {
639 5
                        $offsetExists = \array_key_exists($lastOffset, $container);
640 5
                    }
641
                );
642
            }
643
        }
644
645 5
        return $offsetExists;
646
    }
647
648
    /**
649
     * Returns the value at specified offset.
650
     *
651
     * @param int|string $offset
652
     *
653
     * @return mixed
654
     *               <p>Will return null if the offset did not exists.</p>
655
     */
656 106
    public function offsetGet($offset)
657
    {
658 106
        return $this->offsetExists($offset) ? $this->get($offset) : null;
659
    }
660
661
    /**
662
     * Assigns a value to the specified offset + check the type.
663
     *
664
     * @param int|string|null $offset
665
     * @param mixed           $value
666
     *
667
     * @return void
668
     */
669 20
    public function offsetSet($offset, $value)
670
    {
671 20
        $this->generatorToArray();
672
673 20
        if ($offset === null) {
674 6
            if ($this->properties !== []) {
675 1
                $this->checkType(null, $value);
676
            }
677
678 5
            $this->array[] = $value;
679
        } else {
680 14
            $this->internalSet(
681 14
                $offset,
682 14
                $value,
683 14
                true
684
            );
685
        }
686 19
    }
687
688
    /**
689
     * Unset an offset.
690
     *
691
     * @param int|string $offset
692
     *
693
     * @return void
694
     *              <p>(Mutable) Return nothing.</p>
695
     */
696 25
    public function offsetUnset($offset)
697
    {
698 25
        $this->generatorToArray();
699
700 25
        if ($this->array === []) {
701 6
            return;
702
        }
703
704 20
        if ($this->keyExists($offset)) {
705 13
            unset($this->array[$offset]);
706
707 13
            return;
708
        }
709
710
        /**
711
         * https://github.com/vimeo/psalm/issues/2536
712
         *
713
         * @psalm-suppress PossiblyInvalidArgument
714
         * @psalm-suppress InvalidScalarArgument
715
         */
716 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...
717 10
            $this->pathSeparator
718
            &&
719 10
            (string) $offset === $offset
720
            &&
721 10
            \strpos($offset, $this->pathSeparator) !== false
722
        ) {
723 7
            $path = \explode($this->pathSeparator, (string) $offset);
724
725 7
            if ($path !== false) {
726 7
                $pathToUnset = \array_pop($path);
727
728
                /**
729
                 * @psalm-suppress MissingClosureReturnType
730
                 * @psalm-suppress MissingClosureParamType
731
                 */
732 7
                $this->callAtPath(
733 7
                    \implode($this->pathSeparator, $path),
734 7
                    static function (&$offset) use ($pathToUnset) {
735 6
                        if (\is_array($offset)) {
736 5
                            unset($offset[$pathToUnset]);
737
                        } else {
738 1
                            $offset = null;
739
                        }
740 7
                    }
741
                );
742
            }
743
        }
744
745 10
        unset($this->array[$offset]);
746 10
    }
747
748
    /**
749
     * Serialize the current "Arrayy"-object.
750
     *
751
     * @return string
752
     */
753 2
    public function serialize(): string
754
    {
755 2
        $this->generatorToArray();
756
757 2
        if (\PHP_VERSION_ID < 70400) {
758 2
            return parent::serialize();
759
        }
760
761
        return \serialize($this);
762
    }
763
764
    /**
765
     * Sets the iterator classname for the current "Arrayy"-object.
766
     *
767
     * @param string $iteratorClass
768
     *
769
     * @throws \InvalidArgumentException
770
     *
771
     * @return void
772
     *
773
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
774
     */
775 1164
    public function setIteratorClass($iteratorClass)
776
    {
777 1164
        if (\class_exists($iteratorClass)) {
778 1164
            $this->iteratorClass = $iteratorClass;
779
780 1164
            return;
781
        }
782
783
        if (\strpos($iteratorClass, '\\') === 0) {
784
            $iteratorClass = '\\' . $iteratorClass;
785
            if (\class_exists($iteratorClass)) {
786
                /**
787
                 * @psalm-suppress PropertyTypeCoercion
788
                 */
789
                $this->iteratorClass = $iteratorClass;
790
791
                return;
792
            }
793
        }
794
795
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
796
    }
797
798
    /**
799
     * Sort the entries with a user-defined comparison function and maintain key association.
800
     *
801
     * @param callable $function
802
     *
803
     * @throws \InvalidArgumentException
804
     *
805
     * @return $this
806
     *               <p>(Mutable) Return this Arrayy object.</p>
807
     *
808
     * @psalm-return static<TKey,T>
809
     */
810 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...
811
    {
812 8
        if (!\is_callable($function)) {
813
            throw new \InvalidArgumentException('Passed function must be callable');
814
        }
815
816 8
        $this->generatorToArray();
817
818 8
        \uasort($this->array, $function);
819
820 8
        return $this;
821
    }
822
823
    /**
824
     * Sort the entries with a user-defined comparison function and maintain key association.
825
     *
826
     * @param callable $function
827
     *
828
     * @throws \InvalidArgumentException
829
     *
830
     * @return $this
831
     *               <p>(Immutable) Return this Arrayy object.</p>
832
     *
833
     * @psalm-return static<TKey,T>
834
     * @psalm-mutation-free
835
     */
836 4
    public function uasortImmutable($function): self
837
    {
838 4
        $that = clone $this;
839
840
        /**
841
         * @psalm-suppress ImpureMethodCall - object is already cloned
842
         */
843 4
        $that->uasort($function);
844
845 4
        return $that;
846
    }
847
848
    /**
849
     * Sort the entries by keys using a user-defined comparison function.
850
     *
851
     * @param callable $function
852
     *
853
     * @throws \InvalidArgumentException
854
     *
855
     * @return static
856
     *                <p>(Mutable) Return this Arrayy object.</p>
857
     *
858
     * @psalm-return static<TKey,T>
859
     */
860 5
    public function uksort($function): self
861
    {
862 5
        return $this->customSortKeys($function);
863
    }
864
865
    /**
866
     * Sort the entries by keys using a user-defined comparison function.
867
     *
868
     * @param callable $function
869
     *
870
     * @throws \InvalidArgumentException
871
     *
872
     * @return static
873
     *                <p>(Immutable) Return this Arrayy object.</p>
874
     *
875
     * @psalm-return static<TKey,T>
876
     * @psalm-mutation-free
877
     */
878 1
    public function uksortImmutable($function): self
879
    {
880 1
        return $this->customSortKeysImmutable($function);
881
    }
882
883
    /**
884
     * Unserialize an string and return the instance of the "Arrayy"-class.
885
     *
886
     * @param string $string
887
     *
888
     * @return $this
889
     *
890
     * @psalm-return static<TKey,T>
891
     */
892 2
    public function unserialize($string): self
893
    {
894 2
        if (\PHP_VERSION_ID < 70400) {
895 2
            parent::unserialize($string);
896
897 2
            return $this;
898
        }
899
900
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
901
    }
902
903
    /**
904
     * Append a (key) + values to the current array.
905
     *
906
     * @param array $values
907
     * @param mixed $key
908
     *
909
     * @return $this
910
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
911
     *
912
     * @psalm-param  array<mixed,T> $values
913
     * @psalm-param  TKey|null $key
914
     * @psalm-return static<TKey,T>
915
     */
916 1
    public function appendArrayValues(array $values, $key = null)
917
    {
918 1
        $this->generatorToArray();
919
920 1
        if ($key !== null) {
921
            if (
922 1
                isset($this->array[$key])
923
                &&
924 1
                \is_array($this->array[$key]) === true
925
            ) {
926 1
                foreach ($values as $value) {
927 1
                    $this->array[$key][] = $value;
928
                }
929
            } else {
930
                foreach ($values as $value) {
931 1
                    $this->array[$key] = $value;
932
                }
933
            }
934
        } else {
935
            foreach ($values as $value) {
936
                $this->array[] = $value;
937
            }
938
        }
939
940 1
        return $this;
941
    }
942
943
    /**
944
     * Add a suffix to each key.
945
     *
946
     * @param mixed $prefix
947
     *
948
     * @return static
949
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
950
     *
951
     * @psalm-return static<TKey,T>
952
     * @psalm-mutation-free
953
     */
954 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...
955
    {
956
        // init
957 10
        $result = [];
958
959 10
        foreach ($this->getGenerator() as $key => $item) {
960 9
            if ($item instanceof self) {
961
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
962 9
            } elseif (\is_array($item) === true) {
963
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
964
                    ->appendToEachKey($prefix)
965
                    ->toArray();
966
            } else {
967 9
                $result[$prefix . $key] = $item;
968
            }
969
        }
970
971 10
        return self::create($result, $this->iteratorClass, false);
972
    }
973
974
    /**
975
     * Add a prefix to each value.
976
     *
977
     * @param mixed $prefix
978
     *
979
     * @return static
980
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
981
     *
982
     * @psalm-return static<TKey,T>
983
     * @psalm-mutation-free
984
     */
985 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...
986
    {
987
        // init
988 10
        $result = [];
989
990 10
        foreach ($this->getGenerator() as $key => $item) {
991 9
            if ($item instanceof self) {
992
                $result[$key] = $item->appendToEachValue($prefix);
993 9
            } elseif (\is_array($item) === true) {
994
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
995 9
            } elseif (\is_object($item) === true) {
996 1
                $result[$key] = $item;
997
            } else {
998 9
                $result[$key] = $prefix . $item;
999
            }
1000
        }
1001
1002 10
        return self::create($result, $this->iteratorClass, false);
1003
    }
1004
1005
    /**
1006
     * Sort an array in reverse order and maintain index association.
1007
     *
1008
     * @return $this
1009
     *               <p>(Mutable) Return this Arrayy object.</p>
1010
     *
1011
     * @psalm-return static<TKey,T>
1012
     */
1013 4
    public function arsort(): self
1014
    {
1015 4
        $this->generatorToArray();
1016
1017 4
        \arsort($this->array);
1018
1019 4
        return $this;
1020
    }
1021
1022
    /**
1023
     * Sort an array in reverse order and maintain index association.
1024
     *
1025
     * @return $this
1026
     *               <p>(Immutable) Return this Arrayy object.</p>
1027
     *
1028
     * @psalm-return static<TKey,T>
1029
     * @psalm-mutation-free
1030
     */
1031 10
    public function arsortImmutable(): self
1032
    {
1033 10
        $that = clone $this;
1034
1035 10
        $that->generatorToArray();
1036
1037 10
        \arsort($that->array);
1038
1039 10
        return $that;
1040
    }
1041
1042
    /**
1043
     * Iterate over the current array and execute a callback for each loop.
1044
     *
1045
     * @param \Closure $closure
1046
     *
1047
     * @return static
1048
     *                <p>(Immutable)</p>
1049
     *
1050
     * @psalm-return static<TKey,T>
1051
     * @psalm-mutation-free
1052
     */
1053 2
    public function at(\Closure $closure): self
1054
    {
1055 2
        $that = clone $this;
1056
1057 2
        foreach ($that->getGenerator() as $key => $value) {
1058 2
            $closure($value, $key);
1059
        }
1060
1061 2
        return static::create(
1062 2
            $that->toArray(),
1063 2
            $this->iteratorClass,
1064 2
            false
1065
        );
1066
    }
1067
1068
    /**
1069
     * Returns the average value of the current array.
1070
     *
1071
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1072
     *
1073
     * @return float|int
1074
     *                   <p>The average value.</p>
1075
     * @psalm-mutation-free
1076
     */
1077 10
    public function average($decimals = 0)
1078
    {
1079 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1080
1081 10
        if (!$count) {
1082 2
            return 0;
1083
        }
1084
1085 8
        if ((int) $decimals !== $decimals) {
1086 3
            $decimals = 0;
1087
        }
1088
1089 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1090
    }
1091
1092
    /**
1093
     * Changes all keys in an array.
1094
     *
1095
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1096
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1097
     *
1098
     * @return static
1099
     *                <p>(Immutable)</p>
1100
     *
1101
     * @psalm-return static<TKey,T>
1102
     * @psalm-mutation-free
1103
     */
1104 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1105
    {
1106
        if (
1107 1
            $case !== \CASE_LOWER
1108
            &&
1109 1
            $case !== \CASE_UPPER
1110
        ) {
1111
            $case = \CASE_LOWER;
1112
        }
1113
1114 1
        $return = [];
1115 1
        foreach ($this->getGenerator() as $key => $value) {
1116 1
            if ($case === \CASE_LOWER) {
1117 1
                $key = \mb_strtolower((string) $key);
1118
            } else {
1119 1
                $key = \mb_strtoupper((string) $key);
1120
            }
1121
1122 1
            $return[$key] = $value;
1123
        }
1124
1125 1
        return static::create(
1126 1
            $return,
1127 1
            $this->iteratorClass,
1128 1
            false
1129
        );
1130
    }
1131
1132
    /**
1133
     * Change the path separator of the array wrapper.
1134
     *
1135
     * By default, the separator is: "."
1136
     *
1137
     * @param string $separator <p>Separator to set.</p>
1138
     *
1139
     * @return $this
1140
     *               <p>(Mutable) Return this Arrayy object.</p>
1141
     *
1142
     * @psalm-return static<TKey,T>
1143
     */
1144 11
    public function changeSeparator($separator): self
1145
    {
1146 11
        $this->pathSeparator = $separator;
1147
1148 11
        return $this;
1149
    }
1150
1151
    /**
1152
     * Create a chunked version of the current array.
1153
     *
1154
     * @param int  $size         <p>Size of each chunk.</p>
1155
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1156
     *
1157
     * @return static
1158
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1159
     *
1160
     * @psalm-return static<TKey,T>
1161
     * @psalm-mutation-free
1162
     */
1163 5
    public function chunk($size, $preserveKeys = false): self
1164
    {
1165 5
        return static::create(
1166 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1167 5
            $this->iteratorClass,
1168 5
            false
1169
        );
1170
    }
1171
1172
    /**
1173
     * Clean all falsy values from the current array.
1174
     *
1175
     * @return static
1176
     *                <p>(Immutable)</p>
1177
     *
1178
     * @psalm-return static<TKey,T>
1179
     * @psalm-mutation-free
1180
     */
1181 8
    public function clean(): self
1182
    {
1183 8
        return $this->filter(
1184 8
            static function ($value) {
1185 7
                return (bool) $value;
1186 8
            }
1187
        );
1188
    }
1189
1190
    /**
1191
     * WARNING!!! -> Clear the current full array or a $key of it.
1192
     *
1193
     * @param int|int[]|string|string[]|null $key
1194
     *
1195
     * @return $this
1196
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1197
     *
1198
     * @psalm-return static<TKey,T>
1199
     */
1200 10
    public function clear($key = null): self
1201
    {
1202 10
        if ($key !== null) {
1203 3
            if (\is_array($key)) {
1204 1
                foreach ($key as $keyTmp) {
1205 1
                    $this->offsetUnset($keyTmp);
1206
                }
1207
            } else {
1208 2
                $this->offsetUnset($key);
1209
            }
1210
1211 3
            return $this;
1212
        }
1213
1214 7
        $this->array = [];
1215 7
        $this->generator = null;
1216
1217 7
        return $this;
1218
    }
1219
1220
    /**
1221
     * Check if an item is in the current array.
1222
     *
1223
     * @param float|int|string $value
1224
     * @param bool             $recursive
1225
     * @param bool             $strict
1226
     *
1227
     * @return bool
1228
     * @psalm-mutation-free
1229
     */
1230 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1231
    {
1232 23
        if ($recursive === true) {
1233 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1234
        }
1235
1236 14
        foreach ($this->getGenerator() as $valueFromArray) {
1237 11
            if ($strict) {
1238 11
                if ($value === $valueFromArray) {
1239 11
                    return true;
1240
                }
1241
            } else {
1242
                /** @noinspection NestedPositiveIfStatementsInspection */
1243
                if ($value == $valueFromArray) {
1244 7
                    return true;
1245
                }
1246
            }
1247
        }
1248
1249 7
        return false;
1250
    }
1251
1252
    /**
1253
     * Check if an (case-insensitive) string is in the current array.
1254
     *
1255
     * @param mixed $value
1256
     * @param bool  $recursive
1257
     *
1258
     * @return bool
1259
     * @psalm-mutation-free
1260
     *
1261
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1262
     */
1263 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1264
    {
1265 26
        if ($value === null) {
1266 2
            return false;
1267
        }
1268
1269 24
        if ($recursive === true) {
1270 24
            foreach ($this->getGenerator() as $key => $valueTmp) {
1271 22
                if (\is_array($valueTmp) === true) {
1272 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1273 5
                    if ($return === true) {
1274 5
                        return $return;
1275
                    }
1276 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1277 22
                    return true;
1278
                }
1279
            }
1280
1281 8
            return false;
1282
        }
1283
1284 12
        foreach ($this->getGenerator() as $key => $valueTmp) {
1285 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1286 11
                return true;
1287
            }
1288
        }
1289
1290 4
        return false;
1291
    }
1292
1293
    /**
1294
     * Check if the given key/index exists in the array.
1295
     *
1296
     * @param int|string $key <p>key/index to search for</p>
1297
     *
1298
     * @return bool
1299
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1300
     *
1301
     * @psalm-mutation-free
1302
     */
1303 4
    public function containsKey($key): bool
1304
    {
1305 4
        return $this->offsetExists($key);
1306
    }
1307
1308
    /**
1309
     * Check if all given needles are present in the array as key/index.
1310
     *
1311
     * @param array $needles   <p>The keys you are searching for.</p>
1312
     * @param bool  $recursive
1313
     *
1314
     * @return bool
1315
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1316
     *
1317
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1318
     * @psalm-mutation-free
1319
     */
1320 2
    public function containsKeys(array $needles, $recursive = false): bool
1321
    {
1322 2
        if ($recursive === true) {
1323
            return
1324 2
                \count(
1325 2
                    \array_intersect(
1326 2
                        $needles,
1327 2
                        $this->keys(true)->toArray()
1328
                    ),
1329 2
                    \COUNT_RECURSIVE
1330
                )
1331
                ===
1332 2
                \count(
1333 2
                    $needles,
1334 2
                    \COUNT_RECURSIVE
1335
                );
1336
        }
1337
1338 1
        return \count(
1339 1
            \array_intersect($needles, $this->keys()->toArray()),
1340 1
            \COUNT_NORMAL
1341
        )
1342
               ===
1343 1
               \count(
1344 1
                   $needles,
1345 1
                   \COUNT_NORMAL
1346
               );
1347
    }
1348
1349
    /**
1350
     * Check if all given needles are present in the array as key/index.
1351
     *
1352
     * @param array $needles <p>The keys you are searching for.</p>
1353
     *
1354
     * @return bool
1355
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1356
     *
1357
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1358
     * @psalm-mutation-free
1359
     */
1360 1
    public function containsKeysRecursive(array $needles): bool
1361
    {
1362 1
        return $this->containsKeys($needles, true);
1363
    }
1364
1365
    /**
1366
     * alias: for "Arrayy->contains()"
1367
     *
1368
     * @param float|int|string $value
1369
     *
1370
     * @return bool
1371
     *
1372
     * @see Arrayy::contains()
1373
     * @psalm-mutation-free
1374
     */
1375 9
    public function containsValue($value): bool
1376
    {
1377 9
        return $this->contains($value);
1378
    }
1379
1380
    /**
1381
     * alias: for "Arrayy->contains($value, true)"
1382
     *
1383
     * @param float|int|string $value
1384
     *
1385
     * @return bool
1386
     *
1387
     * @see Arrayy::contains()
1388
     * @psalm-mutation-free
1389
     */
1390 18
    public function containsValueRecursive($value): bool
1391
    {
1392 18
        return $this->contains($value, true);
1393
    }
1394
1395
    /**
1396
     * Check if all given needles are present in the array.
1397
     *
1398
     * @param array $needles
1399
     *
1400
     * @return bool
1401
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1402
     *
1403
     * @psalm-param array<mixed>|array<T> $needles
1404
     * @psalm-mutation-free
1405
     */
1406 1
    public function containsValues(array $needles): bool
1407
    {
1408 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1409
               ===
1410 1
               \count($needles, \COUNT_NORMAL);
1411
    }
1412
1413
    /**
1414
     * Counts all the values of an array
1415
     *
1416
     * @see          http://php.net/manual/en/function.array-count-values.php
1417
     *
1418
     * @return static
1419
     *                <p>
1420
     *                (Immutable)
1421
     *                An associative Arrayy-object of values from input as
1422
     *                keys and their count as value.
1423
     *                </p>
1424
     *
1425
     * @psalm-return static<TKey,T>
1426
     * @psalm-mutation-free
1427
     */
1428 7
    public function countValues(): self
1429
    {
1430 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1431
    }
1432
1433
    /**
1434
     * Creates an Arrayy object.
1435
     *
1436
     * @param mixed  $data
1437
     * @param string $iteratorClass
1438
     * @param bool   $checkPropertiesInConstructor
1439
     *
1440
     * @return static
1441
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1442
     *
1443
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1444
     *
1445
     * @psalm-mutation-free
1446
     */
1447 693
    public static function create(
1448
        $data = [],
1449
        string $iteratorClass = ArrayyIterator::class,
1450
        bool $checkPropertiesInConstructor = true
1451
    ) {
1452 693
        return new static(
1453 693
            $data,
1454 693
            $iteratorClass,
1455 693
            $checkPropertiesInConstructor
1456
        );
1457
    }
1458
1459
    /**
1460
     * Flatten an array with the given character as a key delimiter
1461
     *
1462
     * @param string     $delimiter
1463
     * @param string     $prepend
1464
     * @param array|null $items
1465
     *
1466
     * @return array
1467
     */
1468 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1469
    {
1470
        // init
1471 2
        $flatten = [];
1472
1473 2
        if ($items === null) {
1474 2
            $items = $this->array;
1475
        }
1476
1477 2
        foreach ($items as $key => $value) {
1478 2
            if (\is_array($value) && !empty($value)) {
1479 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1480
            } else {
1481 2
                $flatten[] = [$prepend . $key => $value];
1482
            }
1483
        }
1484
1485 2
        if (\count($flatten) === 0) {
1486
            return [];
1487
        }
1488
1489 2
        return \array_merge_recursive([], ...$flatten);
1490
    }
1491
1492
    /**
1493
     * WARNING: Creates an Arrayy object by reference.
1494
     *
1495
     * @param array $array
1496
     *
1497
     * @return $this
1498
     *               <p>(Mutable) Return this Arrayy object.</p>
1499
     *
1500
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1501
     */
1502 2
    public function createByReference(array &$array = []): self
1503
    {
1504 2
        $array = $this->fallbackForArray($array);
1505
1506 2
        $this->array = &$array;
1507
1508 2
        return $this;
1509
    }
1510
1511
    /**
1512
     * Create an new instance from a callable function which will return an Generator.
1513
     *
1514
     * @param callable $generatorFunction
1515
     *
1516
     * @return static
1517
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1518
     *
1519
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1520
     *
1521
     * @psalm-mutation-free
1522
     */
1523 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1524
    {
1525 7
        return self::create($generatorFunction);
1526
    }
1527
1528
    /**
1529
     * Create an new instance filled with a copy of values from a "Generator"-object.
1530
     *
1531
     * @param \Generator $generator
1532
     *
1533
     * @return static
1534
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1535
     *
1536
     * @psalm-param \Generator<array-key,mixed> $generator
1537
     *
1538
     * @psalm-mutation-free
1539
     */
1540 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1541
    {
1542 4
        return self::create(\iterator_to_array($generator, true));
1543
    }
1544
1545
    /**
1546
     * Create an new Arrayy object via JSON.
1547
     *
1548
     * @param string $json
1549
     *
1550
     * @return static
1551
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1552
     *
1553
     * @psalm-mutation-free
1554
     */
1555 5
    public static function createFromJson(string $json): self
1556
    {
1557 5
        return static::create(\json_decode($json, true));
1558
    }
1559
1560
    /**
1561
     * Create an new Arrayy object via JSON.
1562
     *
1563
     * @param array $array
1564
     *
1565
     * @return static
1566
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1567
     *
1568
     * @psalm-mutation-free
1569
     */
1570 1
    public static function createFromArray(array $array): self
1571
    {
1572 1
        return static::create($array);
1573
    }
1574
1575
    /**
1576
     * Create an new instance filled with values from an object that is iterable.
1577
     *
1578
     * @param \Traversable $object <p>iterable object</p>
1579
     *
1580
     * @return static
1581
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1582
     *
1583
     * @psalm-param \Traversable<array-key,mixed> $object
1584
     *
1585
     * @psalm-mutation-free
1586
     */
1587 4
    public static function createFromObject(\Traversable $object): self
1588
    {
1589
        // init
1590 4
        $arrayy = new static();
1591
1592 4
        if ($object instanceof self) {
1593 4
            $objectArray = $object->getGenerator();
1594
        } else {
1595
            $objectArray = $object;
1596
        }
1597
1598 4
        foreach ($objectArray as $key => $value) {
1599
            /**
1600
             * @psalm-suppress ImpureMethodCall - object is already re-created
1601
             */
1602 3
            $arrayy->internalSet($key, $value);
1603
        }
1604
1605 4
        return $arrayy;
1606
    }
1607
1608
    /**
1609
     * Create an new instance filled with values from an object.
1610
     *
1611
     * @param object $object
1612
     *
1613
     * @return static
1614
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1615
     *
1616
     * @psalm-mutation-free
1617
     */
1618 5
    public static function createFromObjectVars($object): self
1619
    {
1620 5
        return self::create(self::objectToArray($object));
1621
    }
1622
1623
    /**
1624
     * Create an new Arrayy object via string.
1625
     *
1626
     * @param string      $str       <p>The input string.</p>
1627
     * @param string|null $delimiter <p>The boundary string.</p>
1628
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1629
     *                               used.</p>
1630
     *
1631
     * @return static
1632
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1633
     *
1634
     * @psalm-mutation-free
1635
     */
1636 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1637
    {
1638 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...
1639 1
            \preg_match_all($regEx, $str, $array);
1640
1641 1
            if (!empty($array)) {
1642 1
                $array = $array[0];
1643
            }
1644
        } else {
1645
            /** @noinspection NestedPositiveIfStatementsInspection */
1646 9
            if ($delimiter !== null) {
1647 7
                $array = \explode($delimiter, $str);
1648
            } else {
1649 2
                $array = [$str];
1650
            }
1651
        }
1652
1653
        // trim all string in the array
1654
        /**
1655
         * @psalm-suppress MissingClosureParamType
1656
         */
1657 10
        \array_walk(
1658 10
            $array,
1659 10
            static function (&$val) {
1660 10
                if ((string) $val === $val) {
1661 10
                    $val = \trim($val);
1662
                }
1663 10
            }
1664
        );
1665
1666 10
        return static::create($array);
1667
    }
1668
1669
    /**
1670
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1671
     *
1672
     * @param \Traversable $traversable
1673
     *
1674
     * @return static
1675
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1676
     *
1677
     * @psalm-param \Traversable<array-key,mixed> $traversable
1678
     *
1679
     * @psalm-mutation-free
1680
     */
1681 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1682
    {
1683 1
        return self::create(\iterator_to_array($traversable, true));
1684
    }
1685
1686
    /**
1687
     * Create an new instance containing a range of elements.
1688
     *
1689
     * @param float|int|string $low  <p>First value of the sequence.</p>
1690
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1691
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1692
     *
1693
     * @return static
1694
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1695
     *
1696
     * @psalm-mutation-free
1697
     */
1698 2
    public static function createWithRange($low, $high, $step = 1): self
1699
    {
1700 2
        return static::create(\range($low, $high, $step));
1701
    }
1702
1703
    /**
1704
     * Gets the element of the array at the current internal iterator position.
1705
     *
1706
     * @return false|mixed
1707
     */
1708
    public function current()
1709
    {
1710
        return \current($this->array);
1711
    }
1712
1713
    /**
1714
     * Custom sort by index via "uksort".
1715
     *
1716
     * @see          http://php.net/manual/en/function.uksort.php
1717
     *
1718
     * @param callable $function
1719
     *
1720
     * @throws \InvalidArgumentException
1721
     *
1722
     * @return $this
1723
     *               <p>(Mutable) Return this Arrayy object.</p>
1724
     *
1725
     * @psalm-return static<TKey,T>
1726
     */
1727 5
    public function customSortKeys(callable $function): self
1728
    {
1729 5
        $this->generatorToArray();
1730
1731 5
        \uksort($this->array, $function);
1732
1733 5
        return $this;
1734
    }
1735
1736
    /**
1737
     * Custom sort by index via "uksort".
1738
     *
1739
     * @see          http://php.net/manual/en/function.uksort.php
1740
     *
1741
     * @param callable $function
1742
     *
1743
     * @throws \InvalidArgumentException
1744
     *
1745
     * @return $this
1746
     *               <p>(Immutable) Return this Arrayy object.</p>
1747
     *
1748
     * @psalm-return static<TKey,T>
1749
     * @psalm-mutation-free
1750
     */
1751 1
    public function customSortKeysImmutable(callable $function): self
1752
    {
1753 1
        $that = clone $this;
1754
1755 1
        $that->generatorToArray();
1756
1757
        /**
1758
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1759
         */
1760 1
        \uksort($that->array, $function);
1761
1762 1
        return $that;
1763
    }
1764
1765
    /**
1766
     * Custom sort by value via "usort".
1767
     *
1768
     * @see          http://php.net/manual/en/function.usort.php
1769
     *
1770
     * @param callable $function
1771
     *
1772
     * @throws \InvalidArgumentException
1773
     *
1774
     * @return $this
1775
     *               <p>(Mutable) Return this Arrayy object.</p>
1776
     *
1777
     * @psalm-return static<TKey,T>
1778
     */
1779 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...
1780
    {
1781 10
        if (\is_callable($function) === false) {
1782
            throw new \InvalidArgumentException('Passed function must be callable');
1783
        }
1784
1785 10
        $this->generatorToArray();
1786
1787 10
        \usort($this->array, $function);
1788
1789 10
        return $this;
1790
    }
1791
1792
    /**
1793
     * Custom sort by value via "usort".
1794
     *
1795
     * @see          http://php.net/manual/en/function.usort.php
1796
     *
1797
     * @param callable $function
1798
     *
1799
     * @throws \InvalidArgumentException
1800
     *
1801
     * @return $this
1802
     *               <p>(Immutable) Return this Arrayy object.</p>
1803
     *
1804
     * @psalm-return static<TKey,T>
1805
     * @psalm-mutation-free
1806
     */
1807 4
    public function customSortValuesImmutable($function): self
1808
    {
1809 4
        $that = clone $this;
1810
1811
        /**
1812
         * @psalm-suppress ImpureMethodCall - object is already cloned
1813
         */
1814 4
        $that->customSortValues($function);
1815
1816 4
        return $that;
1817
    }
1818
1819
    /**
1820
     * Delete the given key or keys.
1821
     *
1822
     * @param int|int[]|string|string[] $keyOrKeys
1823
     *
1824
     * @return void
1825
     */
1826 9
    public function delete($keyOrKeys)
1827
    {
1828 9
        $keyOrKeys = (array) $keyOrKeys;
1829
1830 9
        foreach ($keyOrKeys as $key) {
1831 9
            $this->offsetUnset($key);
1832
        }
1833 9
    }
1834
1835
    /**
1836
     * Return values that are only in the current array.
1837
     *
1838
     * @param array ...$array
1839
     *
1840
     * @return static
1841
     *                <p>(Immutable)</p>
1842
     *
1843
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1844
     * @psalm-return static<TKey,T>
1845
     * @psalm-mutation-free
1846
     */
1847 13
    public function diff(...$array): self
1848
    {
1849 13
        return static::create(
1850 13
            \array_diff($this->toArray(), ...$array),
1851 13
            $this->iteratorClass,
1852 13
            false
1853
        );
1854
    }
1855
1856
    /**
1857
     * Return values that are only in the current array.
1858
     *
1859
     * @param array ...$array
1860
     *
1861
     * @return static
1862
     *                <p>(Immutable)</p>
1863
     *
1864
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1865
     * @psalm-return static<TKey,T>
1866
     * @psalm-mutation-free
1867
     */
1868 8
    public function diffKey(...$array): self
1869
    {
1870 8
        return static::create(
1871 8
            \array_diff_key($this->toArray(), ...$array),
1872 8
            $this->iteratorClass,
1873 8
            false
1874
        );
1875
    }
1876
1877
    /**
1878
     * Return values and Keys that are only in the current array.
1879
     *
1880
     * @param array $array
1881
     *
1882
     * @return static
1883
     *                <p>(Immutable)</p>
1884
     *
1885
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1886
     * @psalm-return static<TKey,T>
1887
     * @psalm-mutation-free
1888
     */
1889 8
    public function diffKeyAndValue(array $array = []): self
1890
    {
1891 8
        return static::create(
1892 8
            \array_diff_assoc($this->toArray(), $array),
1893 8
            $this->iteratorClass,
1894 8
            false
1895
        );
1896
    }
1897
1898
    /**
1899
     * Return values that are only in the current multi-dimensional array.
1900
     *
1901
     * @param array                 $array
1902
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
1903
     *
1904
     * @return static
1905
     *                <p>(Immutable)</p>
1906
     *
1907
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1908
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
1909
     * @psalm-return static<TKey,T>
1910
     * @psalm-mutation-free
1911
     */
1912 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1913
    {
1914
        // init
1915 1
        $result = [];
1916
1917
        if (
1918 1
            $helperVariableForRecursion !== null
1919
            &&
1920 1
            \is_array($helperVariableForRecursion) === true
1921
        ) {
1922
            $arrayForTheLoop = $helperVariableForRecursion;
1923
        } else {
1924 1
            $arrayForTheLoop = $this->getGenerator();
1925
        }
1926
1927 1
        foreach ($arrayForTheLoop as $key => $value) {
1928 1
            if ($value instanceof self) {
1929
                $value = $value->toArray();
1930
            }
1931
1932 1
            if (\array_key_exists($key, $array)) {
1933 1
                if ($value !== $array[$key]) {
1934 1
                    $result[$key] = $value;
1935
                }
1936
            } else {
1937 1
                $result[$key] = $value;
1938
            }
1939
        }
1940
1941 1
        return static::create(
1942 1
            $result,
1943 1
            $this->iteratorClass,
1944 1
            false
1945
        );
1946
    }
1947
1948
    /**
1949
     * Return values that are only in the new $array.
1950
     *
1951
     * @param array $array
1952
     *
1953
     * @return static
1954
     *                <p>(Immutable)</p>
1955
     *
1956
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1957
     * @psalm-return static<TKey,T>
1958
     * @psalm-mutation-free
1959
     */
1960 8
    public function diffReverse(array $array = []): self
1961
    {
1962 8
        return static::create(
1963 8
            \array_diff($array, $this->toArray()),
1964 8
            $this->iteratorClass,
1965 8
            false
1966
        );
1967
    }
1968
1969
    /**
1970
     * Divide an array into two arrays. One with keys and the other with values.
1971
     *
1972
     * @return static
1973
     *                <p>(Immutable)</p>
1974
     *
1975
     * @psalm-return static<TKey,T>
1976
     * @psalm-mutation-free
1977
     */
1978 1
    public function divide(): self
1979
    {
1980 1
        return static::create(
1981
            [
1982 1
                $this->keys(),
1983 1
                $this->values(),
1984
            ],
1985 1
            $this->iteratorClass,
1986 1
            false
1987
        );
1988
    }
1989
1990
    /**
1991
     * Iterate over the current array and modify the array's value.
1992
     *
1993
     * @param \Closure $closure
1994
     *
1995
     * @return static
1996
     *                <p>(Immutable)</p>
1997
     *
1998
     * @psalm-return static<TKey,T>
1999
     * @psalm-mutation-free
2000
     */
2001 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...
2002
    {
2003
        // init
2004 5
        $array = [];
2005
2006 5
        foreach ($this->getGenerator() as $key => $value) {
2007 5
            $array[$key] = $closure($value, $key);
2008
        }
2009
2010 5
        return static::create(
2011 5
            $array,
2012 5
            $this->iteratorClass,
2013 5
            false
2014
        );
2015
    }
2016
2017
    /**
2018
     * Sets the internal iterator to the last element in the array and returns this element.
2019
     *
2020
     * @return mixed
2021
     */
2022
    public function end()
2023
    {
2024
        return \end($this->array);
2025
    }
2026
2027
    /**
2028
     * Check if a value is in the current array using a closure.
2029
     *
2030
     * @param \Closure $closure
2031
     *
2032
     * @return bool
2033
     *              <p>Returns true if the given value is found, false otherwise.</p>
2034
     */
2035 4
    public function exists(\Closure $closure): bool
2036
    {
2037
        // init
2038 4
        $isExists = false;
2039
2040 4
        foreach ($this->getGenerator() as $key => $value) {
2041 3
            if ($closure($value, $key)) {
2042 1
                $isExists = true;
2043
2044 3
                break;
2045
            }
2046
        }
2047
2048 4
        return $isExists;
2049
    }
2050
2051
    /**
2052
     * Fill the array until "$num" with "$default" values.
2053
     *
2054
     * @param int   $num
2055
     * @param mixed $default
2056
     *
2057
     * @return static
2058
     *                <p>(Immutable)</p>
2059
     *
2060
     * @psalm-return static<TKey,T>
2061
     * @psalm-mutation-free
2062
     */
2063 8
    public function fillWithDefaults(int $num, $default = null): self
2064
    {
2065 8
        if ($num < 0) {
2066 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2067
        }
2068
2069 7
        $this->generatorToArray();
2070
2071 7
        $tmpArray = $this->array;
2072
2073 7
        $count = \count($tmpArray);
2074
2075 7
        while ($count < $num) {
2076 4
            $tmpArray[] = $default;
2077 4
            ++$count;
2078
        }
2079
2080 7
        return static::create(
2081 7
            $tmpArray,
2082 7
            $this->iteratorClass,
2083 7
            false
2084
        );
2085
    }
2086
2087
    /**
2088
     * Find all items in an array that pass the truth test.
2089
     *
2090
     * @param \Closure|null $closure [optional] <p>
2091
     *                               The callback function to use
2092
     *                               </p>
2093
     *                               <p>
2094
     *                               If no callback is supplied, all entries of
2095
     *                               input equal to false (see
2096
     *                               converting to
2097
     *                               boolean) will be removed.
2098
     *                               </p>
2099
     * @param int           $flag    [optional] <p>
2100
     *                               Flag determining what arguments are sent to <i>callback</i>:
2101
     *                               </p><ul>
2102
     *                               <li>
2103
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
2104
     *                               to <i>callback</i> instead of the value</span>
2105
     *                               </li>
2106
     *                               <li>
2107
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
2108
     *                               arguments to <i>callback</i> instead of the value</span>
2109
     *                               </li>
2110
     *                               </ul>
2111
     *
2112
     * @return static
2113
     *                <p>(Immutable)</p>
2114
     *
2115
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
2116
     * @psalm-return static<TKey,T>
2117
     * @psalm-mutation-free
2118
     */
2119 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2120
    {
2121 12
        if (!$closure) {
2122 1
            return $this->clean();
2123
        }
2124
2125 12
        return static::create(
2126 12
            \array_filter($this->toArray(), $closure, $flag),
2127 12
            $this->iteratorClass,
2128 12
            false
2129
        );
2130
    }
2131
2132
    /**
2133
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2134
     * property within that.
2135
     *
2136
     * @param string          $property
2137
     * @param string|string[] $value
2138
     * @param string          $comparisonOp
2139
     *                                      <p>
2140
     *                                      'eq' (equals),<br />
2141
     *                                      'gt' (greater),<br />
2142
     *                                      'gte' || 'ge' (greater or equals),<br />
2143
     *                                      'lt' (less),<br />
2144
     *                                      'lte' || 'le' (less or equals),<br />
2145
     *                                      'ne' (not equals),<br />
2146
     *                                      'contains',<br />
2147
     *                                      'notContains',<br />
2148
     *                                      'newer' (via strtotime),<br />
2149
     *                                      'older' (via strtotime),<br />
2150
     *                                      </p>
2151
     *
2152
     * @return static
2153
     *                <p>(Immutable)</p>
2154
     *
2155
     * @psalm-return static<TKey,T>
2156
     * @psalm-mutation-free
2157
     *
2158
     * @psalm-suppress MissingClosureReturnType
2159
     * @psalm-suppress MissingClosureParamType
2160
     */
2161 1
    public function filterBy(
2162
        string $property,
2163
        $value,
2164
        string $comparisonOp = null
2165
    ): self {
2166 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...
2167 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2168
        }
2169
2170
        $ops = [
2171 1
            'eq' => static function ($item, $prop, $value): bool {
2172 1
                return $item[$prop] === $value;
2173 1
            },
2174 1
            'gt' => static function ($item, $prop, $value): bool {
2175
                return $item[$prop] > $value;
2176 1
            },
2177 1
            'ge' => static function ($item, $prop, $value): bool {
2178
                return $item[$prop] >= $value;
2179 1
            },
2180 1
            'gte' => static function ($item, $prop, $value): bool {
2181
                return $item[$prop] >= $value;
2182 1
            },
2183 1
            'lt' => static function ($item, $prop, $value): bool {
2184 1
                return $item[$prop] < $value;
2185 1
            },
2186 1
            'le' => static function ($item, $prop, $value): bool {
2187
                return $item[$prop] <= $value;
2188 1
            },
2189 1
            'lte' => static function ($item, $prop, $value): bool {
2190
                return $item[$prop] <= $value;
2191 1
            },
2192 1
            'ne' => static function ($item, $prop, $value): bool {
2193
                return $item[$prop] !== $value;
2194 1
            },
2195 1
            'contains' => static function ($item, $prop, $value): bool {
2196 1
                return \in_array($item[$prop], (array) $value, true);
2197 1
            },
2198 1
            'notContains' => static function ($item, $prop, $value): bool {
2199
                return !\in_array($item[$prop], (array) $value, true);
2200 1
            },
2201 1
            'newer' => static function ($item, $prop, $value): bool {
2202
                return \strtotime($item[$prop]) > \strtotime($value);
2203 1
            },
2204 1
            'older' => static function ($item, $prop, $value): bool {
2205
                return \strtotime($item[$prop]) < \strtotime($value);
2206 1
            },
2207
        ];
2208
2209 1
        $result = \array_values(
2210 1
            \array_filter(
2211 1
                $this->toArray(false, true),
2212 1
                static function ($item) use (
2213 1
                    $property,
2214 1
                    $value,
2215 1
                    $ops,
2216 1
                    $comparisonOp
2217
                ) {
2218 1
                    $item = (array) $item;
2219 1
                    $itemArrayy = static::create($item);
2220 1
                    $item[$property] = $itemArrayy->get($property, []);
2221
2222 1
                    return $ops[$comparisonOp]($item, $property, $value);
2223 1
                }
2224
            )
2225
        );
2226
2227 1
        return static::create(
2228 1
            $result,
2229 1
            $this->iteratorClass,
2230 1
            false
2231
        );
2232
    }
2233
2234
    /**
2235
     * Find the first item in an array that passes the truth test,
2236
     *  otherwise return false
2237
     *
2238
     * @param \Closure $closure
2239
     *
2240
     * @return false|mixed
2241
     *                     <p>Return false if we did not find the value.</p>
2242
     */
2243 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...
2244
    {
2245 8
        foreach ($this->getGenerator() as $key => $value) {
2246 6
            if ($closure($value, $key)) {
2247 6
                return $value;
2248
            }
2249
        }
2250
2251 3
        return false;
2252
    }
2253
2254
    /**
2255
     * find by ...
2256
     *
2257
     * @param string          $property
2258
     * @param string|string[] $value
2259
     * @param string          $comparisonOp
2260
     *
2261
     * @return static
2262
     *                <p>(Immutable)</p>
2263
     *
2264
     * @psalm-return static<TKey,T>
2265
     * @psalm-mutation-free
2266
     */
2267 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2268
    {
2269 1
        return $this->filterBy($property, $value, $comparisonOp);
2270
    }
2271
2272
    /**
2273
     * Get the first value from the current array.
2274
     *
2275
     * @return mixed
2276
     *               <p>Return null if there wasn't a element.</p>
2277
     */
2278 21
    public function first()
2279
    {
2280 21
        $key_first = $this->firstKey();
2281 21
        if ($key_first === null) {
2282 3
            return null;
2283
        }
2284
2285 18
        return $this->get($key_first);
2286
    }
2287
2288
    /**
2289
     * Get the first key from the current array.
2290
     *
2291
     * @return mixed
2292
     *               <p>Return null if there wasn't a element.</p>
2293
     * @psalm-mutation-free
2294
     */
2295 28
    public function firstKey()
2296
    {
2297 28
        $this->generatorToArray();
2298
2299 28
        return \array_key_first($this->array);
2300
    }
2301
2302
    /**
2303
     * Get the first value(s) from the current array.
2304
     * And will return an empty array if there was no first entry.
2305
     *
2306
     * @param int|null $number <p>How many values you will take?</p>
2307
     *
2308
     * @return static
2309
     *                <p>(Immutable)</p>
2310
     *
2311
     * @psalm-return static<TKey,T>
2312
     * @psalm-mutation-free
2313
     */
2314 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...
2315
    {
2316 37
        $arrayTmp = $this->toArray();
2317
2318 37
        if ($number === null) {
2319 14
            $array = (array) \array_shift($arrayTmp);
2320
        } else {
2321 23
            $number = (int) $number;
2322 23
            $array = \array_splice($arrayTmp, 0, $number);
2323
        }
2324
2325 37
        return static::create(
2326 37
            $array,
2327 37
            $this->iteratorClass,
2328 37
            false
2329
        );
2330
    }
2331
2332
    /**
2333
     * Get the first value(s) from the current array.
2334
     * And will return an empty array if there was no first entry.
2335
     *
2336
     * @param int|null $number <p>How many values you will take?</p>
2337
     *
2338
     * @return static
2339
     *                <p>(Immutable)</p>
2340
     *
2341
     * @psalm-return static<TKey,T>
2342
     * @psalm-mutation-free
2343
     */
2344 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...
2345
    {
2346 3
        $arrayTmp = $this->keys()->toArray();
2347
2348 3
        if ($number === null) {
2349
            $array = (array) \array_shift($arrayTmp);
2350
        } else {
2351 3
            $number = (int) $number;
2352 3
            $array = \array_splice($arrayTmp, 0, $number);
2353
        }
2354
2355 3
        return static::create(
2356 3
            $array,
2357 3
            $this->iteratorClass,
2358 3
            false
2359
        );
2360
    }
2361
2362
    /**
2363
     * Get and rmove the first value(s) from the current array.
2364
     * And will return an empty array if there was no first entry.
2365
     *
2366
     * @param int|null $number <p>How many values you will take?</p>
2367
     *
2368
     * @return $this
2369
     *               <p>(Mutable)</p>
2370
     *
2371
     * @psalm-return static<TKey,T>
2372
     */
2373 34
    public function firstsMutable(int $number = null): self
2374
    {
2375 34
        $this->generatorToArray();
2376
2377 34
        if ($number === null) {
2378 19
            $this->array = (array) \array_shift($this->array);
2379
        } else {
2380 15
            $number = (int) $number;
2381 15
            $this->array = \array_splice($this->array, 0, $number);
2382
        }
2383
2384 34
        return $this;
2385
    }
2386
2387
    /**
2388
     * Exchanges all keys with their associated values in an array.
2389
     *
2390
     * @return static
2391
     *                <p>(Immutable)</p>
2392
     *
2393
     * @psalm-return static<TKey,T>
2394
     * @psalm-mutation-free
2395
     */
2396 1
    public function flip(): self
2397
    {
2398 1
        return static::create(
2399 1
            \array_flip($this->toArray()),
2400 1
            $this->iteratorClass,
2401 1
            false
2402
        );
2403
    }
2404
2405
    /**
2406
     * Get a value from an array (optional using dot-notation).
2407
     *
2408
     * @param mixed $key      <p>The key to look for.</p>
2409
     * @param mixed $fallback <p>Value to fallback to.</p>
2410
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
2411
     *                        class.</p>
2412
     *
2413
     * @return mixed|static
2414
     *
2415
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2416
     * @psalm-mutation-free
2417
     */
2418 219
    public function get($key, $fallback = null, array $array = null)
2419
    {
2420 219
        if ($array !== null) {
2421 4
            $usedArray = $array;
2422
        } else {
2423 216
            $this->generatorToArray();
2424
2425 216
            $usedArray = $this->array;
2426
        }
2427
2428 219
        if ($key === null) {
2429 1
            return static::create(
2430 1
                $usedArray,
2431 1
                $this->iteratorClass,
2432 1
                false
2433
            );
2434
        }
2435
2436
        // php cast "bool"-index into "int"-index
2437 219
        if ((bool) $key === $key) {
2438 3
            $key = (int) $key;
2439
        }
2440
2441 219
        if (\array_key_exists($key, $usedArray) === true) {
2442 182
            if (\is_array($usedArray[$key]) === true) {
2443 18
                return static::create(
2444 18
                    $usedArray[$key],
2445 18
                    $this->iteratorClass,
2446 18
                    false
2447
                );
2448
            }
2449
2450 168
            return $usedArray[$key];
2451
        }
2452
2453
        // crawl through array, get key according to object or not
2454 56
        $usePath = false;
2455
        if (
2456 56
            $this->pathSeparator
2457
            &&
2458 56
            (string) $key === $key
2459
            &&
2460 56
            \strpos($key, $this->pathSeparator) !== false
2461
        ) {
2462 30
            $segments = \explode($this->pathSeparator, (string) $key);
2463 30
            if ($segments !== false) {
2464 30
                $usePath = true;
2465
2466 30
                foreach ($segments as $segment) {
2467
                    if (
2468
                        (
2469 30
                            \is_array($usedArray) === true
2470
                            ||
2471 30
                            $usedArray instanceof \ArrayAccess
2472
                        )
2473
                        &&
2474 30
                        isset($usedArray[$segment])
2475
                    ) {
2476 30
                        $usedArray = $usedArray[$segment];
2477
2478 30
                        continue;
2479
                    }
2480
2481
                    if (
2482 12
                        \is_object($usedArray) === true
2483
                        &&
2484 12
                        \property_exists($usedArray, $segment)
2485
                    ) {
2486 1
                        $usedArray = $usedArray->{$segment};
2487
2488 1
                        continue;
2489
                    }
2490
2491 11
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2492
                }
2493
            }
2494
        }
2495
2496 54
        if (!$usePath && !isset($usedArray[$key])) {
2497 26
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2498
        }
2499
2500 28
        if (\is_array($usedArray) === true) {
2501 6
            return static::create(
2502 6
                $usedArray,
2503 6
                $this->iteratorClass,
2504 6
                false
2505
            );
2506
        }
2507
2508 28
        return $usedArray;
2509
    }
2510
2511
    /**
2512
     * alias: for "Arrayy->toArray()"
2513
     *
2514
     * @return array
2515
     *
2516
     * @see          Arrayy::getArray()
2517
     *
2518
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2519
     */
2520 12
    public function getAll(): array
2521
    {
2522 12
        return $this->toArray();
2523
    }
2524
2525
    /**
2526
     * Get the current array from the "Arrayy"-object.
2527
     *
2528
     * alias for "toArray()"
2529
     *
2530
     * @param bool $convertAllArrayyElements <p>
2531
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2532
     *                                       </p>
2533
     * @param bool $preserveKeys             <p>
2534
     *                                       e.g.: A generator maybe return the same key more then once,
2535
     *                                       so maybe you will ignore the keys.
2536
     *                                       </p>
2537
     *
2538
     * @return array
2539
     *
2540
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2541
     * @psalm-mutation-free
2542
     *
2543
     * @see Arrayy::toArray()
2544
     */
2545 494
    public function getArray(
2546
        bool $convertAllArrayyElements = false,
2547
        bool $preserveKeys = true
2548
    ): array {
2549 494
        return $this->toArray(
2550 494
            $convertAllArrayyElements,
2551 494
            $preserveKeys
2552
        );
2553
    }
2554
2555
    /**
2556
     * Get the current array from the "Arrayy"-object as list.
2557
     *
2558
     * alias for "toList()"
2559
     *
2560
     * @param bool $convertAllArrayyElements <p>
2561
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2562
     *                                       </p>
2563
     *
2564
     * @return array
2565
     *
2566
     * @psalm-return array<int,mixed>|array<int,T>
2567
     * @psalm-mutation-free
2568
     *
2569
     * @see Arrayy::toList()
2570
     */
2571 1
    public function getList(bool $convertAllArrayyElements = false): array
2572
    {
2573 1
        return $this->toList($convertAllArrayyElements);
2574
    }
2575
2576
    /**
2577
     * Returns the values from a single column of the input array, identified by
2578
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2579
     *
2580
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2581
     * array by the values from the $indexKey column in the input array.
2582
     *
2583
     * @param mixed $columnKey
2584
     * @param mixed $indexKey
2585
     *
2586
     * @return static
2587
     *                <p>(Immutable)</p>
2588
     *
2589
     * @psalm-return static<TKey,T>
2590
     * @psalm-mutation-free
2591
     */
2592 1
    public function getColumn($columnKey = null, $indexKey = null): self
2593
    {
2594 1
        return static::create(
2595 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2596 1
            $this->iteratorClass,
2597 1
            false
2598
        );
2599
    }
2600
2601
    /**
2602
     * Get the current array from the "Arrayy"-object as generator.
2603
     *
2604
     * @return \Generator
2605
     *
2606
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2607
     * @psalm-mutation-free
2608
     */
2609 1012
    public function getGenerator(): \Generator
2610
    {
2611 1012
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2612 42
            yield from $this->generator;
2613
        }
2614
2615 1012
        yield from $this->array;
2616 968
    }
2617
2618
    /**
2619
     * alias: for "Arrayy->keys()"
2620
     *
2621
     * @return static
2622
     *                <p>(Immutable)</p>
2623
     *
2624
     * @see          Arrayy::keys()
2625
     *
2626
     * @psalm-return static<array-key,TKey>
2627
     * @psalm-mutation-free
2628
     */
2629 2
    public function getKeys()
2630
    {
2631 2
        return $this->keys();
2632
    }
2633
2634
    /**
2635
     * Get the current array from the "Arrayy"-object as object.
2636
     *
2637
     * @return \stdClass
2638
     */
2639 4
    public function getObject(): \stdClass
2640
    {
2641 4
        return self::arrayToObject($this->toArray());
2642
    }
2643
2644
    /**
2645
     * alias: for "Arrayy->randomImmutable()"
2646
     *
2647
     * @return static
2648
     *                <p>(Immutable)</p>
2649
     *
2650
     * @see          Arrayy::randomImmutable()
2651
     *
2652
     * @psalm-return static<int|array-key,T>
2653
     */
2654 4
    public function getRandom(): self
2655
    {
2656 4
        return $this->randomImmutable();
2657
    }
2658
2659
    /**
2660
     * alias: for "Arrayy->randomKey()"
2661
     *
2662
     * @return mixed
2663
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2664
     *
2665
     * @see Arrayy::randomKey()
2666
     */
2667 3
    public function getRandomKey()
2668
    {
2669 3
        return $this->randomKey();
2670
    }
2671
2672
    /**
2673
     * alias: for "Arrayy->randomKeys()"
2674
     *
2675
     * @param int $number
2676
     *
2677
     * @return static
2678
     *                <p>(Immutable)</p>
2679
     *
2680
     * @see          Arrayy::randomKeys()
2681
     *
2682
     * @psalm-return static<TKey,T>
2683
     */
2684 8
    public function getRandomKeys(int $number): self
2685
    {
2686 8
        return $this->randomKeys($number);
2687
    }
2688
2689
    /**
2690
     * alias: for "Arrayy->randomValue()"
2691
     *
2692
     * @return mixed
2693
     *               <p>Get a random value or null if there wasn't a value.</p>
2694
     *
2695
     * @see Arrayy::randomValue()
2696
     */
2697 3
    public function getRandomValue()
2698
    {
2699 3
        return $this->randomValue();
2700
    }
2701
2702
    /**
2703
     * alias: for "Arrayy->randomValues()"
2704
     *
2705
     * @param int $number
2706
     *
2707
     * @return static
2708
     *                <p>(Immutable)</p>
2709
     *
2710
     * @see          Arrayy::randomValues()
2711
     *
2712
     * @psalm-return static<TKey,T>
2713
     */
2714 6
    public function getRandomValues(int $number): self
2715
    {
2716 6
        return $this->randomValues($number);
2717
    }
2718
2719
    /**
2720
     * Gets all values.
2721
     *
2722
     * @return static
2723
     *                <p>The values of all elements in this array, in the order they
2724
     *                appear in the array.</p>
2725
     *
2726
     * @psalm-return static<TKey,T>
2727
     */
2728 4
    public function getValues()
2729
    {
2730 4
        $this->generatorToArray(false);
2731
2732 4
        return static::create(
2733 4
            \array_values($this->array),
2734 4
            $this->iteratorClass,
2735 4
            false
2736
        );
2737
    }
2738
2739
    /**
2740
     * Gets all values via Generator.
2741
     *
2742
     * @return \Generator
2743
     *                    <p>The values of all elements in this array, in the order they
2744
     *                    appear in the array as Generator.</p>
2745
     *
2746
     * @psalm-return \Generator<TKey,T>
2747
     */
2748 4
    public function getValuesYield(): \Generator
2749
    {
2750 4
        yield from $this->getGenerator();
2751 4
    }
2752
2753
    /**
2754
     * Group values from a array according to the results of a closure.
2755
     *
2756
     * @param callable|string $grouper  <p>A callable function name.</p>
2757
     * @param bool            $saveKeys
2758
     *
2759
     * @return static
2760
     *                <p>(Immutable)</p>
2761
     *
2762
     * @psalm-return static<TKey,T>
2763
     * @psalm-mutation-free
2764
     */
2765 4
    public function group($grouper, bool $saveKeys = false): self
2766
    {
2767
        // init
2768 4
        $result = [];
2769
2770
        // Iterate over values, group by property/results from closure.
2771 4
        foreach ($this->getGenerator() as $key => $value) {
2772 4
            if (\is_callable($grouper) === true) {
2773 3
                $groupKey = $grouper($value, $key);
2774
            } else {
2775 1
                $groupKey = $this->get($grouper);
2776
            }
2777
2778 4
            $newValue = $this->get($groupKey, null, $result);
2779
2780 4
            if ($groupKey instanceof self) {
2781
                $groupKey = $groupKey->toArray();
2782
            }
2783
2784 4
            if ($newValue instanceof self) {
2785 4
                $newValue = $newValue->toArray();
2786
            }
2787
2788
            // Add to results.
2789 4
            if ($groupKey !== null) {
2790 3
                if ($saveKeys) {
2791 2
                    $result[$groupKey] = $newValue;
2792 2
                    $result[$groupKey][$key] = $value;
2793
                } else {
2794 1
                    $result[$groupKey] = $newValue;
2795 4
                    $result[$groupKey][] = $value;
2796
                }
2797
            }
2798
        }
2799
2800 4
        return static::create(
2801 4
            $result,
2802 4
            $this->iteratorClass,
2803 4
            false
2804
        );
2805
    }
2806
2807
    /**
2808
     * Check if an array has a given key.
2809
     *
2810
     * @param mixed $key
2811
     *
2812
     * @return bool
2813
     */
2814 30
    public function has($key): bool
2815
    {
2816 30
        static $UN_FOUND = null;
2817
2818 30
        if ($UN_FOUND === null) {
2819
            // Generate unique string to use as marker.
2820 1
            $UN_FOUND = \uniqid('arrayy', true);
2821
        }
2822
2823 30
        if (\is_array($key)) {
2824 1
            if ($key === []) {
2825
                return false;
2826
            }
2827
2828 1
            foreach ($key as $keyTmp) {
2829 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
2830 1
                if ($found === false) {
2831 1
                    return false;
2832
                }
2833
            }
2834
2835 1
            return true;
2836
        }
2837
2838 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2839
    }
2840
2841
    /**
2842
     * Check if an array has a given value.
2843
     *
2844
     * INFO: if you need to search recursive please use ```contains()```
2845
     *
2846
     * @param mixed $value
2847
     *
2848
     * @return bool
2849
     */
2850 1
    public function hasValue($value): bool
2851
    {
2852 1
        return $this->contains($value);
2853
    }
2854
2855
    /**
2856
     * Implodes the values of this array.
2857
     *
2858
     * @param string $glue
2859
     *
2860
     * @return string
2861
     * @psalm-mutation-free
2862
     */
2863 28
    public function implode(string $glue = ''): string
2864
    {
2865 28
        return $this->implode_recursive($glue, $this->toArray(), false);
2866
    }
2867
2868
    /**
2869
     * Implodes the keys of this array.
2870
     *
2871
     * @param string $glue
2872
     *
2873
     * @return string
2874
     * @psalm-mutation-free
2875
     */
2876 8
    public function implodeKeys(string $glue = ''): string
2877
    {
2878 8
        return $this->implode_recursive($glue, $this->toArray(), true);
2879
    }
2880
2881
    /**
2882
     * Given a list and an iterate-function that returns
2883
     * a key for each element in the list (or a property name),
2884
     * returns an object with an index of each item.
2885
     *
2886
     * @param mixed $key
2887
     *
2888
     * @return static
2889
     *                <p>(Immutable)</p>
2890
     *
2891
     * @psalm-return static<TKey,T>
2892
     * @psalm-mutation-free
2893
     */
2894 4
    public function indexBy($key): self
2895
    {
2896
        // init
2897 4
        $results = [];
2898
2899 4
        foreach ($this->getGenerator() as $a) {
2900 4
            if (\array_key_exists($key, $a) === true) {
2901 4
                $results[$a[$key]] = $a;
2902
            }
2903
        }
2904
2905 4
        return static::create(
2906 4
            $results,
2907 4
            $this->iteratorClass,
2908 4
            false
2909
        );
2910
    }
2911
2912
    /**
2913
     * alias: for "Arrayy->searchIndex()"
2914
     *
2915
     * @param mixed $value <p>The value to search for.</p>
2916
     *
2917
     * @return false|mixed
2918
     *
2919
     * @see Arrayy::searchIndex()
2920
     */
2921 4
    public function indexOf($value)
2922
    {
2923 4
        return $this->searchIndex($value);
2924
    }
2925
2926
    /**
2927
     * Get everything but the last..$to items.
2928
     *
2929
     * @param int $to
2930
     *
2931
     * @return static
2932
     *                <p>(Immutable)</p>
2933
     *
2934
     * @psalm-return static<TKey,T>
2935
     * @psalm-mutation-free
2936
     */
2937 12
    public function initial(int $to = 1): self
2938
    {
2939 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
2940
    }
2941
2942
    /**
2943
     * Return an array with all elements found in input array.
2944
     *
2945
     * @param array $search
2946
     * @param bool  $keepKeys
2947
     *
2948
     * @return static
2949
     *                <p>(Immutable)</p>
2950
     *
2951
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
2952
     * @psalm-return static<TKey,T>
2953
     * @psalm-mutation-free
2954
     */
2955 4
    public function intersection(array $search, bool $keepKeys = false): self
2956
    {
2957 4
        if ($keepKeys) {
2958
            /**
2959
             * @psalm-suppress MissingClosureReturnType
2960
             * @psalm-suppress MissingClosureParamType
2961
             */
2962 1
            return static::create(
2963 1
                \array_uintersect(
2964 1
                    $this->toArray(),
2965 1
                    $search,
2966 1
                    static function ($a, $b) {
2967 1
                        return $a === $b ? 0 : -1;
2968 1
                    }
2969
                ),
2970 1
                $this->iteratorClass,
2971 1
                false
2972
            );
2973
        }
2974
2975 3
        return static::create(
2976 3
            \array_values(\array_intersect($this->toArray(), $search)),
2977 3
            $this->iteratorClass,
2978 3
            false
2979
        );
2980
    }
2981
2982
    /**
2983
     * Return an array with all elements found in input array.
2984
     *
2985
     * @param array ...$array
2986
     *
2987
     * @return static
2988
     *                <p>(Immutable)</p>
2989
     *
2990
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2991
     * @psalm-return static<TKey,T>
2992
     * @psalm-mutation-free
2993
     */
2994 1
    public function intersectionMulti(...$array): self
2995
    {
2996 1
        return static::create(
2997 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
2998 1
            $this->iteratorClass,
2999 1
            false
3000
        );
3001
    }
3002
3003
    /**
3004
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3005
     *
3006
     * @param array $search
3007
     *
3008
     * @return bool
3009
     *
3010
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3011
     */
3012 1
    public function intersects(array $search): bool
3013
    {
3014 1
        return $this->intersection($search)->count() > 0;
3015
    }
3016
3017
    /**
3018
     * Invoke a function on all of an array's values.
3019
     *
3020
     * @param callable $callable
3021
     * @param mixed    $arguments
3022
     *
3023
     * @return static
3024
     *                <p>(Immutable)</p>
3025
     *
3026
     * @psalm-param  callable(T=,mixed):mixed $callable
3027
     * @psalm-return static<TKey,T>
3028
     * @psalm-mutation-free
3029
     */
3030 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...
3031
    {
3032
        // If one argument given for each iteration, create an array for it.
3033 1
        if (\is_array($arguments) === false) {
3034 1
            $arguments = \array_fill(
3035 1
                0,
3036 1
                $this->count(),
3037 1
                $arguments
3038
            );
3039
        }
3040
3041
        // If the callable has arguments, pass them.
3042 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...
3043 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3044
        } else {
3045 1
            $array = $this->map($callable);
3046
        }
3047
3048 1
        return static::create(
3049 1
            $array,
3050 1
            $this->iteratorClass,
3051 1
            false
3052
        );
3053
    }
3054
3055
    /**
3056
     * Check whether array is associative or not.
3057
     *
3058
     * @param bool $recursive
3059
     *
3060
     * @return bool
3061
     *              <p>Returns true if associative, false otherwise.</p>
3062
     */
3063 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...
3064
    {
3065 15
        if ($this->isEmpty()) {
3066 3
            return false;
3067
        }
3068
3069 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
3070 13
            if ((string) $key !== $key) {
3071 13
                return false;
3072
            }
3073
        }
3074
3075 3
        return true;
3076
    }
3077
3078
    /**
3079
     * Check if a given key or keys are empty.
3080
     *
3081
     * @param int|int[]|string|string[]|null $keys
3082
     *
3083
     * @return bool
3084
     *              <p>Returns true if empty, false otherwise.</p>
3085
     * @psalm-mutation-free
3086
     */
3087 45
    public function isEmpty($keys = null): bool
3088
    {
3089 45
        if ($this->generator) {
3090
            return $this->toArray() === [];
3091
        }
3092
3093 45
        if ($keys === null) {
3094 43
            return $this->array === [];
3095
        }
3096
3097 2
        foreach ((array) $keys as $key) {
3098 2
            if (!empty($this->get($key))) {
3099 2
                return false;
3100
            }
3101
        }
3102
3103 2
        return true;
3104
    }
3105
3106
    /**
3107
     * Check if the current array is equal to the given "$array" or not.
3108
     *
3109
     * @param array $array
3110
     *
3111
     * @return bool
3112
     *
3113
     * @psalm-param array<mixed,mixed> $array
3114
     */
3115 1
    public function isEqual(array $array): bool
3116
    {
3117 1
        return $this->toArray() === $array;
3118
    }
3119
3120
    /**
3121
     * Check if the current array is a multi-array.
3122
     *
3123
     * @return bool
3124
     */
3125 22
    public function isMultiArray(): bool
3126
    {
3127
        return !(
3128 22
            \count($this->toArray(), \COUNT_NORMAL)
3129
            ===
3130 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3131
        );
3132
    }
3133
3134
    /**
3135
     * Check whether array is numeric or not.
3136
     *
3137
     * @return bool
3138
     *              <p>Returns true if numeric, false otherwise.</p>
3139
     */
3140 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...
3141
    {
3142 5
        if ($this->isEmpty()) {
3143 2
            return false;
3144
        }
3145
3146 4
        foreach ($this->keys()->getGenerator() as $key) {
3147 4
            if ((int) $key !== $key) {
3148 4
                return false;
3149
            }
3150
        }
3151
3152 2
        return true;
3153
    }
3154
3155
    /**
3156
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3157
     *
3158
     * @param bool $recursive
3159
     *
3160
     * @return bool
3161
     * @psalm-mutation-free
3162
     */
3163 9
    public function isSequential(bool $recursive = false): bool
3164
    {
3165
3166
        // recursive
3167
3168 9
        if ($recursive === true) {
3169
            return $this->array_keys_recursive($this->toArray())
3170
                   ===
3171
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3172
        }
3173
3174
        // non recursive
3175
3176 9
        return \array_keys($this->toArray())
3177
               ===
3178 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3179
    }
3180
3181
    /**
3182
     * @return array
3183
     *
3184
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3185
     */
3186 1
    public function jsonSerialize(): array
3187
    {
3188 1
        return $this->toArray();
3189
    }
3190
3191
    /**
3192
     * Gets the key/index of the element at the current internal iterator position.
3193
     *
3194
     * @return int|string|null
3195
     */
3196
    public function key()
3197
    {
3198
        return \key($this->array);
3199
    }
3200
3201
    /**
3202
     * Checks if the given key exists in the provided array.
3203
     *
3204
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3205
     *       then you need to use "Arrayy->offsetExists()".
3206
     *
3207
     * @param int|string $key the key to look for
3208
     *
3209
     * @return bool
3210
     * @psalm-mutation-free
3211
     */
3212 142
    public function keyExists($key): bool
3213
    {
3214 142
        return \array_key_exists($key, $this->array);
3215
    }
3216
3217
    /**
3218
     * Get all keys from the current array.
3219
     *
3220
     * @param bool       $recursive     [optional] <p>
3221
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3222
     *                                  </p>
3223
     * @param mixed|null $search_values [optional] <p>
3224
     *                                  If specified, then only keys containing these values are returned.
3225
     *                                  </p>
3226
     * @param bool       $strict        [optional] <p>
3227
     *                                  Determines if strict comparison (===) should be used during the search.
3228
     *                                  </p>
3229
     *
3230
     * @return static
3231
     *                <p>(Immutable) An array of all the keys in input.</p>
3232
     *
3233
     * @psalm-return static<array-key,TKey>
3234
     * @psalm-mutation-free
3235
     */
3236 29
    public function keys(
3237
        bool $recursive = false,
3238
        $search_values = null,
3239
        bool $strict = true
3240
    ): self {
3241
3242
        // recursive
3243
3244 29
        if ($recursive === true) {
3245 4
            $array = $this->array_keys_recursive(
3246 4
                null,
3247 4
                $search_values,
3248 4
                $strict
3249
            );
3250
3251 4
            return static::create(
3252 4
                $array,
3253 4
                $this->iteratorClass,
3254 4
                false
3255
            );
3256
        }
3257
3258
        // non recursive
3259
3260 28
        if ($search_values === null) {
3261 28
            $arrayFunction = function (): \Generator {
3262 28
                foreach ($this->getGenerator() as $key => $value) {
3263 26
                    yield $key;
3264
                }
3265 28
            };
3266
        } else {
3267 1
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3268 1
                $is_array_tmp = \is_array($search_values);
3269
3270 1
                foreach ($this->getGenerator() as $key => $value) {
3271 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...
3272
                        (
3273 1
                            $is_array_tmp === false
3274
                            &&
3275 1
                            $strict === true
3276
                            &&
3277 1
                            $search_values === $value
3278
                        )
3279
                        ||
3280
                        (
3281 1
                            $is_array_tmp === false
3282
                            &&
3283 1
                            $strict === false
3284
                            &&
3285 1
                            $search_values == $value
3286
                        )
3287
                        ||
3288
                        (
3289 1
                            $is_array_tmp === true
3290
                            &&
3291 1
                            \in_array($value, $search_values, $strict)
3292
                        )
3293
                    ) {
3294 1
                        yield $key;
3295
                    }
3296
                }
3297 1
            };
3298
        }
3299
3300 28
        return static::create(
3301 28
            $arrayFunction,
3302 28
            $this->iteratorClass,
3303 28
            false
3304
        );
3305
    }
3306
3307
    /**
3308
     * Sort an array by key in reverse order.
3309
     *
3310
     * @param int $sort_flags [optional] <p>
3311
     *                        You may modify the behavior of the sort using the optional
3312
     *                        parameter sort_flags, for details
3313
     *                        see sort.
3314
     *                        </p>
3315
     *
3316
     * @return $this
3317
     *               <p>(Mutable) Return this Arrayy object.</p>
3318
     *
3319
     * @psalm-return static<TKey,T>
3320
     */
3321 4
    public function krsort(int $sort_flags = 0): self
3322
    {
3323 4
        $this->generatorToArray();
3324
3325 4
        \krsort($this->array, $sort_flags);
3326
3327 4
        return $this;
3328
    }
3329
3330
    /**
3331
     * Sort an array by key in reverse order.
3332
     *
3333
     * @param int $sort_flags [optional] <p>
3334
     *                        You may modify the behavior of the sort using the optional
3335
     *                        parameter sort_flags, for details
3336
     *                        see sort.
3337
     *                        </p>
3338
     *
3339
     * @return $this
3340
     *               <p>(Immutable) Return this Arrayy object.</p>
3341
     *
3342
     * @psalm-return static<TKey,T>
3343
     * @psalm-mutation-free
3344
     */
3345 4
    public function krsortImmutable(int $sort_flags = 0): self
3346
    {
3347 4
        $that = clone $this;
3348
3349
        /**
3350
         * @psalm-suppress ImpureMethodCall - object is already cloned
3351
         */
3352 4
        $that->krsort($sort_flags);
3353
3354 4
        return $that;
3355
    }
3356
3357
    /**
3358
     * Get the last value from the current array.
3359
     *
3360
     * @return mixed|null
3361
     *                    <p>Return null if there wasn't a element.</p>
3362
     * @psalm-mutation-free
3363
     */
3364 17
    public function last()
3365
    {
3366 17
        $key_last = $this->lastKey();
3367 17
        if ($key_last === null) {
3368 2
            return null;
3369
        }
3370
3371 15
        return $this->get($key_last);
3372
    }
3373
3374
    /**
3375
     * Get the last key from the current array.
3376
     *
3377
     * @return mixed|null
3378
     *                    <p>Return null if there wasn't a element.</p>
3379
     * @psalm-mutation-free
3380
     */
3381 21
    public function lastKey()
3382
    {
3383 21
        $this->generatorToArray();
3384
3385 21
        return \array_key_last($this->array);
3386
    }
3387
3388
    /**
3389
     * Get the last value(s) from the current array.
3390
     *
3391
     * @param int|null $number
3392
     *
3393
     * @return static
3394
     *                <p>(Immutable)</p>
3395
     *
3396
     * @psalm-return static<TKey,T>
3397
     * @psalm-mutation-free
3398
     */
3399 13
    public function lastsImmutable(int $number = null): self
3400
    {
3401 13
        if ($this->isEmpty()) {
3402 1
            return static::create(
3403 1
                [],
3404 1
                $this->iteratorClass,
3405 1
                false
3406
            );
3407
        }
3408
3409 12
        if ($number === null) {
3410 8
            $poppedValue = $this->last();
3411
3412 8
            if ($poppedValue === null) {
3413 1
                $poppedValue = [$poppedValue];
3414
            } else {
3415 7
                $poppedValue = (array) $poppedValue;
3416
            }
3417
3418 8
            $arrayy = static::create(
3419 8
                $poppedValue,
3420 8
                $this->iteratorClass,
3421 8
                false
3422
            );
3423
        } else {
3424 4
            $number = (int) $number;
3425 4
            $arrayy = $this->rest(-$number);
3426
        }
3427
3428 12
        return $arrayy;
3429
    }
3430
3431
    /**
3432
     * Get the last value(s) from the current array.
3433
     *
3434
     * @param int|null $number
3435
     *
3436
     * @return $this
3437
     *               <p>(Mutable)</p>
3438
     *
3439
     * @psalm-return static<TKey,T>
3440
     */
3441 13
    public function lastsMutable(int $number = null): self
3442
    {
3443 13
        if ($this->isEmpty()) {
3444 1
            return $this;
3445
        }
3446
3447 12
        if ($number === null) {
3448 8
            $poppedValue = $this->last();
3449
3450 8
            if ($poppedValue === null) {
3451 1
                $poppedValue = [$poppedValue];
3452
            } else {
3453 7
                $poppedValue = (array) $poppedValue;
3454
            }
3455
3456 8
            $this->array = static::create(
3457 8
                $poppedValue,
3458 8
                $this->iteratorClass,
3459 8
                false
3460 8
            )->toArray();
3461
        } else {
3462 4
            $number = (int) $number;
3463 4
            $this->array = $this->rest(-$number)->toArray();
3464
        }
3465
3466 12
        $this->generator = null;
3467
3468 12
        return $this;
3469
    }
3470
3471
    /**
3472
     * Count the values from the current array.
3473
     *
3474
     * alias: for "Arrayy->count()"
3475
     *
3476
     * @param int $mode
3477
     *
3478
     * @return int
3479
     *
3480
     * @see Arrayy::count()
3481
     */
3482 20
    public function length(int $mode = \COUNT_NORMAL): int
3483
    {
3484 20
        return $this->count($mode);
3485
    }
3486
3487
    /**
3488
     * Apply the given function to the every element of the array,
3489
     * collecting the results.
3490
     *
3491
     * @param callable $callable
3492
     * @param bool     $useKeyAsSecondParameter
3493
     * @param mixed    ...$arguments
3494
     *
3495
     * @return static
3496
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3497
     *
3498
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3499
     * @psalm-return static<TKey,T>
3500
     * @psalm-mutation-free
3501
     */
3502 5
    public function map(
3503
        callable $callable,
3504
        bool $useKeyAsSecondParameter = false,
3505
        ...$arguments
3506
    ) {
3507
        /**
3508
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3509
         */
3510 5
        $useArguments = \func_num_args() > 2;
3511
3512 5
        return static::create(
3513 5
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3514 5
                foreach ($this->getGenerator() as $key => $value) {
3515 4
                    if ($useArguments) {
3516 3
                        if ($useKeyAsSecondParameter) {
3517
                            yield $key => $callable($value, $key, ...$arguments);
3518
                        } else {
3519 3
                            yield $key => $callable($value, ...$arguments);
3520
                        }
3521
                    } else {
3522
                        /** @noinspection NestedPositiveIfStatementsInspection */
3523 4
                        if ($useKeyAsSecondParameter) {
3524
                            yield $key => $callable($value, $key);
3525
                        } else {
3526 4
                            yield $key => $callable($value);
3527
                        }
3528
                    }
3529
                }
3530 5
            },
3531 5
            $this->iteratorClass,
3532 5
            false
3533
        );
3534
    }
3535
3536
    /**
3537
     * Check if all items in current array match a truth test.
3538
     *
3539
     * @param \Closure $closure
3540
     *
3541
     * @return bool
3542
     */
3543 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...
3544
    {
3545 15
        if ($this->count() === 0) {
3546 2
            return false;
3547
        }
3548
3549 13
        foreach ($this->getGenerator() as $key => $value) {
3550 13
            $value = $closure($value, $key);
3551
3552 13
            if ($value === false) {
3553 13
                return false;
3554
            }
3555
        }
3556
3557 7
        return true;
3558
    }
3559
3560
    /**
3561
     * Check if any item in the current array matches a truth test.
3562
     *
3563
     * @param \Closure $closure
3564
     *
3565
     * @return bool
3566
     */
3567 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...
3568
    {
3569 14
        if ($this->count() === 0) {
3570 2
            return false;
3571
        }
3572
3573 12
        foreach ($this->getGenerator() as $key => $value) {
3574 12
            $value = $closure($value, $key);
3575
3576 12
            if ($value === true) {
3577 12
                return true;
3578
            }
3579
        }
3580
3581 4
        return false;
3582
    }
3583
3584
    /**
3585
     * Get the max value from an array.
3586
     *
3587
     * @return false|mixed
3588
     *                     <p>Will return false if there are no values.</p>
3589
     */
3590 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...
3591
    {
3592 10
        if ($this->count() === 0) {
3593 1
            return false;
3594
        }
3595
3596 9
        $max = false;
3597 9
        foreach ($this->getGenerator() as $value) {
3598
            if (
3599 9
                $max === false
3600
                ||
3601 9
                $value > $max
3602
            ) {
3603 9
                $max = $value;
3604
            }
3605
        }
3606
3607 9
        return $max;
3608
    }
3609
3610
    /**
3611
     * Merge the new $array into the current array.
3612
     *
3613
     * - keep key,value from the current array, also if the index is in the new $array
3614
     *
3615
     * @param array $array
3616
     * @param bool  $recursive
3617
     *
3618
     * @return static
3619
     *                <p>(Immutable)</p>
3620
     *
3621
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3622
     * @psalm-return static<int|TKey,T>
3623
     * @psalm-mutation-free
3624
     */
3625 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...
3626
    {
3627 32
        if ($recursive === true) {
3628 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
3629 9
            $result = \array_replace_recursive($this->toArray(), $array);
3630
        } else {
3631 23
            $result = \array_replace($this->toArray(), $array);
3632
        }
3633
3634 32
        return static::create(
3635 32
            $result,
3636 32
            $this->iteratorClass,
3637 32
            false
3638
        );
3639
    }
3640
3641
    /**
3642
     * Merge the new $array into the current array.
3643
     *
3644
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3645
     * - create new indexes
3646
     *
3647
     * @param array $array
3648
     * @param bool  $recursive
3649
     *
3650
     * @return static
3651
     *                <p>(Immutable)</p>
3652
     *
3653
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3654
     * @psalm-return static<TKey,T>
3655
     * @psalm-mutation-free
3656
     */
3657 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...
3658
    {
3659 19
        if ($recursive === true) {
3660 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
3661 5
            $result = \array_merge_recursive($this->toArray(), $array);
3662
        } else {
3663 14
            $result = \array_merge($this->toArray(), $array);
3664
        }
3665
3666 19
        return static::create(
3667 19
            $result,
3668 19
            $this->iteratorClass,
3669 19
            false
3670
        );
3671
    }
3672
3673
    /**
3674
     * Merge the the current array into the $array.
3675
     *
3676
     * - use key,value from the new $array, also if the index is in the current array
3677
     *
3678
     * @param array $array
3679
     * @param bool  $recursive
3680
     *
3681
     * @return static
3682
     *                <p>(Immutable)</p>
3683
     *
3684
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3685
     * @psalm-return static<TKey,T>
3686
     * @psalm-mutation-free
3687
     */
3688 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...
3689
    {
3690 16
        if ($recursive === true) {
3691 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
3692 4
            $result = \array_replace_recursive($array, $this->toArray());
3693
        } else {
3694 12
            $result = \array_replace($array, $this->toArray());
3695
        }
3696
3697 16
        return static::create(
3698 16
            $result,
3699 16
            $this->iteratorClass,
3700 16
            false
3701
        );
3702
    }
3703
3704
    /**
3705
     * Merge the current array into the new $array.
3706
     *
3707
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3708
     * - create new indexes
3709
     *
3710
     * @param array $array
3711
     * @param bool  $recursive
3712
     *
3713
     * @return static
3714
     *                <p>(Immutable)</p>
3715
     *
3716
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3717
     * @psalm-return static<TKey,T>
3718
     * @psalm-mutation-free
3719
     */
3720 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...
3721
    {
3722 20
        if ($recursive === true) {
3723 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
3724 7
            $result = \array_merge_recursive($array, $this->toArray());
3725
        } else {
3726 13
            $result = \array_merge($array, $this->toArray());
3727
        }
3728
3729 20
        return static::create(
3730 20
            $result,
3731 20
            $this->iteratorClass,
3732 20
            false
3733
        );
3734
    }
3735
3736
    /**
3737
     * @return ArrayyMeta|static
3738
     */
3739 15
    public static function meta()
3740
    {
3741 15
        return (new ArrayyMeta())->getMetaObject(static::class);
3742
    }
3743
3744
    /**
3745
     * Get the min value from an array.
3746
     *
3747
     * @return false|mixed
3748
     *                     <p>Will return false if there are no values.</p>
3749
     */
3750 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...
3751
    {
3752 10
        if ($this->count() === 0) {
3753 1
            return false;
3754
        }
3755
3756 9
        $min = false;
3757 9
        foreach ($this->getGenerator() as $value) {
3758
            if (
3759 9
                $min === false
3760
                ||
3761 9
                $value < $min
3762
            ) {
3763 9
                $min = $value;
3764
            }
3765
        }
3766
3767 9
        return $min;
3768
    }
3769
3770
    /**
3771
     * Get the most used value from the array.
3772
     *
3773
     * @return mixed|null
3774
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
3775
     * @psalm-mutation-free
3776
     */
3777 3
    public function mostUsedValue()
3778
    {
3779 3
        return $this->countValues()->arsortImmutable()->firstKey();
3780
    }
3781
3782
    /**
3783
     * Get the most used value from the array.
3784
     *
3785
     * @param int|null $number <p>How many values you will take?</p>
3786
     *
3787
     * @return static
3788
     *                <p>(Immutable)</p>
3789
     *
3790
     * @psalm-return static<TKey,T>
3791
     * @psalm-mutation-free
3792
     */
3793 3
    public function mostUsedValues(int $number = null): self
3794
    {
3795 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
3796
    }
3797
3798
    /**
3799
     * Move an array element to a new index.
3800
     *
3801
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3802
     *
3803
     * @param int|string $from
3804
     * @param int        $to
3805
     *
3806
     * @return static
3807
     *                <p>(Immutable)</p>
3808
     *
3809
     * @psalm-return static<TKey,T>
3810
     * @psalm-mutation-free
3811
     */
3812 1
    public function moveElement($from, $to): self
3813
    {
3814 1
        $array = $this->toArray();
3815
3816 1
        if ((int) $from === $from) {
3817 1
            $tmp = \array_splice($array, $from, 1);
3818 1
            \array_splice($array, (int) $to, 0, $tmp);
3819 1
            $output = $array;
3820 1
        } elseif ((string) $from === $from) {
3821 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3822 1
            $itemToMove = $array[$from];
3823 1
            if ($indexToMove !== false) {
3824 1
                \array_splice($array, $indexToMove, 1);
3825
            }
3826 1
            $i = 0;
3827 1
            $output = [];
3828 1
            foreach ($array as $key => $item) {
3829 1
                if ($i === $to) {
3830 1
                    $output[$from] = $itemToMove;
3831
                }
3832 1
                $output[$key] = $item;
3833 1
                ++$i;
3834
            }
3835
        } else {
3836
            $output = [];
3837
        }
3838
3839 1
        return static::create(
3840 1
            $output,
3841 1
            $this->iteratorClass,
3842 1
            false
3843
        );
3844
    }
3845
3846
    /**
3847
     * Move an array element to the first place.
3848
     *
3849
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3850
     *       loss the keys of an indexed array.
3851
     *
3852
     * @param int|string $key
3853
     *
3854
     * @return static
3855
     *                <p>(Immutable)</p>
3856
     *
3857
     * @psalm-return static<TKey,T>
3858
     * @psalm-mutation-free
3859
     */
3860 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...
3861
    {
3862 1
        $array = $this->toArray();
3863
3864 1
        if ($this->offsetExists($key)) {
3865 1
            $tmpValue = $this->get($key);
3866 1
            unset($array[$key]);
3867 1
            $array = [$key => $tmpValue] + $array;
3868
        }
3869
3870 1
        return static::create(
3871 1
            $array,
3872 1
            $this->iteratorClass,
3873 1
            false
3874
        );
3875
    }
3876
3877
    /**
3878
     * Move an array element to the last place.
3879
     *
3880
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3881
     *       loss the keys of an indexed array.
3882
     *
3883
     * @param int|string $key
3884
     *
3885
     * @return static
3886
     *                <p>(Immutable)</p>
3887
     *
3888
     * @psalm-return static<TKey,T>
3889
     * @psalm-mutation-free
3890
     */
3891 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...
3892
    {
3893 1
        $array = $this->toArray();
3894
3895 1
        if ($this->offsetExists($key)) {
3896 1
            $tmpValue = $this->get($key);
3897 1
            unset($array[$key]);
3898 1
            $array += [$key => $tmpValue];
3899
        }
3900
3901 1
        return static::create(
3902 1
            $array,
3903 1
            $this->iteratorClass,
3904 1
            false
3905
        );
3906
    }
3907
3908
    /**
3909
     * Moves the internal iterator position to the next element and returns this element.
3910
     *
3911
     * @return false|mixed
3912
     *                     <p>(Mutable) Will return false if there are no values.</p>
3913
     */
3914
    public function next()
3915
    {
3916
        return \next($this->array);
3917
    }
3918
3919
    /**
3920
     * Get the next nth keys and values from the array.
3921
     *
3922
     * @param int $step
3923
     * @param int $offset
3924
     *
3925
     * @return static
3926
     *                <p>(Immutable)</p>
3927
     *
3928
     * @psalm-return static<TKey,T>
3929
     * @psalm-mutation-free
3930
     */
3931
    public function nth(int $step, int $offset = 0): self
3932
    {
3933 1
        $arrayFunction = function () use ($step, $offset): \Generator {
3934 1
            $position = 0;
3935 1
            foreach ($this->getGenerator() as $key => $value) {
3936 1
                if ($position++ % $step !== $offset) {
3937 1
                    continue;
3938
                }
3939
3940 1
                yield $key => $value;
3941
            }
3942 1
        };
3943
3944 1
        return static::create(
3945 1
            $arrayFunction,
3946 1
            $this->iteratorClass,
3947 1
            false
3948
        );
3949
    }
3950
3951
    /**
3952
     * Get a subset of the items from the given array.
3953
     *
3954
     * @param mixed[] $keys
3955
     *
3956
     * @return static
3957
     *                <p>(Immutable)</p>
3958
     *
3959
     * @psalm-return static<TKey,T>
3960
     * @psalm-mutation-free
3961
     */
3962 1
    public function only(array $keys): self
3963
    {
3964 1
        $array = $this->toArray();
3965
3966 1
        return static::create(
3967 1
            \array_intersect_key($array, \array_flip($keys)),
3968 1
            $this->iteratorClass,
3969 1
            false
3970
        );
3971
    }
3972
3973
    /**
3974
     * Pad array to the specified size with a given value.
3975
     *
3976
     * @param int   $size  <p>Size of the result array.</p>
3977
     * @param mixed $value <p>Empty value by default.</p>
3978
     *
3979
     * @return static
3980
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3981
     *
3982
     * @psalm-return static<TKey,T>
3983
     * @psalm-mutation-free
3984
     */
3985 5
    public function pad(int $size, $value): self
3986
    {
3987 5
        return static::create(
3988 5
            \array_pad($this->toArray(), $size, $value),
3989 5
            $this->iteratorClass,
3990 5
            false
3991
        );
3992
    }
3993
3994
    /**
3995
     * Partitions this array in two array according to a predicate.
3996
     * Keys are preserved in the resulting array.
3997
     *
3998
     * @param \Closure $closure
3999
     *                          <p>The predicate on which to partition.</p>
4000
     *
4001
     * @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...
4002
     *                    <p>An array with two elements. The first element contains the array
4003
     *                    of elements where the predicate returned TRUE, the second element
4004
     *                    contains the array of elements where the predicate returned FALSE.</p>
4005
     *
4006
     * @psalm-return array<int, static<TKey,T>>
4007
     */
4008 1
    public function partition(\Closure $closure): array
4009
    {
4010
        // init
4011 1
        $matches = [];
4012 1
        $noMatches = [];
4013
4014 1
        foreach ($this->array as $key => $value) {
4015 1
            if ($closure($value, $key)) {
4016 1
                $matches[$key] = $value;
4017
            } else {
4018 1
                $noMatches[$key] = $value;
4019
            }
4020
        }
4021
4022 1
        return [self::create($matches), self::create($noMatches)];
4023
    }
4024
4025
    /**
4026
     * Pop a specified value off the end of the current array.
4027
     *
4028
     * @return mixed|null
4029
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4030
     */
4031 5
    public function pop()
4032
    {
4033 5
        $this->generatorToArray();
4034
4035 5
        return \array_pop($this->array);
4036
    }
4037
4038
    /**
4039
     * Prepend a (key) + value to the current array.
4040
     *
4041
     * @param mixed $value
4042
     * @param mixed $key
4043
     *
4044
     * @return $this
4045
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4046
     *
4047
     * @psalm-return static<TKey,T>
4048
     */
4049 11
    public function prepend($value, $key = null)
4050
    {
4051 11
        $this->generatorToArray();
4052
4053 11
        if ($this->properties !== []) {
4054 3
            $this->checkType($key, $value);
4055
        }
4056
4057 9
        if ($key === null) {
4058 8
            \array_unshift($this->array, $value);
4059
        } else {
4060 2
            $this->array = [$key => $value] + $this->array;
4061
        }
4062
4063 9
        return $this;
4064
    }
4065
4066
    /**
4067
     * Add a suffix to each key.
4068
     *
4069
     * @param mixed $suffix
4070
     *
4071
     * @return static
4072
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4073
     *
4074
     * @psalm-return static<TKey,T>
4075
     * @psalm-mutation-free
4076
     */
4077 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...
4078
    {
4079
        // init
4080 10
        $result = [];
4081
4082 10
        foreach ($this->getGenerator() as $key => $item) {
4083 9
            if ($item instanceof self) {
4084
                $result[$key] = $item->prependToEachKey($suffix);
4085 9
            } elseif (\is_array($item) === true) {
4086
                $result[$key] = self::create(
4087
                    $item,
4088
                    $this->iteratorClass,
4089
                    false
4090
                )->prependToEachKey($suffix)
4091
                    ->toArray();
4092
            } else {
4093 9
                $result[$key . $suffix] = $item;
4094
            }
4095
        }
4096
4097 10
        return self::create(
4098 10
            $result,
4099 10
            $this->iteratorClass,
4100 10
            false
4101
        );
4102
    }
4103
4104
    /**
4105
     * Add a suffix to each value.
4106
     *
4107
     * @param mixed $suffix
4108
     *
4109
     * @return static
4110
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4111
     *
4112
     * @psalm-return static<TKey,T>
4113
     * @psalm-mutation-free
4114
     */
4115 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...
4116
    {
4117
        // init
4118 10
        $result = [];
4119
4120 10
        foreach ($this->getGenerator() as $key => $item) {
4121 9
            if ($item instanceof self) {
4122
                $result[$key] = $item->prependToEachValue($suffix);
4123 9
            } elseif (\is_array($item) === true) {
4124
                $result[$key] = self::create(
4125
                    $item,
4126
                    $this->iteratorClass,
4127
                    false
4128
                )->prependToEachValue($suffix)
4129
                    ->toArray();
4130 9
            } elseif (\is_object($item) === true) {
4131 1
                $result[$key] = $item;
4132
            } else {
4133 9
                $result[$key] = $item . $suffix;
4134
            }
4135
        }
4136
4137 10
        return self::create(
4138 10
            $result,
4139 10
            $this->iteratorClass,
4140 10
            false
4141
        );
4142
    }
4143
4144
    /**
4145
     * Return the value of a given key and
4146
     * delete the key.
4147
     *
4148
     * @param int|int[]|string|string[]|null $keyOrKeys
4149
     * @param mixed                          $fallback
4150
     *
4151
     * @return mixed
4152
     */
4153 5
    public function pull($keyOrKeys = null, $fallback = null)
4154
    {
4155 5
        if ($keyOrKeys === null) {
4156 1
            $array = $this->toArray();
4157 1
            $this->clear();
4158
4159 1
            return $array;
4160
        }
4161
4162 4
        if (\is_array($keyOrKeys) === true) {
4163 1
            $valueOrValues = [];
4164 1
            foreach ($keyOrKeys as $key) {
4165 1
                $valueOrValues[] = $this->get($key, $fallback);
4166 1
                $this->offsetUnset($key);
4167
            }
4168
        } else {
4169 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4170 4
            $this->offsetUnset($keyOrKeys);
4171
        }
4172
4173 4
        return $valueOrValues;
4174
    }
4175
4176
    /**
4177
     * Push one or more values onto the end of array at once.
4178
     *
4179
     * @param array ...$args
4180
     *
4181
     * @return $this
4182
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4183
     *
4184
     * @noinspection ReturnTypeCanBeDeclaredInspection
4185
     *
4186
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4187
     * @psalm-return static<TKey,T>
4188
     */
4189 7
    public function push(...$args)
4190
    {
4191 7
        $this->generatorToArray();
4192
4193
        if (
4194 7
            $this->checkPropertyTypes
4195
            &&
4196 7
            $this->properties !== []
4197
        ) {
4198 1
            foreach ($args as $key => $value) {
4199 1
                $this->checkType($key, $value);
4200
            }
4201
        }
4202
4203 7
        \array_push($this->array, ...$args);
4204
4205 7
        return $this;
4206
    }
4207
4208
    /**
4209
     * Get a random value from the current array.
4210
     *
4211
     * @param int|null $number <p>How many values you will take?</p>
4212
     *
4213
     * @return static
4214
     *                <p>(Immutable)</p>
4215
     *
4216
     * @psalm-return static<int|array-key,T>
4217
     */
4218 19
    public function randomImmutable(int $number = null): self
4219
    {
4220 19
        $this->generatorToArray();
4221
4222 19
        if ($this->count() === 0) {
4223 1
            return static::create(
4224 1
                [],
4225 1
                $this->iteratorClass,
4226 1
                false
4227
            );
4228
        }
4229
4230 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...
4231
            /** @noinspection NonSecureArrayRandUsageInspection */
4232 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4233
4234 13
            return static::create(
4235 13
                $arrayRandValue,
4236 13
                $this->iteratorClass,
4237 13
                false
4238
            );
4239
        }
4240
4241 6
        $arrayTmp = $this->array;
4242
        /** @noinspection NonSecureShuffleUsageInspection */
4243 6
        \shuffle($arrayTmp);
4244
4245 6
        return static::create(
4246 6
            $arrayTmp,
4247 6
            $this->iteratorClass,
4248 6
            false
4249 6
        )->firstsImmutable($number);
4250
    }
4251
4252
    /**
4253
     * Pick a random key/index from the keys of this array.
4254
     *
4255
     * @throws \RangeException If array is empty
4256
     *
4257
     * @return mixed
4258
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4259
     */
4260 4
    public function randomKey()
4261
    {
4262 4
        $result = $this->randomKeys(1);
4263
4264 4
        if (!isset($result[0])) {
4265
            $result[0] = null;
4266
        }
4267
4268 4
        return $result[0];
4269
    }
4270
4271
    /**
4272
     * Pick a given number of random keys/indexes out of this array.
4273
     *
4274
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4275
     *
4276
     * @throws \RangeException If array is empty
4277
     *
4278
     * @return static
4279
     *                <p>(Immutable)</p>
4280
     *
4281
     * @psalm-return static<TKey,T>
4282
     */
4283 13
    public function randomKeys(int $number): self
4284
    {
4285 13
        $this->generatorToArray();
4286
4287 13
        $count = $this->count();
4288
4289
        if (
4290 13
            $number === 0
4291
            ||
4292 13
            $number > $count
4293
        ) {
4294 2
            throw new \RangeException(
4295 2
                \sprintf(
4296 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4297 2
                    $number,
4298 2
                    $count
4299
                )
4300
            );
4301
        }
4302
4303 11
        $result = (array) \array_rand($this->array, $number);
4304
4305 11
        return static::create(
4306 11
            $result,
4307 11
            $this->iteratorClass,
4308 11
            false
4309
        );
4310
    }
4311
4312
    /**
4313
     * Get a random value from the current array.
4314
     *
4315
     * @param int|null $number <p>How many values you will take?</p>
4316
     *
4317
     * @return $this
4318
     *               <p>(Mutable) Return this Arrayy object.</p>
4319
     *
4320
     * @psalm-return static<TKey,T>
4321
     */
4322 17
    public function randomMutable(int $number = null): self
4323
    {
4324 17
        $this->generatorToArray();
4325
4326 17
        if ($this->count() === 0) {
4327
            return static::create(
4328
                [],
4329
                $this->iteratorClass,
4330
                false
4331
            );
4332
        }
4333
4334 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...
4335
            /** @noinspection NonSecureArrayRandUsageInspection */
4336 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4337 7
            $this->array = $arrayRandValue;
4338
4339 7
            return $this;
4340
        }
4341
4342
        /** @noinspection NonSecureShuffleUsageInspection */
4343 11
        \shuffle($this->array);
4344
4345 11
        return $this->firstsMutable($number);
4346
    }
4347
4348
    /**
4349
     * Pick a random value from the values of this array.
4350
     *
4351
     * @return mixed
4352
     *               <p>Get a random value or null if there wasn't a value.</p>
4353
     */
4354 4
    public function randomValue()
4355
    {
4356 4
        $result = $this->randomImmutable();
4357
4358 4
        if (!isset($result[0])) {
4359
            $result[0] = null;
4360
        }
4361
4362 4
        return $result[0];
4363
    }
4364
4365
    /**
4366
     * Pick a given number of random values out of this array.
4367
     *
4368
     * @param int $number
4369
     *
4370
     * @return static
4371
     *                <p>(Mutable)</p>
4372
     *
4373
     * @psalm-return static<TKey,T>
4374
     */
4375 7
    public function randomValues(int $number): self
4376
    {
4377 7
        return $this->randomMutable($number);
4378
    }
4379
4380
    /**
4381
     * Get a random value from an array, with the ability to skew the results.
4382
     *
4383
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4384
     *
4385
     * @param array    $array
4386
     * @param int|null $number <p>How many values you will take?</p>
4387
     *
4388
     * @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...
4389
     *                           <p>(Immutable)</p>
4390
     *
4391
     * @psalm-param  array<mixed,mixed> $array
4392
     * @psalm-return static<int|array-key,T>
4393
     */
4394 9
    public function randomWeighted(array $array, int $number = null): self
4395
    {
4396
        // init
4397 9
        $options = [];
4398
4399 9
        foreach ($array as $option => $weight) {
4400 9
            if ($this->searchIndex($option) !== false) {
4401 9
                for ($i = 0; $i < $weight; ++$i) {
4402 1
                    $options[] = $option;
4403
                }
4404
            }
4405
        }
4406
4407 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4408
    }
4409
4410
    /**
4411
     * Reduce the current array via callable e.g. anonymous-function.
4412
     *
4413
     * @param callable $callable
4414
     * @param mixed    $init
4415
     *
4416
     * @return static
4417
     *                <p>(Immutable)</p>
4418
     *
4419
     * @psalm-return static<TKey,T>
4420
     * @psalm-mutation-free
4421
     */
4422 18
    public function reduce($callable, $init = []): self
4423
    {
4424 18
        if ($this->generator) {
4425 1
            $result = $init;
4426
4427 1
            foreach ($this->getGenerator() as $value) {
4428 1
                $result = $callable($result, $value);
4429
            }
4430
4431 1
            return static::create(
4432 1
                $result,
4433 1
                $this->iteratorClass,
4434 1
                false
4435
            );
4436
        }
4437
4438 18
        $result = \array_reduce($this->array, $callable, $init);
4439
4440 18
        if ($result === null) {
4441
            $this->array = [];
4442
        } else {
4443 18
            $this->array = (array) $result;
4444
        }
4445
4446 18
        return static::create(
4447 18
            $this->array,
4448 18
            $this->iteratorClass,
4449 18
            false
4450
        );
4451
    }
4452
4453
    /**
4454
     * @param bool $unique
4455
     *
4456
     * @return static
4457
     *                <p>(Immutable)</p>
4458
     *
4459
     * @psalm-return static<TKey,T>
4460
     * @psalm-mutation-free
4461
     */
4462 14
    public function reduce_dimension(bool $unique = true): self
4463
    {
4464
        // init
4465 14
        $result = [];
4466
4467 14
        foreach ($this->getGenerator() as $val) {
4468 12
            if (\is_array($val) === true) {
4469 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4470
            } else {
4471 12
                $result[] = [$val];
4472
            }
4473
        }
4474
4475 14
        $result = $result === [] ? [] : \array_merge(...$result);
4476
4477 14
        $resultArrayy = new static($result);
4478
4479
        /**
4480
         * @psalm-suppress ImpureMethodCall - object is already re-created
4481
         * @psalm-suppress InvalidReturnStatement - why?
4482
         */
4483 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4484
    }
4485
4486
    /**
4487
     * Create a numerically re-indexed Arrayy object.
4488
     *
4489
     * @return $this
4490
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4491
     *
4492
     * @psalm-return static<TKey,T>
4493
     */
4494 9
    public function reindex(): self
4495
    {
4496 9
        $this->generatorToArray(false);
4497
4498 9
        $this->array = \array_values($this->array);
4499
4500 9
        return $this;
4501
    }
4502
4503
    /**
4504
     * Return all items that fail the truth test.
4505
     *
4506
     * @param \Closure $closure
4507
     *
4508
     * @return static
4509
     *                <p>(Immutable)</p>
4510
     *
4511
     * @psalm-return static<TKey,T>
4512
     * @psalm-mutation-free
4513
     */
4514 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...
4515
    {
4516
        // init
4517 1
        $filtered = [];
4518
4519 1
        foreach ($this->getGenerator() as $key => $value) {
4520 1
            if (!$closure($value, $key)) {
4521 1
                $filtered[$key] = $value;
4522
            }
4523
        }
4524
4525 1
        return static::create(
4526 1
            $filtered,
4527 1
            $this->iteratorClass,
4528 1
            false
4529
        );
4530
    }
4531
4532
    /**
4533
     * Remove a value from the current array (optional using dot-notation).
4534
     *
4535
     * @param mixed $key
4536
     *
4537
     * @return static
4538
     *                <p>(Mutable)</p>
4539
     *
4540
     * @psalm-param  TKey $key
4541
     * @psalm-return static<TKey,T>
4542
     */
4543 21
    public function remove($key)
4544
    {
4545
        // recursive call
4546 21
        if (\is_array($key) === true) {
4547
            foreach ($key as $k) {
4548
                $this->internalRemove($k);
4549
            }
4550
4551
            return static::create(
4552
                $this->toArray(),
4553
                $this->iteratorClass,
4554
                false
4555
            );
4556
        }
4557
4558 21
        $this->internalRemove($key);
4559
4560 21
        return static::create(
4561 21
            $this->toArray(),
4562 21
            $this->iteratorClass,
4563 21
            false
4564
        );
4565
    }
4566
4567
    /**
4568
     * alias: for "Arrayy->removeValue()"
4569
     *
4570
     * @param mixed $element
4571
     *
4572
     * @return static
4573
     *                <p>(Immutable)</p>
4574
     *
4575
     * @psalm-param  T $element
4576
     * @psalm-return static<TKey,T>
4577
     * @psalm-mutation-free
4578
     */
4579 8
    public function removeElement($element)
4580
    {
4581 8
        return $this->removeValue($element);
4582
    }
4583
4584
    /**
4585
     * Remove the first value from the current array.
4586
     *
4587
     * @return static
4588
     *                <p>(Immutable)</p>
4589
     *
4590
     * @psalm-return static<TKey,T>
4591
     * @psalm-mutation-free
4592
     */
4593 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...
4594
    {
4595 7
        $tmpArray = $this->toArray();
4596
4597 7
        \array_shift($tmpArray);
4598
4599 7
        return static::create(
4600 7
            $tmpArray,
4601 7
            $this->iteratorClass,
4602 7
            false
4603
        );
4604
    }
4605
4606
    /**
4607
     * Remove the last value from the current array.
4608
     *
4609
     * @return static
4610
     *                <p>(Immutable)</p>
4611
     *
4612
     * @psalm-return static<TKey,T>
4613
     * @psalm-mutation-free
4614
     */
4615 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...
4616
    {
4617 7
        $tmpArray = $this->toArray();
4618
4619 7
        \array_pop($tmpArray);
4620
4621 7
        return static::create(
4622 7
            $tmpArray,
4623 7
            $this->iteratorClass,
4624 7
            false
4625
        );
4626
    }
4627
4628
    /**
4629
     * Removes a particular value from an array (numeric or associative).
4630
     *
4631
     * @param mixed $value
4632
     *
4633
     * @return static
4634
     *                <p>(Immutable)</p>
4635
     *
4636
     * @psalm-param  T $value
4637
     * @psalm-return static<TKey,T>
4638
     * @psalm-mutation-free
4639
     */
4640 8
    public function removeValue($value): self
4641
    {
4642 8
        $this->generatorToArray();
4643
4644
        // init
4645 8
        $isSequentialArray = $this->isSequential();
4646
4647 8
        foreach ($this->array as $key => $item) {
4648 7
            if ($item === $value) {
4649 7
                unset($this->array[$key]);
4650
            }
4651
        }
4652
4653 8
        if ($isSequentialArray) {
4654 6
            $this->array = \array_values($this->array);
4655
        }
4656
4657 8
        return static::create(
4658 8
            $this->array,
4659 8
            $this->iteratorClass,
4660 8
            false
4661
        );
4662
    }
4663
4664
    /**
4665
     * Generate array of repeated arrays.
4666
     *
4667
     * @param int $times <p>How many times has to be repeated.</p>
4668
     *
4669
     * @return static
4670
     *                <p>(Immutable)</p>
4671
     *
4672
     * @psalm-return static<TKey,T>
4673
     * @psalm-mutation-free
4674
     */
4675 1
    public function repeat($times): self
4676
    {
4677 1
        if ($times === 0) {
4678 1
            return static::create([], $this->iteratorClass);
4679
        }
4680
4681 1
        return static::create(
4682 1
            \array_fill(0, (int) $times, $this->toArray()),
4683 1
            $this->iteratorClass,
4684 1
            false
4685
        );
4686
    }
4687
4688
    /**
4689
     * Replace a key with a new key/value pair.
4690
     *
4691
     * @param mixed $oldKey
4692
     * @param mixed $newKey
4693
     * @param mixed $newValue
4694
     *
4695
     * @return static
4696
     *                <p>(Immutable)</p>
4697
     *
4698
     * @psalm-return static<TKey,T>
4699
     * @psalm-mutation-free
4700
     */
4701 5
    public function replace($oldKey, $newKey, $newValue): self
4702
    {
4703 5
        $that = clone $this;
4704
4705
        /**
4706
         * @psalm-suppress ImpureMethodCall - object is already cloned
4707
         */
4708 5
        return $that->remove($oldKey)
4709 5
            ->set($newKey, $newValue);
4710
    }
4711
4712
    /**
4713
     * Create an array using the current array as values and the other array as keys.
4714
     *
4715
     * @param array $keys <p>An array of keys.</p>
4716
     *
4717
     * @return static
4718
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4719
     *
4720
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4721
     * @psalm-return static<TKey,T>
4722
     * @psalm-mutation-free
4723
     */
4724 2
    public function replaceAllKeys(array $keys): self
4725
    {
4726 2
        return static::create(
4727 2
            \array_combine($keys, $this->toArray()),
4728 2
            $this->iteratorClass,
4729 2
            false
4730
        );
4731
    }
4732
4733
    /**
4734
     * Create an array using the current array as keys and the other array as values.
4735
     *
4736
     * @param array $array <p>An array o values.</p>
4737
     *
4738
     * @return static
4739
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4740
     *
4741
     * @psalm-param  array<mixed,T> $array
4742
     * @psalm-return static<TKey,T>
4743
     * @psalm-mutation-free
4744
     */
4745 2
    public function replaceAllValues(array $array): self
4746
    {
4747 2
        return static::create(
4748 2
            \array_combine($this->array, $array),
4749 2
            $this->iteratorClass,
4750 2
            false
4751
        );
4752
    }
4753
4754
    /**
4755
     * Replace the keys in an array with another set.
4756
     *
4757
     * @param array $keys <p>An array of keys matching the array's size</p>
4758
     *
4759
     * @return static
4760
     *                <p>(Immutable)</p>
4761
     *
4762
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4763
     * @psalm-return static<TKey,T>
4764
     * @psalm-mutation-free
4765
     */
4766 1
    public function replaceKeys(array $keys): self
4767
    {
4768 1
        $values = \array_values($this->toArray());
4769 1
        $result = \array_combine($keys, $values);
4770
4771 1
        return static::create(
4772 1
            $result,
4773 1
            $this->iteratorClass,
4774 1
            false
4775
        );
4776
    }
4777
4778
    /**
4779
     * Replace the first matched value in an array.
4780
     *
4781
     * @param mixed $search      <p>The value to replace.</p>
4782
     * @param mixed $replacement <p>The value to replace.</p>
4783
     *
4784
     * @return static
4785
     *                <p>(Immutable)</p>
4786
     *
4787
     * @psalm-return static<TKey,T>
4788
     * @psalm-mutation-free
4789
     */
4790 3
    public function replaceOneValue($search, $replacement = ''): self
4791
    {
4792 3
        $array = $this->toArray();
4793 3
        $key = \array_search($search, $array, true);
4794
4795 3
        if ($key !== false) {
4796 3
            $array[$key] = $replacement;
4797
        }
4798
4799 3
        return static::create(
4800 3
            $array,
4801 3
            $this->iteratorClass,
4802 3
            false
4803
        );
4804
    }
4805
4806
    /**
4807
     * Replace values in the current array.
4808
     *
4809
     * @param mixed $search      <p>The value to replace.</p>
4810
     * @param mixed $replacement <p>What to replace it with.</p>
4811
     *
4812
     * @return static
4813
     *                <p>(Immutable)</p>
4814
     *
4815
     * @psalm-return static<TKey,T>
4816
     * @psalm-mutation-free
4817
     */
4818 1
    public function replaceValues($search, $replacement = ''): self
4819
    {
4820
        /**
4821
         * @psalm-suppress MissingClosureReturnType
4822
         * @psalm-suppress MissingClosureParamType
4823
         */
4824 1
        return $this->each(
4825 1
            static function ($value) use ($search, $replacement) {
4826 1
                return \str_replace($search, $replacement, $value);
4827 1
            }
4828
        );
4829
    }
4830
4831
    /**
4832
     * Get the last elements from index $from until the end of this array.
4833
     *
4834
     * @param int $from
4835
     *
4836
     * @return static
4837
     *                <p>(Immutable)</p>
4838
     *
4839
     * @psalm-return static<TKey,T>
4840
     * @psalm-mutation-free
4841
     */
4842 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...
4843
    {
4844 15
        $tmpArray = $this->toArray();
4845
4846 15
        return static::create(
4847 15
            \array_splice($tmpArray, $from),
4848 15
            $this->iteratorClass,
4849 15
            false
4850
        );
4851
    }
4852
4853
    /**
4854
     * Return the array in the reverse order.
4855
     *
4856
     * @return $this
4857
     *               <p>(Mutable) Return this Arrayy object.</p>
4858
     *
4859
     * @psalm-return static<TKey,T>
4860
     */
4861 9
    public function reverse(): self
4862
    {
4863 9
        $this->generatorToArray();
4864
4865 9
        $this->array = \array_reverse($this->array);
4866
4867 9
        return $this;
4868
    }
4869
4870
    /**
4871
     * Sort an array in reverse order.
4872
     *
4873
     * @param int $sort_flags [optional] <p>
4874
     *                        You may modify the behavior of the sort using the optional
4875
     *                        parameter sort_flags, for details
4876
     *                        see sort.
4877
     *                        </p>
4878
     *
4879
     * @return $this
4880
     *               <p>(Mutable) Return this Arrayy object.</p>
4881
     *
4882
     * @psalm-return static<TKey,T>
4883
     */
4884 4
    public function rsort(int $sort_flags = 0): self
4885
    {
4886 4
        $this->generatorToArray();
4887
4888 4
        \rsort($this->array, $sort_flags);
4889
4890 4
        return $this;
4891
    }
4892
4893
    /**
4894
     * Sort an array in reverse order.
4895
     *
4896
     * @param int $sort_flags [optional] <p>
4897
     *                        You may modify the behavior of the sort using the optional
4898
     *                        parameter sort_flags, for details
4899
     *                        see sort.
4900
     *                        </p>
4901
     *
4902
     * @return $this
4903
     *               <p>(Immutable) Return this Arrayy object.</p>
4904
     *
4905
     * @psalm-return static<TKey,T>
4906
     * @psalm-mutation-free
4907
     */
4908 4
    public function rsortImmutable(int $sort_flags = 0): self
4909
    {
4910 4
        $that = clone $this;
4911
4912
        /**
4913
         * @psalm-suppress ImpureMethodCall - object is already cloned
4914
         */
4915 4
        $that->rsort($sort_flags);
4916
4917 4
        return $that;
4918
    }
4919
4920
    /**
4921
     * Search for the first index of the current array via $value.
4922
     *
4923
     * @param mixed $value
4924
     *
4925
     * @return false|float|int|string
4926
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4927
     * @psalm-mutation-free
4928
     */
4929 21
    public function searchIndex($value)
4930
    {
4931 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
4932 20
            if ($value === $valueFromArray) {
4933 20
                return $keyFromArray;
4934
            }
4935
        }
4936
4937 11
        return false;
4938
    }
4939
4940
    /**
4941
     * Search for the value of the current array via $index.
4942
     *
4943
     * @param mixed $index
4944
     *
4945
     * @return static
4946
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
4947
     *
4948
     * @psalm-return static<TKey,T>
4949
     * @psalm-mutation-free
4950
     */
4951 9
    public function searchValue($index): self
4952
    {
4953 9
        $this->generatorToArray();
4954
4955
        // init
4956 9
        $return = [];
4957
4958 9
        if ($this->array === []) {
4959
            return static::create(
4960
                [],
4961
                $this->iteratorClass,
4962
                false
4963
            );
4964
        }
4965
4966
        // php cast "bool"-index into "int"-index
4967 9
        if ((bool) $index === $index) {
4968 1
            $index = (int) $index;
4969
        }
4970
4971 9
        if ($this->offsetExists($index)) {
4972 7
            $return = [$this->array[$index]];
4973
        }
4974
4975 9
        return static::create(
4976 9
            $return,
4977 9
            $this->iteratorClass,
4978 9
            false
4979
        );
4980
    }
4981
4982
    /**
4983
     * Set a value for the current array (optional using dot-notation).
4984
     *
4985
     * @param string $key   <p>The key to set.</p>
4986
     * @param mixed  $value <p>Its value.</p>
4987
     *
4988
     * @return $this
4989
     *               <p>(Mutable) Return this Arrayy object.</p>
4990
     *
4991
     * @psalm-param  TKey $key
4992
     * @psalm-param  T $value
4993
     * @psalm-return static<TKey,T>
4994
     */
4995 28
    public function set($key, $value): self
4996
    {
4997 28
        $this->internalSet($key, $value);
4998
4999 27
        return $this;
5000
    }
5001
5002
    /**
5003
     * Get a value from a array and set it if it was not.
5004
     *
5005
     * WARNING: this method only set the value, if the $key is not already set
5006
     *
5007
     * @param mixed $key      <p>The key</p>
5008
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5009
     *
5010
     * @return mixed
5011
     *               <p>(Mutable)</p>
5012
     */
5013 11
    public function setAndGet($key, $fallback = null)
5014
    {
5015 11
        $this->generatorToArray();
5016
5017
        // If the key doesn't exist, set it.
5018 11
        if (!$this->has($key)) {
5019 4
            $this->array = $this->set($key, $fallback)->toArray();
5020
        }
5021
5022 11
        return $this->get($key);
5023
    }
5024
5025
    /**
5026
     * Shifts a specified value off the beginning of array.
5027
     *
5028
     * @return mixed
5029
     *               <p>(Mutable) A shifted element from the current array.</p>
5030
     */
5031 5
    public function shift()
5032
    {
5033 5
        $this->generatorToArray();
5034
5035 5
        return \array_shift($this->array);
5036
    }
5037
5038
    /**
5039
     * Shuffle the current array.
5040
     *
5041
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5042
     * @param array $array  [optional]
5043
     *
5044
     * @return static
5045
     *                <p>(Immutable)</p>
5046
     *
5047
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5048
     * @psalm-return static<TKey,T>
5049
     *
5050
     * @noinspection BadExceptionsProcessingInspection
5051
     * @noinspection RandomApiMigrationInspection
5052
     * @noinspection NonSecureShuffleUsageInspection
5053
     */
5054 2
    public function shuffle(bool $secure = false, array $array = null): self
5055
    {
5056 2
        if ($array === null) {
5057 2
            $array = $this->toArray(false);
5058
        }
5059
5060 2
        if ($secure !== true) {
5061 2
            \shuffle($array);
5062
        } else {
5063 1
            $size = \count($array, \COUNT_NORMAL);
5064 1
            $keys = \array_keys($array);
5065 1
            for ($i = $size - 1; $i > 0; --$i) {
5066
                try {
5067 1
                    $r = \random_int(0, $i);
5068
                } catch (\Exception $e) {
5069
                    $r = \mt_rand(0, $i);
5070
                }
5071 1
                if ($r !== $i) {
5072
                    $temp = $array[$keys[$r]];
5073
                    $array[$keys[$r]] = $array[$keys[$i]];
5074
                    $array[$keys[$i]] = $temp;
5075
                }
5076
            }
5077
        }
5078
5079 2
        foreach ($array as $key => $value) {
5080
            // check if recursive is needed
5081 2
            if (\is_array($value) === true) {
5082 2
                $array[$key] = $this->shuffle($secure, $value);
5083
            }
5084
        }
5085
5086 2
        return static::create(
5087 2
            $array,
5088 2
            $this->iteratorClass,
5089 2
            false
5090
        );
5091
    }
5092
5093
    /**
5094
     * Count the values from the current array.
5095
     *
5096
     * alias: for "Arrayy->count()"
5097
     *
5098
     * @param int $mode
5099
     *
5100
     * @return int
5101
     */
5102 20
    public function size(int $mode = \COUNT_NORMAL): int
5103
    {
5104 20
        return $this->count($mode);
5105
    }
5106
5107
    /**
5108
     * Checks whether array has exactly $size items.
5109
     *
5110
     * @param int $size
5111
     *
5112
     * @return bool
5113
     */
5114 1 View Code Duplication
    public function sizeIs(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
5115
    {
5116
        // init
5117 1
        $itemsTempCount = 0;
5118
5119 1
        foreach ($this->getGenerator() as $key => $value) {
5120 1
            ++$itemsTempCount;
5121 1
            if ($itemsTempCount > $size) {
5122 1
                return false;
5123
            }
5124
        }
5125
5126 1
        return $itemsTempCount === $size;
5127
    }
5128
5129
    /**
5130
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5131
     * smaller than $fromSize.
5132
     *
5133
     * @param int $fromSize
5134
     * @param int $toSize
5135
     *
5136
     * @return bool
5137
     */
5138 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5139
    {
5140 1
        if ($fromSize > $toSize) {
5141 1
            $tmp = $toSize;
5142 1
            $toSize = $fromSize;
5143 1
            $fromSize = $tmp;
5144
        }
5145
5146
        // init
5147 1
        $itemsTempCount = 0;
5148
5149 1
        foreach ($this->getGenerator() as $key => $value) {
5150 1
            ++$itemsTempCount;
5151 1
            if ($itemsTempCount > $toSize) {
5152 1
                return false;
5153
            }
5154
        }
5155
5156 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5157
    }
5158
5159
    /**
5160
     * Checks whether array has more than $size items.
5161
     *
5162
     * @param int $size
5163
     *
5164
     * @return bool
5165
     */
5166 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...
5167
    {
5168
        // init
5169 1
        $itemsTempCount = 0;
5170
5171 1
        foreach ($this->getGenerator() as $key => $value) {
5172 1
            ++$itemsTempCount;
5173 1
            if ($itemsTempCount > $size) {
5174 1
                return true;
5175
            }
5176
        }
5177
5178 1
        return $itemsTempCount > $size;
5179
    }
5180
5181
    /**
5182
     * Checks whether array has less than $size items.
5183
     *
5184
     * @param int $size
5185
     *
5186
     * @return bool
5187
     */
5188 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...
5189
    {
5190
        // init
5191 1
        $itemsTempCount = 0;
5192
5193 1
        foreach ($this->getGenerator() as $key => $value) {
5194 1
            ++$itemsTempCount;
5195 1
            if ($itemsTempCount > $size) {
5196 1
                return false;
5197
            }
5198
        }
5199
5200 1
        return $itemsTempCount < $size;
5201
    }
5202
5203
    /**
5204
     * Counts all elements in an array, or something in an object.
5205
     *
5206
     * <p>
5207
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5208
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5209
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5210
     * implemented and used in PHP.
5211
     * </p>
5212
     *
5213
     * @return int
5214
     *             <p>
5215
     *             The number of elements in var, which is
5216
     *             typically an array, since anything else will have one
5217
     *             element.
5218
     *             </p>
5219
     *             <p>
5220
     *             If var is not an array or an object with
5221
     *             implemented Countable interface,
5222
     *             1 will be returned.
5223
     *             There is one exception, if var is &null;,
5224
     *             0 will be returned.
5225
     *             </p>
5226
     *             <p>
5227
     *             Caution: count may return 0 for a variable that isn't set,
5228
     *             but it may also return 0 for a variable that has been initialized with an
5229
     *             empty array. Use isset to test if a variable is set.
5230
     *             </p>
5231
     */
5232 10
    public function sizeRecursive(): int
5233
    {
5234 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5235
    }
5236
5237
    /**
5238
     * Extract a slice of the array.
5239
     *
5240
     * @param int      $offset       <p>Slice begin index.</p>
5241
     * @param int|null $length       <p>Length of the slice.</p>
5242
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5243
     *
5244
     * @return static
5245
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5246
     *
5247
     * @psalm-return static<TKey,T>
5248
     * @psalm-mutation-free
5249
     */
5250 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5251
    {
5252 5
        return static::create(
5253 5
            \array_slice(
5254 5
                $this->toArray(),
5255 5
                $offset,
5256 5
                $length,
5257 5
                $preserveKeys
5258
            ),
5259 5
            $this->iteratorClass,
5260 5
            false
5261
        );
5262
    }
5263
5264
    /**
5265
     * Sort the current array and optional you can keep the keys.
5266
     *
5267
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5268
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5269
     *                              <strong>SORT_NATURAL</strong></p>
5270
     * @param bool       $keepKeys
5271
     *
5272
     * @return static
5273
     *                <p>(Mutable) Return this Arrayy object.</p>
5274
     *
5275
     * @psalm-return static<TKey,T>
5276
     */
5277 20
    public function sort(
5278
        $direction = \SORT_ASC,
5279
        int $strategy = \SORT_REGULAR,
5280
        bool $keepKeys = false
5281
    ): self {
5282 20
        $this->generatorToArray();
5283
5284 20
        return $this->sorting(
5285 20
            $this->array,
5286 20
            $direction,
5287 20
            $strategy,
5288 20
            $keepKeys
5289
        );
5290
    }
5291
5292
    /**
5293
     * Sort the current array and optional you can keep the keys.
5294
     *
5295
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5296
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5297
     *                              <strong>SORT_NATURAL</strong></p>
5298
     * @param bool       $keepKeys
5299
     *
5300
     * @return static
5301
     *                <p>(Immutable) Return this Arrayy object.</p>
5302
     *
5303
     * @psalm-return static<TKey,T>
5304
     */
5305 12
    public function sortImmutable(
5306
        $direction = \SORT_ASC,
5307
        int $strategy = \SORT_REGULAR,
5308
        bool $keepKeys = false
5309
    ): self {
5310 12
        $that = clone $this;
5311
5312 12
        $that->generatorToArray();
5313
5314 12
        return $that->sorting(
5315 12
            $that->array,
5316 12
            $direction,
5317 12
            $strategy,
5318 12
            $keepKeys
5319
        );
5320
    }
5321
5322
    /**
5323
     * Sort the current array by key.
5324
     *
5325
     * @see          http://php.net/manual/en/function.ksort.php
5326
     * @see          http://php.net/manual/en/function.krsort.php
5327
     *
5328
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5329
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5330
     *                              <strong>SORT_NATURAL</strong></p>
5331
     *
5332
     * @return $this
5333
     *               <p>(Mutable) Return this Arrayy object.</p>
5334
     *
5335
     * @psalm-return static<TKey,T>
5336
     */
5337 18
    public function sortKeys(
5338
        $direction = \SORT_ASC,
5339
        int $strategy = \SORT_REGULAR
5340
    ): self {
5341 18
        $this->generatorToArray();
5342
5343 18
        $this->sorterKeys($this->array, $direction, $strategy);
5344
5345 18
        return $this;
5346
    }
5347
5348
    /**
5349
     * Sort the current array by key.
5350
     *
5351
     * @see          http://php.net/manual/en/function.ksort.php
5352
     * @see          http://php.net/manual/en/function.krsort.php
5353
     *
5354
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5355
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5356
     *                              <strong>SORT_NATURAL</strong></p>
5357
     *
5358
     * @return $this
5359
     *               <p>(Immutable) Return this Arrayy object.</p>
5360
     *
5361
     * @psalm-return static<TKey,T>
5362
     * @psalm-mutation-free
5363
     */
5364 8
    public function sortKeysImmutable(
5365
        $direction = \SORT_ASC,
5366
        int $strategy = \SORT_REGULAR
5367
    ): self {
5368 8
        $that = clone $this;
5369
5370
        /**
5371
         * @psalm-suppress ImpureMethodCall - object is already cloned
5372
         */
5373 8
        $that->sortKeys($direction, $strategy);
5374
5375 8
        return $that;
5376
    }
5377
5378
    /**
5379
     * Sort the current array by value.
5380
     *
5381
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5382
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5383
     *                              <strong>SORT_NATURAL</strong></p>
5384
     *
5385
     * @return static
5386
     *                <p>(Mutable)</p>
5387
     *
5388
     * @psalm-return static<TKey,T>
5389
     */
5390 1
    public function sortValueKeepIndex(
5391
        $direction = \SORT_ASC,
5392
        int $strategy = \SORT_REGULAR
5393
    ): self {
5394 1
        return $this->sort($direction, $strategy, true);
5395
    }
5396
5397
    /**
5398
     * Sort the current array by value.
5399
     *
5400
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5401
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5402
     *                              <strong>SORT_NATURAL</strong></p>
5403
     *
5404
     * @return static
5405
     *                <p>(Mutable)</p>
5406
     *
5407
     * @psalm-return static<TKey,T>
5408
     */
5409 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5410
    {
5411 1
        return $this->sort($direction, $strategy, false);
5412
    }
5413
5414
    /**
5415
     * Sort a array by value, by a closure or by a property.
5416
     *
5417
     * - If the sorter is null, the array is sorted naturally.
5418
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5419
     *
5420
     * @param callable|string|null $sorter
5421
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5422
     *                                        <strong>SORT_DESC</strong></p>
5423
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5424
     *                                        <strong>SORT_NATURAL</strong></p>
5425
     *
5426
     * @return static
5427
     *                <p>(Immutable)</p>
5428
     *
5429
     * @psalm-return static<TKey,T>
5430
     * @psalm-mutation-free
5431
     */
5432 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5433
    {
5434 1
        $array = $this->toArray();
5435 1
        $direction = $this->getDirection($direction);
5436
5437
        // Transform all values into their results.
5438 1
        if ($sorter) {
5439 1
            $arrayy = static::create(
5440 1
                $array,
5441 1
                $this->iteratorClass,
5442 1
                false
5443
            );
5444
5445
            /**
5446
             * @psalm-suppress MissingClosureReturnType
5447
             * @psalm-suppress MissingClosureParamType
5448
             */
5449 1
            $results = $arrayy->each(
5450 1
                function ($value) use ($sorter) {
5451 1
                    if (\is_callable($sorter) === true) {
5452 1
                        return $sorter($value);
5453
                    }
5454
5455 1
                    return $this->get($sorter);
5456 1
                }
5457
            );
5458
5459 1
            $results = $results->toArray();
5460
        } else {
5461 1
            $results = $array;
5462
        }
5463
5464
        // Sort by the results and replace by original values
5465 1
        \array_multisort($results, $direction, $strategy, $array);
5466
5467 1
        return static::create(
5468 1
            $array,
5469 1
            $this->iteratorClass,
5470 1
            false
5471
        );
5472
    }
5473
5474
    /**
5475
     * @param int      $offset
5476
     * @param int|null $length
5477
     * @param array    $replacement
5478
     *
5479
     * @return static
5480
     *                <p>(Immutable)</p>
5481
     *
5482
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5483
     * @psalm-return static<TKey,T>
5484
     * @psalm-mutation-free
5485
     */
5486 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5487
    {
5488 1
        $tmpArray = $this->toArray();
5489
5490 1
        \array_splice(
5491 1
            $tmpArray,
5492 1
            $offset,
5493 1
            $length ?? $this->count(),
5494 1
            $replacement
5495
        );
5496
5497 1
        return static::create(
5498 1
            $tmpArray,
5499 1
            $this->iteratorClass,
5500 1
            false
5501
        );
5502
    }
5503
5504
    /**
5505
     * Split an array in the given amount of pieces.
5506
     *
5507
     * @param int  $numberOfPieces
5508
     * @param bool $keepKeys
5509
     *
5510
     * @return static
5511
     *                <p>(Immutable)</p>
5512
     *
5513
     * @psalm-return static<TKey,T>
5514
     * @psalm-mutation-free
5515
     */
5516 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5517
    {
5518 1
        $this->generatorToArray();
5519
5520 1
        $count = $this->count();
5521
5522 1
        if ($count === 0) {
5523 1
            $result = [];
5524
        } else {
5525 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5526 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5527
        }
5528
5529 1
        return static::create(
5530 1
            $result,
5531 1
            $this->iteratorClass,
5532 1
            false
5533
        );
5534
    }
5535
5536
    /**
5537
     * Stripe all empty items.
5538
     *
5539
     * @return static
5540
     *                <p>(Immutable)</p>
5541
     *
5542
     * @psalm-return static<TKey,T>
5543
     * @psalm-mutation-free
5544
     */
5545 1
    public function stripEmpty(): self
5546
    {
5547 1
        return $this->filter(
5548 1
            static function ($item) {
5549 1
                if ($item === null) {
5550 1
                    return false;
5551
                }
5552
5553 1
                return (bool) \trim((string) $item);
5554 1
            }
5555
        );
5556
    }
5557
5558
    /**
5559
     * Swap two values between positions by key.
5560
     *
5561
     * @param int|string $swapA <p>a key in the array</p>
5562
     * @param int|string $swapB <p>a key in the array</p>
5563
     *
5564
     * @return static
5565
     *                <p>(Immutable)</p>
5566
     *
5567
     * @psalm-return static<TKey,T>
5568
     * @psalm-mutation-free
5569
     */
5570 1
    public function swap($swapA, $swapB): self
5571
    {
5572 1
        $array = $this->toArray();
5573
5574 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5575
5576 1
        return static::create(
5577 1
            $array,
5578 1
            $this->iteratorClass,
5579 1
            false
5580
        );
5581
    }
5582
5583
    /**
5584
     * Get the current array from the "Arrayy"-object.
5585
     * alias for "getArray()"
5586
     *
5587
     * @param bool $convertAllArrayyElements <p>
5588
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5589
     *                                       </p>
5590
     * @param bool $preserveKeys             <p>
5591
     *                                       e.g.: A generator maybe return the same key more then once,
5592
     *                                       so maybe you will ignore the keys.
5593
     *                                       </p>
5594
     *
5595
     * @return array
5596
     *
5597
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5598
     * @psalm-mutation-free
5599
     */
5600 931
    public function toArray(
5601
        bool $convertAllArrayyElements = false,
5602
        bool $preserveKeys = true
5603
    ): array {
5604
        // init
5605 931
        $array = [];
5606
5607 931
        if ($convertAllArrayyElements) {
5608 2
            foreach ($this->getGenerator() as $key => $value) {
5609 2
                if ($value instanceof self) {
5610 1
                    $value = $value->toArray(true);
5611
                }
5612
5613 2
                if ($preserveKeys) {
5614 1
                    $array[$key] = $value;
5615
                } else {
5616 2
                    $array[] = $value;
5617
                }
5618
            }
5619
        } else {
5620 931
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5621
        }
5622
5623 931
        return $array;
5624
    }
5625
5626
    /**
5627
     * Get the current array from the "Arrayy"-object as list.
5628
     *
5629
     * @param bool $convertAllArrayyElements <p>
5630
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5631
     *                                       </p>
5632
     *
5633
     * @return array
5634
     *
5635
     * @psalm-return array<int,mixed>|array<int,T>
5636
     * @psalm-mutation-free
5637
     */
5638 1
    public function toList(bool $convertAllArrayyElements = false): array
5639
    {
5640 1
        return $this->toArray(
5641 1
            $convertAllArrayyElements,
5642 1
            false
5643
        );
5644
    }
5645
5646
    /**
5647
     * Convert the current array to JSON.
5648
     *
5649
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
5650
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
5651
     *
5652
     * @return string
5653
     */
5654 11
    public function toJson(int $options = 0, int $depth = 512): string
5655
    {
5656 11
        $return = \json_encode($this->toArray(), $options, $depth);
5657 11
        if ($return === false) {
5658
            return '';
5659
        }
5660
5661 11
        return $return;
5662
    }
5663
5664
    /**
5665
     * @param string[]|null $items  [optional]
5666
     * @param string[]      $helper [optional]
5667
     *
5668
     * @return static|static[]
5669
     *
5670
     * @psalm-return static<int, static<TKey,T>>
5671
     */
5672 1
    public function toPermutation(array $items = null, array $helper = []): self
5673
    {
5674
        // init
5675 1
        $return = [];
5676
5677 1
        if ($items === null) {
5678 1
            $items = $this->toArray();
5679
        }
5680
5681 1
        if (empty($items)) {
5682 1
            $return[] = $helper;
5683
        } else {
5684 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5685 1
                $new_items = $items;
5686 1
                $new_helper = $helper;
5687 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5688
                /** @noinspection PhpSillyAssignmentInspection */
5689
                /** @var string[] $new_items */
5690 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...
5691 1
                \array_unshift($new_helper, $tmp_helper);
5692
                /** @noinspection SlowArrayOperationsInLoopInspection */
5693 1
                $return = \array_merge(
5694 1
                    $return,
5695 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5696
                );
5697
            }
5698
        }
5699
5700 1
        return static::create(
5701 1
            $return,
5702 1
            $this->iteratorClass,
5703 1
            false
5704
        );
5705
    }
5706
5707
    /**
5708
     * Implodes array to a string with specified separator.
5709
     *
5710
     * @param string $separator [optional] <p>The element's separator.</p>
5711
     *
5712
     * @return string
5713
     *                <p>The string representation of array, separated by ",".</p>
5714
     */
5715 19
    public function toString(string $separator = ','): string
5716
    {
5717 19
        return $this->implode($separator);
5718
    }
5719
5720
    /**
5721
     * Return a duplicate free copy of the current array.
5722
     *
5723
     * @return $this
5724
     *               <p>(Mutable)</p>
5725
     *
5726
     * @psalm-return static<TKey,T>
5727
     */
5728 13
    public function unique(): self
5729
    {
5730
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5731
5732
        /**
5733
         * @psalm-suppress MissingClosureReturnType
5734
         * @psalm-suppress MissingClosureParamType
5735
         */
5736 13
        $this->array = $this->reduce(
5737 13
            static function ($resultArray, $value) {
5738 12
                if (!\in_array($value, $resultArray, true)) {
5739 12
                    $resultArray[] = $value;
5740
                }
5741
5742 12
                return $resultArray;
5743 13
            },
5744 13
            []
5745 13
        )->toArray();
5746 13
        $this->generator = null;
5747
5748 13
        return $this;
5749
    }
5750
5751
    /**
5752
     * Return a duplicate free copy of the current array. (with the old keys)
5753
     *
5754
     * @return $this
5755
     *               <p>(Mutable)</p>
5756
     *
5757
     * @psalm-return static<TKey,T>
5758
     */
5759 11
    public function uniqueKeepIndex(): self
5760
    {
5761
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5762
5763
        // init
5764 11
        $array = $this->toArray();
5765
5766
        /**
5767
         * @psalm-suppress MissingClosureReturnType
5768
         * @psalm-suppress MissingClosureParamType
5769
         */
5770 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...
5771 11
            \array_keys($array),
5772 11
            static function ($resultArray, $key) use ($array) {
5773 10
                if (!\in_array($array[$key], $resultArray, true)) {
5774 10
                    $resultArray[$key] = $array[$key];
5775
                }
5776
5777 10
                return $resultArray;
5778 11
            },
5779 11
            []
5780
        );
5781 11
        $this->generator = null;
5782
5783 11
        return $this;
5784
    }
5785
5786
    /**
5787
     * alias: for "Arrayy->unique()"
5788
     *
5789
     * @return static
5790
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
5791
     *
5792
     * @see          Arrayy::unique()
5793
     *
5794
     * @psalm-return static<TKey,T>
5795
     */
5796 10
    public function uniqueNewIndex(): self
5797
    {
5798 10
        return $this->unique();
5799
    }
5800
5801
    /**
5802
     * Prepends one or more values to the beginning of array at once.
5803
     *
5804
     * @param array ...$args
5805
     *
5806
     * @return $this
5807
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
5808
     *
5809
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
5810
     * @psalm-return static<TKey,T>
5811
     */
5812 4
    public function unshift(...$args): self
5813
    {
5814 4
        $this->generatorToArray();
5815
5816 4
        \array_unshift($this->array, ...$args);
5817
5818 4
        return $this;
5819
    }
5820
5821
    /**
5822
     * Tests whether the given closure return something valid for all elements of this array.
5823
     *
5824
     * @param \Closure $closure the predicate
5825
     *
5826
     * @return bool
5827
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
5828
     */
5829 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...
5830
    {
5831 1
        foreach ($this->getGenerator() as $key => $value) {
5832 1
            if (!$closure($value, $key)) {
5833 1
                return false;
5834
            }
5835
        }
5836
5837 1
        return true;
5838
    }
5839
5840
    /**
5841
     * Get all values from a array.
5842
     *
5843
     * @return static
5844
     *                <p>(Immutable)</p>
5845
     *
5846
     * @psalm-return static<TKey,T>
5847
     * @psalm-mutation-free
5848
     */
5849 2
    public function values(): self
5850
    {
5851 2
        return static::create(
5852 2
            function () {
5853
                /** @noinspection YieldFromCanBeUsedInspection */
5854 2
                foreach ($this->getGenerator() as $value) {
5855 2
                    yield $value;
5856
                }
5857 2
            },
5858 2
            $this->iteratorClass,
5859 2
            false
5860
        );
5861
    }
5862
5863
    /**
5864
     * Apply the given function to every element in the array, discarding the results.
5865
     *
5866
     * @param callable $callable
5867
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
5868
     *
5869
     * @return $this
5870
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
5871
     *
5872
     * @psalm-return static<TKey,T>
5873
     */
5874 12
    public function walk($callable, bool $recursive = false): self
5875
    {
5876 12
        $this->generatorToArray();
5877
5878 12
        if ($this->array !== []) {
5879 10
            if ($recursive === true) {
5880 5
                \array_walk_recursive($this->array, $callable);
5881
            } else {
5882 5
                \array_walk($this->array, $callable);
5883
            }
5884
        }
5885
5886 12
        return $this;
5887
    }
5888
5889
    /**
5890
     * Returns a collection of matching items.
5891
     *
5892
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
5893
     * @param mixed  $value                 the value to match
5894
     *
5895
     * @throws \InvalidArgumentException if property or method is not defined
5896
     *
5897
     * @return static
5898
     *
5899
     * @psalm-param  T $value
5900
     * @psalm-return static<TKey,T>
5901
     */
5902 1
    public function where(string $keyOrPropertyOrMethod, $value): self
5903
    {
5904 1
        return $this->filter(
5905 1
            function ($item) use ($keyOrPropertyOrMethod, $value) {
5906 1
                $accessorValue = $this->extractValue(
5907 1
                    $item,
5908 1
                    $keyOrPropertyOrMethod
5909
                );
5910
5911 1
                return $accessorValue === $value;
5912 1
            }
5913
        );
5914
    }
5915
5916
    /**
5917
     * Convert an array into a object.
5918
     *
5919
     * @param array $array
5920
     *
5921
     * @return \stdClass
5922
     *
5923
     * @psalm-param array<mixed,mixed> $array
5924
     */
5925 4
    final protected static function arrayToObject(array $array = []): \stdClass
5926
    {
5927
        // init
5928 4
        $object = new \stdClass();
5929
5930 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
5931 1
            return $object;
5932
        }
5933
5934 3
        foreach ($array as $name => $value) {
5935 3
            if (\is_array($value) === true) {
5936 1
                $object->{$name} = static::arrayToObject($value);
5937
            } else {
5938 3
                $object->{$name} = $value;
5939
            }
5940
        }
5941
5942 3
        return $object;
5943
    }
5944
5945
    /**
5946
     * @param array|\Generator|null $input         <p>
5947
     *                                             An array containing keys to return.
5948
     *                                             </p>
5949
     * @param mixed|null            $search_values [optional] <p>
5950
     *                                             If specified, then only keys containing these values are returned.
5951
     *                                             </p>
5952
     * @param bool                  $strict        [optional] <p>
5953
     *                                             Determines if strict comparison (===) should be used during the
5954
     *                                             search.
5955
     *                                             </p>
5956
     *
5957
     * @return array
5958
     *               <p>an array of all the keys in input</p>
5959
     *
5960
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
5961
     * @psalm-return array<TKey|mixed>
5962
     * @psalm-mutation-free
5963
     */
5964 11
    protected function array_keys_recursive(
5965
        $input = null,
5966
        $search_values = null,
5967
        bool $strict = true
5968
    ): array {
5969
        // init
5970 11
        $keys = [];
5971 11
        $keysTmp = [];
5972
5973 11
        if ($input === null) {
5974 4
            $input = $this->getGenerator();
5975
        }
5976
5977 11
        if ($search_values === null) {
5978 11
            foreach ($input as $key => $value) {
5979 11
                $keys[] = $key;
5980
5981
                // check if recursive is needed
5982 11
                if (\is_array($value) === true) {
5983 11
                    $keysTmp[] = $this->array_keys_recursive($value);
5984
                }
5985
            }
5986
        } else {
5987 1
            $is_array_tmp = \is_array($search_values);
5988
5989 1
            foreach ($input as $key => $value) {
5990 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...
5991
                    (
5992 1
                        $is_array_tmp === false
5993
                        &&
5994 1
                        $strict === true
5995
                        &&
5996 1
                        $search_values === $value
5997
                    )
5998
                    ||
5999
                    (
6000 1
                        $is_array_tmp === false
6001
                        &&
6002 1
                        $strict === false
6003
                        &&
6004 1
                        $search_values == $value
6005
                    )
6006
                    ||
6007
                    (
6008 1
                        $is_array_tmp === true
6009
                        &&
6010 1
                        \in_array($value, $search_values, $strict)
6011
                    )
6012
                ) {
6013 1
                    $keys[] = $key;
6014
                }
6015
6016
                // check if recursive is needed
6017 1
                if (\is_array($value) === true) {
6018 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6019
                }
6020
            }
6021
        }
6022
6023 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6024
    }
6025
6026
    /**
6027
     * @param mixed      $path
6028
     * @param callable   $callable
6029
     * @param array|null $currentOffset
6030
     *
6031
     * @return void
6032
     *
6033
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6034
     * @psalm-mutation-free
6035
     */
6036 11
    protected function callAtPath($path, $callable, &$currentOffset = null)
6037
    {
6038 11
        $this->generatorToArray();
6039
6040 11
        if ($currentOffset === null) {
6041 11
            $currentOffset = &$this->array;
6042
        }
6043
6044 11
        $explodedPath = \explode($this->pathSeparator, $path);
6045 11
        if ($explodedPath === false) {
6046
            return;
6047
        }
6048
6049 11
        $nextPath = \array_shift($explodedPath);
6050
6051 11
        if (!isset($currentOffset[$nextPath])) {
6052 1
            return;
6053
        }
6054
6055 10
        if (!empty($explodedPath)) {
6056 1
            $this->callAtPath(
6057 1
                \implode($this->pathSeparator, $explodedPath),
6058 1
                $callable,
6059 1
                $currentOffset[$nextPath]
6060
            );
6061
        } else {
6062 10
            $callable($currentOffset[$nextPath]);
6063
        }
6064 10
    }
6065
6066
    /**
6067
     * Extracts the value of the given property or method from the object.
6068
     *
6069
     * @param static $object                <p>The object to extract the value from.</p>
6070
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
6071
     *                                      value should be extracted.</p>
6072
     *
6073
     * @throws \InvalidArgumentException if the method or property is not defined
6074
     *
6075
     * @return mixed
6076
     *               <p>The value extracted from the specified property or method.</p>
6077
     *
6078
     * @psalm-param self<TKey,T> $object
6079
     */
6080 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
6081
    {
6082 2
        if (isset($object[$keyOrPropertyOrMethod])) {
6083 2
            $return = $object->get($keyOrPropertyOrMethod);
6084
6085 2
            if ($return instanceof self) {
6086 1
                return $return->toArray();
6087
            }
6088
6089 1
            return $return;
6090
        }
6091
6092
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
6093
            return $object->{$keyOrPropertyOrMethod};
6094
        }
6095
6096
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
6097
            return $object->{$keyOrPropertyOrMethod}();
6098
        }
6099
6100
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6101
    }
6102
6103
    /**
6104
     * create a fallback for array
6105
     *
6106
     * 1. use the current array, if it's a array
6107
     * 2. fallback to empty array, if there is nothing
6108
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6109
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6110
     * 5. call "__toArray()" on object, if the method exists
6111
     * 6. cast a string or object with "__toString()" into an array
6112
     * 7. throw a "InvalidArgumentException"-Exception
6113
     *
6114
     * @param mixed $data
6115
     *
6116
     * @throws \InvalidArgumentException
6117
     *
6118
     * @return array
6119
     *
6120
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6121
     */
6122 1173
    protected function fallbackForArray(&$data): array
6123
    {
6124 1173
        $data = $this->internalGetArray($data);
6125
6126 1173
        if ($data === null) {
6127 2
            throw new \InvalidArgumentException('Passed value should be a array');
6128
        }
6129
6130 1171
        return $data;
6131
    }
6132
6133
    /**
6134
     * @param bool $preserveKeys <p>
6135
     *                           e.g.: A generator maybe return the same key more then once,
6136
     *                           so maybe you will ignore the keys.
6137
     *                           </p>
6138
     *
6139
     * @return bool
6140
     *
6141
     * @noinspection ReturnTypeCanBeDeclaredInspection
6142
     * @psalm-mutation-free :/
6143
     */
6144 1085
    protected function generatorToArray(bool $preserveKeys = true)
6145
    {
6146 1085
        if ($this->generator) {
6147 2
            $this->array = $this->toArray(false, $preserveKeys);
6148 2
            $this->generator = null;
6149
6150 2
            return true;
6151
        }
6152
6153 1085
        return false;
6154
    }
6155
6156
    /**
6157
     * Get correct PHP constant for direction.
6158
     *
6159
     * @param int|string $direction
6160
     *
6161
     * @return int
6162
     * @psalm-mutation-free
6163
     */
6164 43
    protected function getDirection($direction): int
6165
    {
6166 43
        if ((string) $direction === $direction) {
6167 10
            $direction = \strtolower($direction);
6168
6169 10
            if ($direction === 'desc') {
6170 2
                $direction = \SORT_DESC;
6171
            } else {
6172 8
                $direction = \SORT_ASC;
6173
            }
6174
        }
6175
6176
        if (
6177 43
            $direction !== \SORT_DESC
6178
            &&
6179 43
            $direction !== \SORT_ASC
6180
        ) {
6181
            $direction = \SORT_ASC;
6182
        }
6183
6184 43
        return $direction;
6185
    }
6186
6187
    /**
6188
     * @return TypeCheckPhpDoc[]
6189
     *
6190
     * @noinspection ReturnTypeCanBeDeclaredInspection
6191
     */
6192 16
    protected function getPropertiesFromPhpDoc()
6193
    {
6194 16
        static $PROPERTY_CACHE = [];
6195 16
        $cacheKey = 'Class::' . static::class;
6196
6197 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6198 15
            return $PROPERTY_CACHE[$cacheKey];
6199
        }
6200
6201
        // init
6202 2
        $properties = [];
6203
6204 2
        $reflector = new \ReflectionClass($this);
6205 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6206 2
        $docComment = $reflector->getDocComment();
6207 2
        if ($docComment) {
6208 2
            $docblock = $factory->create($docComment);
6209
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6210 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6211 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
6212
            }
6213
        }
6214
6215 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6216
    }
6217
6218
    /**
6219
     * @param mixed $glue
6220
     * @param mixed $pieces
6221
     * @param bool  $useKeys
6222
     *
6223
     * @return string
6224
     * @psalm-mutation-free
6225
     */
6226 36
    protected function implode_recursive(
6227
        $glue = '',
6228
        $pieces = [],
6229
        bool $useKeys = false
6230
    ): string {
6231 36
        if ($pieces instanceof self) {
6232 1
            $pieces = $pieces->toArray();
6233
        }
6234
6235 36
        if (\is_array($pieces) === true) {
6236 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6237 36
            $pieces_count_not_zero = $pieces_count > 0;
6238
6239 36
            return \implode(
6240 36
                $glue,
6241 36
                \array_map(
6242 36
                    [$this, 'implode_recursive'],
6243 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6244 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6245
                )
6246
            );
6247
        }
6248
6249
        if (
6250 36
            \is_scalar($pieces) === true
6251
            ||
6252 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6253
        ) {
6254 32
            return (string) $pieces;
6255
        }
6256
6257 8
        return '';
6258
    }
6259
6260
    /**
6261
     * @param mixed                 $needle   <p>
6262
     *                                        The searched value.
6263
     *                                        </p>
6264
     *                                        <p>
6265
     *                                        If needle is a string, the comparison is done
6266
     *                                        in a case-sensitive manner.
6267
     *                                        </p>
6268
     * @param array|\Generator|null $haystack <p>
6269
     *                                        The array.
6270
     *                                        </p>
6271
     * @param bool                  $strict   [optional] <p>
6272
     *                                        If the third parameter strict is set to true
6273
     *                                        then the in_array function will also check the
6274
     *                                        types of the
6275
     *                                        needle in the haystack.
6276
     *                                        </p>
6277
     *
6278
     * @return bool
6279
     *              <p>true if needle is found in the array, false otherwise</p>
6280
     *
6281
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6282
     * @psalm-mutation-free
6283
     */
6284 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6285
    {
6286 18
        if ($haystack === null) {
6287
            $haystack = $this->getGenerator();
6288
        }
6289
6290 18
        foreach ($haystack as $item) {
6291 14
            if (\is_array($item) === true) {
6292 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6293
            } else {
6294
                /** @noinspection NestedPositiveIfStatementsInspection */
6295 14
                if ($strict === true) {
6296 14
                    $returnTmp = $item === $needle;
6297
                } else {
6298
                    $returnTmp = $item == $needle;
6299
                }
6300
            }
6301
6302 14
            if ($returnTmp === true) {
6303 14
                return true;
6304
            }
6305
        }
6306
6307 8
        return false;
6308
    }
6309
6310
    /**
6311
     * @param mixed $data
6312
     *
6313
     * @return array|null
6314
     *
6315
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6316
     */
6317 1173
    protected function internalGetArray(&$data)
6318
    {
6319 1173
        if (\is_array($data) === true) {
6320 1167
            return $data;
6321
        }
6322
6323 56
        if (!$data) {
6324 6
            return [];
6325
        }
6326
6327 55
        if (\is_object($data) === true) {
6328 49
            if ($data instanceof \ArrayObject) {
6329 5
                return $data->getArrayCopy();
6330
            }
6331
6332 45
            if ($data instanceof \Generator) {
6333
                return static::createFromGeneratorImmutable($data)->toArray();
6334
            }
6335
6336 45
            if ($data instanceof \Traversable) {
6337
                return static::createFromObject($data)->toArray();
6338
            }
6339
6340 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...
6341
                return (array) $data->jsonSerialize();
6342
            }
6343
6344 45
            if (\method_exists($data, '__toArray')) {
6345
                return (array) $data->__toArray();
6346
            }
6347
6348 45
            if (\method_exists($data, '__toString')) {
6349
                return [(string) $data];
6350
            }
6351
        }
6352
6353 51
        if (\is_callable($data)) {
6354
            /**
6355
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6356
             */
6357 43
            $this->generator = new ArrayyRewindableGenerator($data);
6358
6359 43
            return [];
6360
        }
6361
6362 10
        if (\is_scalar($data)) {
6363 8
            return [$data];
6364
        }
6365
6366 2
        return null;
6367
    }
6368
6369
    /**
6370
     * Internal mechanics of remove method.
6371
     *
6372
     * @param mixed $key
6373
     *
6374
     * @return bool
6375
     */
6376 21
    protected function internalRemove($key): bool
6377
    {
6378 21
        $this->generatorToArray();
6379
6380
        if (
6381 21
            $this->pathSeparator
6382
            &&
6383 21
            (string) $key === $key
6384
            &&
6385 21
            \strpos($key, $this->pathSeparator) !== false
6386
        ) {
6387
            $path = \explode($this->pathSeparator, (string) $key);
6388
6389
            if ($path !== false) {
6390
                // crawl though the keys
6391
                while (\count($path, \COUNT_NORMAL) > 1) {
6392
                    $key = \array_shift($path);
6393
6394
                    if (!$this->has($key)) {
6395
                        return false;
6396
                    }
6397
6398
                    $this->array = &$this->array[$key];
6399
                }
6400
6401
                $key = \array_shift($path);
6402
            }
6403
        }
6404
6405 21
        unset($this->array[$key]);
6406
6407 21
        return true;
6408
    }
6409
6410
    /**
6411
     * Internal mechanic of set method.
6412
     *
6413
     * @param int|string|null $key
6414
     * @param mixed           $value
6415
     * @param bool            $checkProperties
6416
     *
6417
     * @return bool
6418
     */
6419 1024
    protected function internalSet(
6420
        $key,
6421
        &$value,
6422
        bool $checkProperties = true
6423
    ): bool {
6424
        if (
6425 1024
            $checkProperties === true
6426
            &&
6427 1024
            $this->properties !== []
6428
        ) {
6429 87
            $this->checkType($key, $value);
6430
        }
6431
6432 1022
        if ($key === null) {
6433
            return false;
6434
        }
6435
6436 1022
        $this->generatorToArray();
6437
6438
        /** @var array<int|string,mixed> $array */
6439 1022
        $array = &$this->array;
6440
6441
        /**
6442
         * https://github.com/vimeo/psalm/issues/2536
6443
         *
6444
         * @psalm-suppress PossiblyInvalidArgument
6445
         * @psalm-suppress InvalidScalarArgument
6446
         */
6447
        if (
6448 1022
            $this->pathSeparator
6449
            &&
6450 1022
            (string) $key === $key
6451
            &&
6452 1022
            \strpos($key, $this->pathSeparator) !== false
6453
        ) {
6454 9
            $path = \explode($this->pathSeparator, (string) $key);
6455
6456 9
            if ($path !== false) {
6457
                // crawl through the keys
6458 9
                while (\count($path, \COUNT_NORMAL) > 1) {
6459 9
                    $key = \array_shift($path);
6460
6461 9
                    $array = &$array[$key];
6462
                }
6463
6464 9
                $key = \array_shift($path);
6465
            }
6466
        }
6467
6468 1022
        if ($array === null) {
6469 4
            $array = [];
6470 1019
        } elseif (!\is_array($array)) {
6471 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
6472
        }
6473
6474 1022
        $array[$key] = $value;
6475
6476 1022
        return true;
6477
    }
6478
6479
    /**
6480
     * Convert a object into an array.
6481
     *
6482
     * @param mixed|object $object
6483
     *
6484
     * @return array|mixed
6485
     *
6486
     * @psalm-mutation-free
6487
     */
6488 5
    protected static function objectToArray($object)
6489
    {
6490 5
        if (!\is_object($object)) {
6491 4
            return $object;
6492
        }
6493
6494 5
        $object = \get_object_vars($object);
6495
6496
        /**
6497
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6498
         */
6499 5
        return \array_map(['static', 'objectToArray'], $object);
6500
    }
6501
6502
    /**
6503
     * @param array $data
6504
     * @param bool  $checkPropertiesInConstructor
6505
     *
6506
     * @return void
6507
     *
6508
     * @psalm-param array<mixed,T> $data
6509
     */
6510 1171
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6511
    {
6512 1171
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6513
                                        &&
6514 1171
                                        $checkPropertiesInConstructor === true;
6515
6516 1171
        if ($this->properties !== []) {
6517 78
            foreach ($data as $key => &$valueInner) {
6518 78
                $this->internalSet(
6519 78
                    $key,
6520 78
                    $valueInner,
6521 78
                    $checkPropertiesInConstructor
6522
                );
6523
            }
6524
        } else {
6525
            if (
6526 1104
                $this->checkPropertyTypes === true
6527
                ||
6528 1104
                $checkPropertiesInConstructor === true
6529
            ) {
6530 16
                $this->properties = $this->getPropertiesFromPhpDoc();
6531
            }
6532
6533
            /** @var TypeCheckInterface[] $properties */
6534 1104
            $properties = $this->properties;
6535
6536
            if (
6537 1104
                $this->checkPropertiesMismatchInConstructor === true
6538
                &&
6539 1104
                \count($data) !== 0
6540
                &&
6541 1104
                \count(\array_diff_key($properties, $data)) > 0
6542
            ) {
6543 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...
6544
            }
6545
6546 1103
            foreach ($data as $key => &$valueInner) {
6547 944
                $this->internalSet(
6548 944
                    $key,
6549 944
                    $valueInner,
6550 944
                    $checkPropertiesInConstructor
6551
                );
6552
            }
6553
        }
6554 1164
    }
6555
6556
    /**
6557
     * sorting keys
6558
     *
6559
     * @param array      $elements
6560
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6561
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6562
     *                              <strong>SORT_NATURAL</strong></p>
6563
     *
6564
     * @return $this
6565
     *               <p>(Mutable) Return this Arrayy object.</p>
6566
     *
6567
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6568
     * @psalm-return static<TKey,T>
6569
     */
6570 18
    protected function sorterKeys(
6571
        array &$elements,
6572
        $direction = \SORT_ASC,
6573
        int $strategy = \SORT_REGULAR
6574
    ): self {
6575 18
        $direction = $this->getDirection($direction);
6576
6577
        switch ($direction) {
6578 18
            case 'desc':
6579 18
            case \SORT_DESC:
6580 6
                \krsort($elements, $strategy);
6581
6582 6
                break;
6583 13
            case 'asc':
6584 13
            case \SORT_ASC:
6585
            default:
6586 13
                \ksort($elements, $strategy);
6587
        }
6588
6589 18
        return $this;
6590
    }
6591
6592
    /**
6593
     * @param array      $elements  <p>Warning: used as reference</p>
6594
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6595
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6596
     *                              <strong>SORT_NATURAL</strong></p>
6597
     * @param bool       $keepKeys
6598
     *
6599
     * @return $this
6600
     *               <p>(Mutable) Return this Arrayy object.</p>
6601
     *
6602
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6603
     * @psalm-return static<TKey,T>
6604
     */
6605 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6606
    {
6607 24
        $direction = $this->getDirection($direction);
6608
6609 24
        if (!$strategy) {
6610 24
            $strategy = \SORT_REGULAR;
6611
        }
6612
6613
        switch ($direction) {
6614 24
            case 'desc':
6615 24
            case \SORT_DESC:
6616 13
                if ($keepKeys) {
6617 9
                    \arsort($elements, $strategy);
6618
                } else {
6619 4
                    \rsort($elements, $strategy);
6620
                }
6621
6622 13
                break;
6623 11
            case 'asc':
6624 11
            case \SORT_ASC:
6625
            default:
6626 11
                if ($keepKeys) {
6627 4
                    \asort($elements, $strategy);
6628
                } else {
6629 7
                    \sort($elements, $strategy);
6630
                }
6631
        }
6632
6633 24
        return $this;
6634
    }
6635
6636
    /**
6637
     * @param array $array
6638
     *
6639
     * @return array
6640
     *
6641
     * @psalm-mutation-free
6642
     */
6643 25
    private function getArrayRecursiveHelperArrayy(array $array)
6644
    {
6645 25
        if ($array === []) {
6646
            return [];
6647
        }
6648
6649 25
        \array_walk_recursive(
6650 25
            $array,
6651
            /**
6652
             * @param array|self $item
6653
             *
6654
             * @return void
6655
             */
6656 25
            static function (&$item) {
6657 25
                if ($item instanceof self) {
6658 1
                    $item = $item->getArray();
6659
                }
6660 25
            }
6661
        );
6662
6663 25
        return $array;
6664
    }
6665
6666
    /**
6667
     * @param int|string|null $key
6668
     * @param mixed           $value
6669
     *
6670
     * @return void
6671
     */
6672 87
    private function checkType($key, $value)
6673
    {
6674
        if (
6675 87
            $key !== null
6676
            &&
6677 87
            isset($this->properties[$key]) === false
6678
            &&
6679 87
            $this->checkPropertiesMismatch === true
6680
        ) {
6681
            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...
6682
        }
6683
6684 87
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6685 76
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6686 17
        } elseif ($key !== null && isset($this->properties[$key])) {
6687 17
            $this->properties[$key]->checkType($value);
6688
        }
6689 85
    }
6690
}
6691