Completed
Push — master ( ece649...2444e2 )
by Lars
05:11 queued 01:50
created

Arrayy::firstsKeys()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16

Duplication

Lines 16
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 2.0054

Importance

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

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

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

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

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

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

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

This assignement can be removed without consequences.

Loading history...
609
610 151
        $tmpReturn = $this->keyExists($offset);
611
612
        if (
613 151
            $tmpReturn === true
614
            ||
615 151
            \strpos((string) $offset, $this->pathSeparator) === false
616
        ) {
617 148
            return $tmpReturn;
618
        }
619
620 4
        $offsetExists = false;
621
622
        /**
623
         * https://github.com/vimeo/psalm/issues/2536
624
         *
625
         * @psalm-suppress PossiblyInvalidArgument
626
         * @psalm-suppress InvalidScalarArgument
627
         */
628 View Code Duplication
        if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
629 4
            $this->pathSeparator
630
            &&
631 4
            (string) $offset === $offset
632
            &&
633 4
            \strpos($offset, $this->pathSeparator) !== false
634
        ) {
635 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
636 4
            if ($explodedPath !== false) {
637
                /** @var string $lastOffset - helper for phpstan */
638 4
                $lastOffset = \array_pop($explodedPath);
639 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
640
641
                /**
642
                 * @psalm-suppress MissingClosureReturnType
643
                 * @psalm-suppress MissingClosureParamType
644
                 */
645 4
                $this->callAtPath(
646 4
                    $containerPath,
647
                    static function ($container) use ($lastOffset, &$offsetExists) {
648 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
649 4
                    }
650
                );
651
            }
652
        }
653
654 4
        return $offsetExists;
655
    }
656
657
    /**
658
     * Returns the value at specified offset.
659
     *
660
     * @param int|string $offset
661
     *
662
     * @return mixed
663
     *               <p>Will return null if the offset did not exists.</p>
664
     */
665 126
    public function &offsetGet($offset)
666
    {
667
        // init
668 126
        $value = null;
669
670 126
        if ($this->offsetExists($offset)) {
671 124
            $value = &$this->__get($offset);
672
        }
673
674 126
        return $value;
675
    }
676
677
    /**
678
     * Assigns a value to the specified offset + check the type.
679
     *
680
     * @param int|string|null $offset
681
     * @param mixed           $value
682
     *
683
     * @return void
684
     */
685 27
    public function offsetSet($offset, $value)
686
    {
687 27
        $this->generatorToArray();
688
689 27
        if ($offset === null) {
690 6
            if ($this->properties !== []) {
691 1
                $this->checkType(null, $value);
692
            }
693
694 5
            $this->array[] = $value;
695
        } else {
696 21
            $this->internalSet(
697 21
                $offset,
698 21
                $value,
699 21
                true
700
            );
701
        }
702 26
    }
703
704
    /**
705
     * Unset an offset.
706
     *
707
     * @param int|string $offset
708
     *
709
     * @return void
710
     *              <p>(Mutable) Return nothing.</p>
711
     */
712 25
    public function offsetUnset($offset)
713
    {
714 25
        $this->generatorToArray();
715
716 25
        if ($this->array === []) {
717 6
            return;
718
        }
719
720 20
        if ($this->keyExists($offset)) {
721 13
            unset($this->array[$offset]);
722
723 13
            return;
724
        }
725
726
        /**
727
         * https://github.com/vimeo/psalm/issues/2536
728
         *
729
         * @psalm-suppress PossiblyInvalidArgument
730
         * @psalm-suppress InvalidScalarArgument
731
         */
732 View Code Duplication
        if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
733 10
            $this->pathSeparator
734
            &&
735 10
            (string) $offset === $offset
736
            &&
737 10
            \strpos($offset, $this->pathSeparator) !== false
738
        ) {
739 7
            $path = \explode($this->pathSeparator, (string) $offset);
740
741 7
            if ($path !== false) {
742 7
                $pathToUnset = \array_pop($path);
743
744
                /**
745
                 * @psalm-suppress MissingClosureReturnType
746
                 * @psalm-suppress MissingClosureParamType
747
                 */
748 7
                $this->callAtPath(
749 7
                    \implode($this->pathSeparator, $path),
750
                    static function (&$offset) use ($pathToUnset) {
751 6
                        if (\is_array($offset)) {
752 5
                            unset($offset[$pathToUnset]);
753
                        } else {
754 1
                            $offset = null;
755
                        }
756 7
                    }
757
                );
758
            }
759
        }
760
761 10
        unset($this->array[$offset]);
762 10
    }
763
764
    /**
765
     * Serialize the current "Arrayy"-object.
766
     *
767
     * @return string
768
     */
769 2
    public function serialize(): string
770
    {
771 2
        $this->generatorToArray();
772
773 2
        if (\PHP_VERSION_ID < 70400) {
774 2
            return parent::serialize();
775
        }
776
777
        return \serialize($this);
778
    }
779
780
    /**
781
     * Sets the iterator classname for the current "Arrayy"-object.
782
     *
783
     * @param string $iteratorClass
784
     *
785
     * @throws \InvalidArgumentException
786
     *
787
     * @return void
788
     *
789
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
790
     */
791 1189
    public function setIteratorClass($iteratorClass)
792
    {
793 1189
        if (\class_exists($iteratorClass)) {
794 1189
            $this->iteratorClass = $iteratorClass;
795
796 1189
            return;
797
        }
798
799
        if (\strpos($iteratorClass, '\\') === 0) {
800
            $iteratorClass = '\\' . $iteratorClass;
801
            if (\class_exists($iteratorClass)) {
802
                /**
803
                 * @psalm-suppress PropertyTypeCoercion
804
                 */
805
                $this->iteratorClass = $iteratorClass;
806
807
                return;
808
            }
809
        }
810
811
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
812
    }
813
814
    /**
815
     * Sort the entries with a user-defined comparison function and maintain key association.
816
     *
817
     * @param callable $function
818
     *
819
     * @throws \InvalidArgumentException
820
     *
821
     * @return $this
822
     *               <p>(Mutable) Return this Arrayy object.</p>
823
     *
824
     * @psalm-return static<TKey,T>
825
     */
826 8 View Code Duplication
    public function uasort($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
827
    {
828 8
        if (!\is_callable($function)) {
829
            throw new \InvalidArgumentException('Passed function must be callable');
830
        }
831
832 8
        $this->generatorToArray();
833
834 8
        \uasort($this->array, $function);
835
836 8
        return $this;
837
    }
838
839
    /**
840
     * Sort the entries with a user-defined comparison function and maintain key association.
841
     *
842
     * @param callable $function
843
     *
844
     * @throws \InvalidArgumentException
845
     *
846
     * @return $this
847
     *               <p>(Immutable) Return this Arrayy object.</p>
848
     *
849
     * @psalm-return static<TKey,T>
850
     * @psalm-mutation-free
851
     */
852 4
    public function uasortImmutable($function): self
853
    {
854 4
        $that = clone $this;
855
856
        /**
857
         * @psalm-suppress ImpureMethodCall - object is already cloned
858
         */
859 4
        $that->uasort($function);
860
861 4
        return $that;
862
    }
863
864
    /**
865
     * Sort the entries by keys using a user-defined comparison function.
866
     *
867
     * @param callable $function
868
     *
869
     * @throws \InvalidArgumentException
870
     *
871
     * @return static
872
     *                <p>(Mutable) Return this Arrayy object.</p>
873
     *
874
     * @psalm-return static<TKey,T>
875
     */
876 5
    public function uksort($function): self
877
    {
878 5
        return $this->customSortKeys($function);
879
    }
880
881
    /**
882
     * Sort the entries by keys using a user-defined comparison function.
883
     *
884
     * @param callable $function
885
     *
886
     * @throws \InvalidArgumentException
887
     *
888
     * @return static
889
     *                <p>(Immutable) Return this Arrayy object.</p>
890
     *
891
     * @psalm-return static<TKey,T>
892
     * @psalm-mutation-free
893
     */
894 1
    public function uksortImmutable($function): self
895
    {
896 1
        return $this->customSortKeysImmutable($function);
897
    }
898
899
    /**
900
     * Unserialize an string and return the instance of the "Arrayy"-class.
901
     *
902
     * @param string $string
903
     *
904
     * @return $this
905
     *
906
     * @psalm-return static<TKey,T>
907
     */
908 2
    public function unserialize($string): self
909
    {
910 2
        if (\PHP_VERSION_ID < 70400) {
911 2
            parent::unserialize($string);
912
913 2
            return $this;
914
        }
915
916
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
917
    }
918
919
    /**
920
     * Append a (key) + values to the current array.
921
     *
922
     * EXAMPLE: <code>
923
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
924
     * </code>
925
     *
926
     * @param array $values
927
     * @param mixed $key
928
     *
929
     * @return $this
930
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
931
     *
932
     * @psalm-param  array<mixed,T> $values
933
     * @psalm-param  TKey|null $key
934
     * @psalm-return static<TKey,T>
935
     */
936 1
    public function appendArrayValues(array $values, $key = null)
937
    {
938 1
        $this->generatorToArray();
939
940 1
        if ($key !== null) {
941
            if (
942 1
                isset($this->array[$key])
943
                &&
944 1
                \is_array($this->array[$key]) === true
945
            ) {
946 1
                foreach ($values as $value) {
947 1
                    $this->array[$key][] = $value;
948
                }
949
            } else {
950 1
                foreach ($values as $value) {
951
                    $this->array[$key] = $value;
952
                }
953
            }
954
        } else {
955
            foreach ($values as $value) {
956
                $this->array[] = $value;
957
            }
958
        }
959
960 1
        return $this;
961
    }
962
963
    /**
964
     * Add a suffix to each key.
965
     *
966
     * @param mixed $prefix
967
     *
968
     * @return static
969
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
970
     *
971
     * @psalm-return static<TKey,T>
972
     * @psalm-mutation-free
973
     */
974 10 View Code Duplication
    public function appendToEachKey($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
975
    {
976
        // init
977 10
        $result = [];
978
979 10
        foreach ($this->getGenerator() as $key => $item) {
980 9
            if ($item instanceof self) {
981
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
982 9
            } elseif (\is_array($item) === true) {
983
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
984
                    ->appendToEachKey($prefix)
985
                    ->toArray();
986
            } else {
987 9
                $result[$prefix . $key] = $item;
988
            }
989
        }
990
991 10
        return self::create($result, $this->iteratorClass, false);
992
    }
993
994
    /**
995
     * Add a prefix to each value.
996
     *
997
     * @param mixed $prefix
998
     *
999
     * @return static
1000
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1001
     *
1002
     * @psalm-return static<TKey,T>
1003
     * @psalm-mutation-free
1004
     */
1005 10 View Code Duplication
    public function appendToEachValue($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1006
    {
1007
        // init
1008 10
        $result = [];
1009
1010 10
        foreach ($this->getGenerator() as $key => $item) {
1011 9
            if ($item instanceof self) {
1012
                $result[$key] = $item->appendToEachValue($prefix);
1013 9
            } elseif (\is_array($item) === true) {
1014
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1015 9
            } elseif (\is_object($item) === true) {
1016 1
                $result[$key] = $item;
1017
            } else {
1018 8
                $result[$key] = $prefix . $item;
1019
            }
1020
        }
1021
1022 10
        return self::create($result, $this->iteratorClass, false);
1023
    }
1024
1025
    /**
1026
     * Sort an array in reverse order and maintain index association.
1027
     *
1028
     * @return $this
1029
     *               <p>(Mutable) Return this Arrayy object.</p>
1030
     *
1031
     * @psalm-return static<TKey,T>
1032
     */
1033 4
    public function arsort(): self
1034
    {
1035 4
        $this->generatorToArray();
1036
1037 4
        \arsort($this->array);
1038
1039 4
        return $this;
1040
    }
1041
1042
    /**
1043
     * Sort an array in reverse order and maintain index association.
1044
     *
1045
     * @return $this
1046
     *               <p>(Immutable) Return this Arrayy object.</p>
1047
     *
1048
     * @psalm-return static<TKey,T>
1049
     * @psalm-mutation-free
1050
     */
1051 10
    public function arsortImmutable(): self
1052
    {
1053 10
        $that = clone $this;
1054
1055 10
        $that->generatorToArray();
1056
1057 10
        \arsort($that->array);
1058
1059 10
        return $that;
1060
    }
1061
1062
    /**
1063
     * Iterate over the current array and execute a callback for each loop.
1064
     *
1065
     * EXAMPLE: <code>
1066
     * $result = A::create();
1067
     * $closure = function ($value, $key) use ($result) {
1068
     *     $result[$key] = ':' . $value . ':';
1069
     * };
1070
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1071
     * </code>
1072
     *
1073
     * @param \Closure $closure
1074
     *
1075
     * @return static
1076
     *                <p>(Immutable)</p>
1077
     *
1078
     * @psalm-return static<TKey,T>
1079
     * @psalm-mutation-free
1080
     */
1081 3
    public function at(\Closure $closure): self
1082
    {
1083 3
        $that = clone $this;
1084
1085 3
        foreach ($that->getGenerator() as $key => $value) {
1086 3
            $closure($value, $key);
1087
        }
1088
1089 3
        return static::create(
1090 3
            $that->toArray(),
1091 3
            $this->iteratorClass,
1092 3
            false
1093
        );
1094
    }
1095
1096
    /**
1097
     * Returns the average value of the current array.
1098
     *
1099
     * EXAMPLE: <code>
1100
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1101
     * </code>
1102
     *
1103
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1104
     *
1105
     * @return float|int
1106
     *                   <p>The average value.</p>
1107
     * @psalm-mutation-free
1108
     */
1109 10
    public function average($decimals = 0)
1110
    {
1111 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1112
1113 10
        if (!$count) {
1114 2
            return 0;
1115
        }
1116
1117 8
        if ((int) $decimals !== $decimals) {
1118 3
            $decimals = 0;
1119
        }
1120
1121 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1122
    }
1123
1124
    /**
1125
     * Changes all keys in an array.
1126
     *
1127
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1128
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1129
     *
1130
     * @return static
1131
     *                <p>(Immutable)</p>
1132
     *
1133
     * @psalm-return static<TKey,T>
1134
     * @psalm-mutation-free
1135
     */
1136 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1137
    {
1138
        if (
1139 1
            $case !== \CASE_LOWER
1140
            &&
1141 1
            $case !== \CASE_UPPER
1142
        ) {
1143
            $case = \CASE_LOWER;
1144
        }
1145
1146 1
        $return = [];
1147 1
        foreach ($this->getGenerator() as $key => $value) {
1148 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1149
1150 1
            if ($case === \CASE_LOWER) {
1151 1
                $key = \mb_strtolower((string) $key);
1152
            } else {
1153 1
                $key = \mb_strtoupper((string) $key);
1154
            }
1155
1156 1
            $return[$key] = $value;
1157
        }
1158
1159 1
        return static::create(
1160 1
            $return,
1161 1
            $this->iteratorClass,
1162 1
            false
1163
        );
1164
    }
1165
1166
    /**
1167
     * Change the path separator of the array wrapper.
1168
     *
1169
     * By default, the separator is: "."
1170
     *
1171
     * @param string $separator <p>Separator to set.</p>
1172
     *
1173
     * @return $this
1174
     *               <p>(Mutable) Return this Arrayy object.</p>
1175
     *
1176
     * @psalm-return static<TKey,T>
1177
     */
1178 11
    public function changeSeparator($separator): self
1179
    {
1180 11
        $this->pathSeparator = $separator;
1181
1182 11
        return $this;
1183
    }
1184
1185
    /**
1186
     * Create a chunked version of the current array.
1187
     *
1188
     * EXAMPLE: <code>
1189
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1190
     * </code>
1191
     *
1192
     * @param int  $size         <p>Size of each chunk.</p>
1193
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1194
     *
1195
     * @return static
1196
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1197
     *
1198
     * @psalm-return static<TKey,T>
1199
     * @psalm-mutation-free
1200
     */
1201 5
    public function chunk($size, $preserveKeys = false): self
1202
    {
1203 5
        return static::create(
1204 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1205 5
            $this->iteratorClass,
1206 5
            false
1207
        );
1208
    }
1209
1210
    /**
1211
     * Clean all falsy values from the current array.
1212
     *
1213
     * EXAMPLE: <code>
1214
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1215
     * </code>
1216
     *
1217
     * @return static
1218
     *                <p>(Immutable)</p>
1219
     *
1220
     * @psalm-return static<TKey,T>
1221
     * @psalm-mutation-free
1222
     */
1223 8
    public function clean(): self
1224
    {
1225 8
        return $this->filter(
1226
            static function ($value) {
1227 7
                return (bool) $value;
1228 8
            }
1229
        );
1230
    }
1231
1232
    /**
1233
     * WARNING!!! -> Clear the current full array or a $key of it.
1234
     *
1235
     * EXAMPLE: <code>
1236
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1237
     * </code>
1238
     *
1239
     * @param int|int[]|string|string[]|null $key
1240
     *
1241
     * @return $this
1242
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1243
     *
1244
     * @psalm-return static<TKey,T>
1245
     */
1246 10
    public function clear($key = null): self
1247
    {
1248 10
        if ($key !== null) {
1249 3
            if (\is_array($key)) {
1250 1
                foreach ($key as $keyTmp) {
1251 1
                    $this->offsetUnset($keyTmp);
1252
                }
1253
            } else {
1254 2
                $this->offsetUnset($key);
1255
            }
1256
1257 3
            return $this;
1258
        }
1259
1260 7
        $this->array = [];
1261 7
        $this->generator = null;
1262
1263 7
        return $this;
1264
    }
1265
1266
    /**
1267
     * Check if an item is in the current array.
1268
     *
1269
     * EXAMPLE: <code>
1270
     * a([1, true])->contains(true); // true
1271
     * </code>
1272
     *
1273
     * @param float|int|string $value
1274
     * @param bool             $recursive
1275
     * @param bool             $strict
1276
     *
1277
     * @return bool
1278
     * @psalm-mutation-free
1279
     */
1280 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1281
    {
1282 23
        if ($recursive === true) {
1283 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1284
        }
1285
1286
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1287 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1288 11
            if ($strict) {
1289 11
                if ($value === $valueFromArray) {
1290 11
                    return true;
1291
                }
1292
            } else {
1293
                /** @noinspection NestedPositiveIfStatementsInspection */
1294
                if ($value == $valueFromArray) {
1295
                    return true;
1296
                }
1297
            }
1298
        }
1299
1300 7
        return false;
1301
    }
1302
1303
    /**
1304
     * Check if an (case-insensitive) string is in the current array.
1305
     *
1306
     * EXAMPLE: <code>
1307
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1308
     * </code>
1309
     *
1310
     * @param mixed $value
1311
     * @param bool  $recursive
1312
     *
1313
     * @return bool
1314
     * @psalm-mutation-free
1315
     *
1316
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1317
     */
1318 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1319
    {
1320 26
        if ($value === null) {
1321 2
            return false;
1322
        }
1323
1324 24
        if ($recursive === true) {
1325
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1326 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1327 22
                if (\is_array($valueTmp) === true) {
1328 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1329 5
                    if ($return === true) {
1330 5
                        return $return;
1331
                    }
1332 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1333 16
                    return true;
1334
                }
1335
            }
1336
1337 8
            return false;
1338
        }
1339
1340
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1341 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1342 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1343 8
                return true;
1344
            }
1345
        }
1346
1347 4
        return false;
1348
    }
1349
1350
    /**
1351
     * Check if the given key/index exists in the array.
1352
     *
1353
     * EXAMPLE: <code>
1354
     * a([1 => true])->containsKey(1); // true
1355
     * </code>
1356
     *
1357
     * @param int|string $key <p>key/index to search for</p>
1358
     *
1359
     * @return bool
1360
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1361
     *
1362
     * @psalm-mutation-free
1363
     */
1364 4
    public function containsKey($key): bool
1365
    {
1366 4
        return $this->offsetExists($key);
1367
    }
1368
1369
    /**
1370
     * Check if all given needles are present in the array as key/index.
1371
     *
1372
     * EXAMPLE: <code>
1373
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1374
     * </code>
1375
     *
1376
     * @param array $needles   <p>The keys you are searching for.</p>
1377
     * @param bool  $recursive
1378
     *
1379
     * @return bool
1380
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1381
     *
1382
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1383
     * @psalm-mutation-free
1384
     */
1385 2
    public function containsKeys(array $needles, $recursive = false): bool
1386
    {
1387 2
        if ($recursive === true) {
1388
            return
1389 2
                \count(
1390 2
                    \array_intersect(
1391 2
                        $needles,
1392 2
                        $this->keys(true)->toArray()
1393
                    ),
1394 2
                    \COUNT_RECURSIVE
1395
                )
1396
                ===
1397 2
                \count(
1398 2
                    $needles,
1399 2
                    \COUNT_RECURSIVE
1400
                );
1401
        }
1402
1403 1
        return \count(
1404 1
            \array_intersect($needles, $this->keys()->toArray()),
1405 1
            \COUNT_NORMAL
1406
        )
1407
               ===
1408 1
               \count(
1409 1
                   $needles,
1410 1
                   \COUNT_NORMAL
1411
               );
1412
    }
1413
1414
    /**
1415
     * Check if all given needles are present in the array as key/index.
1416
     *
1417
     * @param array $needles <p>The keys you are searching for.</p>
1418
     *
1419
     * @return bool
1420
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1421
     *
1422
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1423
     * @psalm-mutation-free
1424
     */
1425 1
    public function containsKeysRecursive(array $needles): bool
1426
    {
1427 1
        return $this->containsKeys($needles, true);
1428
    }
1429
1430
    /**
1431
     * alias: for "Arrayy->contains()"
1432
     *
1433
     * @param float|int|string $value
1434
     *
1435
     * @return bool
1436
     *
1437
     * @see Arrayy::contains()
1438
     * @psalm-mutation-free
1439
     */
1440 9
    public function containsValue($value): bool
1441
    {
1442 9
        return $this->contains($value);
1443
    }
1444
1445
    /**
1446
     * alias: for "Arrayy->contains($value, true)"
1447
     *
1448
     * @param float|int|string $value
1449
     *
1450
     * @return bool
1451
     *
1452
     * @see Arrayy::contains()
1453
     * @psalm-mutation-free
1454
     */
1455 18
    public function containsValueRecursive($value): bool
1456
    {
1457 18
        return $this->contains($value, true);
1458
    }
1459
1460
    /**
1461
     * Check if all given needles are present in the array.
1462
     *
1463
     * EXAMPLE: <code>
1464
     * a([1, true])->containsValues(array(1, true)); // true
1465
     * </code>
1466
     *
1467
     * @param array $needles
1468
     *
1469
     * @return bool
1470
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1471
     *
1472
     * @psalm-param array<mixed>|array<T> $needles
1473
     * @psalm-mutation-free
1474
     */
1475 1
    public function containsValues(array $needles): bool
1476
    {
1477 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1478
               ===
1479 1
               \count($needles, \COUNT_NORMAL);
1480
    }
1481
1482
    /**
1483
     * Counts all the values of an array
1484
     *
1485
     * @see          http://php.net/manual/en/function.array-count-values.php
1486
     *
1487
     * @return static
1488
     *                <p>
1489
     *                (Immutable)
1490
     *                An associative Arrayy-object of values from input as
1491
     *                keys and their count as value.
1492
     *                </p>
1493
     *
1494
     * @psalm-return static<TKey,T>
1495
     * @psalm-mutation-free
1496
     */
1497 7
    public function countValues(): self
1498
    {
1499 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1500
    }
1501
1502
    /**
1503
     * Creates an Arrayy object.
1504
     *
1505
     * @param mixed  $data
1506
     * @param string $iteratorClass
1507
     * @param bool   $checkPropertiesInConstructor
1508
     *
1509
     * @return static
1510
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1511
     *
1512
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1513
     *
1514
     * @psalm-mutation-free
1515
     */
1516 718
    public static function create(
1517
        $data = [],
1518
        string $iteratorClass = ArrayyIterator::class,
1519
        bool $checkPropertiesInConstructor = true
1520
    ) {
1521 718
        return new static(
1522 718
            $data,
1523 718
            $iteratorClass,
1524 718
            $checkPropertiesInConstructor
1525
        );
1526
    }
1527
1528
    /**
1529
     * Flatten an array with the given character as a key delimiter
1530
     *
1531
     * @param string     $delimiter
1532
     * @param string     $prepend
1533
     * @param array|null $items
1534
     *
1535
     * @return array
1536
     */
1537 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1538
    {
1539
        // init
1540 2
        $flatten = [];
1541
1542 2
        if ($items === null) {
1543 2
            $items = $this->array;
1544
        }
1545
1546 2
        foreach ($items as $key => $value) {
1547 2
            if (\is_array($value) && !empty($value)) {
1548 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1549
            } else {
1550 2
                $flatten[] = [$prepend . $key => $value];
1551
            }
1552
        }
1553
1554 2
        if (\count($flatten) === 0) {
1555
            return [];
1556
        }
1557
1558 2
        return \array_merge_recursive([], ...$flatten);
1559
    }
1560
1561
    /**
1562
     * WARNING: Creates an Arrayy object by reference.
1563
     *
1564
     * @param array $array
1565
     *
1566
     * @return $this
1567
     *               <p>(Mutable) Return this Arrayy object.</p>
1568
     *
1569
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1570
     */
1571 27
    public function createByReference(array &$array = []): self
1572
    {
1573 27
        $array = $this->fallbackForArray($array);
1574
1575 27
        $this->array = &$array;
1576
1577 27
        return $this;
1578
    }
1579
1580
    /**
1581
     * Create an new instance from a callable function which will return an Generator.
1582
     *
1583
     * @param callable $generatorFunction
1584
     *
1585
     * @return static
1586
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1587
     *
1588
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1589
     *
1590
     * @psalm-mutation-free
1591
     */
1592 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1593
    {
1594 7
        return self::create($generatorFunction);
1595
    }
1596
1597
    /**
1598
     * Create an new instance filled with a copy of values from a "Generator"-object.
1599
     *
1600
     * @param \Generator $generator
1601
     *
1602
     * @return static
1603
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1604
     *
1605
     * @psalm-param \Generator<array-key,mixed> $generator
1606
     *
1607
     * @psalm-mutation-free
1608
     */
1609 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1610
    {
1611 4
        return self::create(\iterator_to_array($generator, true));
1612
    }
1613
1614
    /**
1615
     * Create an new Arrayy object via JSON.
1616
     *
1617
     * @param string $json
1618
     *
1619
     * @return static
1620
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1621
     *
1622
     * @psalm-mutation-free
1623
     */
1624 5
    public static function createFromJson(string $json): self
1625
    {
1626 5
        return static::create(\json_decode($json, true));
1627
    }
1628
1629
    /**
1630
     * Create an new Arrayy object via JSON.
1631
     *
1632
     * @param array $array
1633
     *
1634
     * @return static
1635
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1636
     *
1637
     * @psalm-mutation-free
1638
     */
1639 1
    public static function createFromArray(array $array): self
1640
    {
1641 1
        return static::create($array);
1642
    }
1643
1644
    /**
1645
     * Create an new instance filled with values from an object that is iterable.
1646
     *
1647
     * @param \Traversable $object <p>iterable object</p>
1648
     *
1649
     * @return static
1650
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1651
     *
1652
     * @psalm-param \Traversable<array-key,mixed> $object
1653
     *
1654
     * @psalm-mutation-free
1655
     */
1656 4
    public static function createFromObject(\Traversable $object): self
1657
    {
1658
        // init
1659 4
        $arrayy = new static();
1660
1661 4
        if ($object instanceof self) {
1662 4
            $objectArray = $object->getGenerator();
1663
        } else {
1664
            $objectArray = $object;
1665
        }
1666
1667 4
        foreach ($objectArray as $key => $value) {
1668
            /**
1669
             * @psalm-suppress ImpureMethodCall - object is already re-created
1670
             */
1671 3
            $arrayy->internalSet($key, $value);
1672
        }
1673
1674 4
        return $arrayy;
1675
    }
1676
1677
    /**
1678
     * Create an new instance filled with values from an object.
1679
     *
1680
     * @param object $object
1681
     *
1682
     * @return static
1683
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1684
     *
1685
     * @psalm-mutation-free
1686
     */
1687 5
    public static function createFromObjectVars($object): self
1688
    {
1689 5
        return self::create(self::objectToArray($object));
1690
    }
1691
1692
    /**
1693
     * Create an new Arrayy object via string.
1694
     *
1695
     * @param string      $str       <p>The input string.</p>
1696
     * @param string|null $delimiter <p>The boundary string.</p>
1697
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1698
     *                               used.</p>
1699
     *
1700
     * @return static
1701
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1702
     *
1703
     * @psalm-mutation-free
1704
     */
1705 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1706
    {
1707 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1708 1
            \preg_match_all($regEx, $str, $array);
1709
1710 1
            if (!empty($array)) {
1711 1
                $array = $array[0];
1712
            }
1713
        } else {
1714
            /** @noinspection NestedPositiveIfStatementsInspection */
1715 9
            if ($delimiter !== null) {
1716 7
                $array = \explode($delimiter, $str);
1717
            } else {
1718 2
                $array = [$str];
1719
            }
1720
        }
1721
1722
        // trim all string in the array
1723
        /**
1724
         * @psalm-suppress MissingClosureParamType
1725
         */
1726 10
        \array_walk(
1727 10
            $array,
1728
            static function (&$val) {
1729 10
                if ((string) $val === $val) {
1730 10
                    $val = \trim($val);
1731
                }
1732 10
            }
1733
        );
1734
1735 10
        return static::create($array);
1736
    }
1737
1738
    /**
1739
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1740
     *
1741
     * @param \Traversable $traversable
1742
     *
1743
     * @return static
1744
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1745
     *
1746
     * @psalm-param \Traversable<array-key,mixed> $traversable
1747
     *
1748
     * @psalm-mutation-free
1749
     */
1750 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1751
    {
1752 1
        return self::create(\iterator_to_array($traversable, true));
1753
    }
1754
1755
    /**
1756
     * Create an new instance containing a range of elements.
1757
     *
1758
     * @param float|int|string $low  <p>First value of the sequence.</p>
1759
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1760
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1761
     *
1762
     * @return static
1763
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1764
     *
1765
     * @psalm-mutation-free
1766
     */
1767 2
    public static function createWithRange($low, $high, $step = 1): self
1768
    {
1769 2
        return static::create(\range($low, $high, $step));
1770
    }
1771
1772
    /**
1773
     * Gets the element of the array at the current internal iterator position.
1774
     *
1775
     * @return false|mixed
1776
     */
1777
    public function current()
1778
    {
1779
        return \current($this->array);
1780
    }
1781
1782
    /**
1783
     * Custom sort by index via "uksort".
1784
     *
1785
     * EXAMPLE: <code>
1786
     * $callable = function ($a, $b) {
1787
     *     if ($a == $b) {
1788
     *         return 0;
1789
     *     }
1790
     *     return ($a > $b) ? 1 : -1;
1791
     * };
1792
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1793
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1794
     * </code>
1795
     *
1796
     * @see          http://php.net/manual/en/function.uksort.php
1797
     *
1798
     * @param callable $function
1799
     *
1800
     * @throws \InvalidArgumentException
1801
     *
1802
     * @return $this
1803
     *               <p>(Mutable) Return this Arrayy object.</p>
1804
     *
1805
     * @psalm-return static<TKey,T>
1806
     */
1807 5
    public function customSortKeys(callable $function): self
1808
    {
1809 5
        $this->generatorToArray();
1810
1811 5
        \uksort($this->array, $function);
1812
1813 5
        return $this;
1814
    }
1815
1816
    /**
1817
     * Custom sort by index via "uksort".
1818
     *
1819
     * @see          http://php.net/manual/en/function.uksort.php
1820
     *
1821
     * @param callable $function
1822
     *
1823
     * @throws \InvalidArgumentException
1824
     *
1825
     * @return $this
1826
     *               <p>(Immutable) Return this Arrayy object.</p>
1827
     *
1828
     * @psalm-return static<TKey,T>
1829
     * @psalm-mutation-free
1830
     */
1831 1
    public function customSortKeysImmutable(callable $function): self
1832
    {
1833 1
        $that = clone $this;
1834
1835 1
        $that->generatorToArray();
1836
1837
        /**
1838
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1839
         */
1840 1
        \uksort($that->array, $function);
1841
1842 1
        return $that;
1843
    }
1844
1845
    /**
1846
     * Custom sort by value via "usort".
1847
     *
1848
     * EXAMPLE: <code>
1849
     * $callable = function ($a, $b) {
1850
     *     if ($a == $b) {
1851
     *         return 0;
1852
     *     }
1853
     *     return ($a > $b) ? 1 : -1;
1854
     * };
1855
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1856
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
1857
     * </code>
1858
     *
1859
     * @see          http://php.net/manual/en/function.usort.php
1860
     *
1861
     * @param callable $function
1862
     *
1863
     * @throws \InvalidArgumentException
1864
     *
1865
     * @return $this
1866
     *               <p>(Mutable) Return this Arrayy object.</p>
1867
     *
1868
     * @psalm-return static<TKey,T>
1869
     */
1870 10 View Code Duplication
    public function customSortValues($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1871
    {
1872 10
        if (\is_callable($function) === false) {
1873
            throw new \InvalidArgumentException('Passed function must be callable');
1874
        }
1875
1876 10
        $this->generatorToArray();
1877
1878 10
        \usort($this->array, $function);
1879
1880 10
        return $this;
1881
    }
1882
1883
    /**
1884
     * Custom sort by value via "usort".
1885
     *
1886
     * @see          http://php.net/manual/en/function.usort.php
1887
     *
1888
     * @param callable $function
1889
     *
1890
     * @throws \InvalidArgumentException
1891
     *
1892
     * @return $this
1893
     *               <p>(Immutable) Return this Arrayy object.</p>
1894
     *
1895
     * @psalm-return static<TKey,T>
1896
     * @psalm-mutation-free
1897
     */
1898 4
    public function customSortValuesImmutable($function): self
1899
    {
1900 4
        $that = clone $this;
1901
1902
        /**
1903
         * @psalm-suppress ImpureMethodCall - object is already cloned
1904
         */
1905 4
        $that->customSortValues($function);
1906
1907 4
        return $that;
1908
    }
1909
1910
    /**
1911
     * Delete the given key or keys.
1912
     *
1913
     * @param int|int[]|string|string[] $keyOrKeys
1914
     *
1915
     * @return void
1916
     */
1917 9
    public function delete($keyOrKeys)
1918
    {
1919 9
        $keyOrKeys = (array) $keyOrKeys;
1920
1921 9
        foreach ($keyOrKeys as $key) {
1922 9
            $this->offsetUnset($key);
1923
        }
1924 9
    }
1925
1926
    /**
1927
     * Return values that are only in the current array.
1928
     *
1929
     * EXAMPLE: <code>
1930
     * a([1 => 1, 2 => 2])->diff([1 => 1]); // Arrayy[2 => 2]
1931
     * </code>
1932
     *
1933
     * @param array ...$array
1934
     *
1935
     * @return static
1936
     *                <p>(Immutable)</p>
1937
     *
1938
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1939
     * @psalm-return static<TKey,T>
1940
     * @psalm-mutation-free
1941
     */
1942 13
    public function diff(...$array): self
1943
    {
1944 13
        return static::create(
1945 13
            \array_diff($this->toArray(), ...$array),
1946 13
            $this->iteratorClass,
1947 13
            false
1948
        );
1949
    }
1950
1951
    /**
1952
     * Return values that are only in the current array.
1953
     *
1954
     * @param array ...$array
1955
     *
1956
     * @return static
1957
     *                <p>(Immutable)</p>
1958
     *
1959
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1960
     * @psalm-return static<TKey,T>
1961
     * @psalm-mutation-free
1962
     */
1963 8
    public function diffKey(...$array): self
1964
    {
1965 8
        return static::create(
1966 8
            \array_diff_key($this->toArray(), ...$array),
1967 8
            $this->iteratorClass,
1968 8
            false
1969
        );
1970
    }
1971
1972
    /**
1973
     * Return values and Keys that are only in the current array.
1974
     *
1975
     * @param array $array
1976
     *
1977
     * @return static
1978
     *                <p>(Immutable)</p>
1979
     *
1980
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1981
     * @psalm-return static<TKey,T>
1982
     * @psalm-mutation-free
1983
     */
1984 8
    public function diffKeyAndValue(array $array = []): self
1985
    {
1986 8
        return static::create(
1987 8
            \array_diff_assoc($this->toArray(), $array),
1988 8
            $this->iteratorClass,
1989 8
            false
1990
        );
1991
    }
1992
1993
    /**
1994
     * Return values that are only in the current multi-dimensional array.
1995
     *
1996
     * @param array                 $array
1997
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
1998
     *
1999
     * @return static
2000
     *                <p>(Immutable)</p>
2001
     *
2002
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2003
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2004
     * @psalm-return static<TKey,T>
2005
     * @psalm-mutation-free
2006
     */
2007 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2008
    {
2009
        // init
2010 1
        $result = [];
2011
2012
        if (
2013 1
            $helperVariableForRecursion !== null
2014
            &&
2015 1
            \is_array($helperVariableForRecursion) === true
2016
        ) {
2017
            $arrayForTheLoop = $helperVariableForRecursion;
2018
        } else {
2019 1
            $arrayForTheLoop = $this->getGenerator();
2020
        }
2021
2022 1
        foreach ($arrayForTheLoop as $key => $value) {
2023 1
            if ($value instanceof self) {
2024
                $value = $value->toArray();
2025
            }
2026
2027 1
            if (\array_key_exists($key, $array)) {
2028 1
                if ($value !== $array[$key]) {
2029 1
                    $result[$key] = $value;
2030
                }
2031
            } else {
2032 1
                $result[$key] = $value;
2033
            }
2034
        }
2035
2036 1
        return static::create(
2037 1
            $result,
2038 1
            $this->iteratorClass,
2039 1
            false
2040
        );
2041
    }
2042
2043
    /**
2044
     * Return values that are only in the new $array.
2045
     *
2046
     * @param array $array
2047
     *
2048
     * @return static
2049
     *                <p>(Immutable)</p>
2050
     *
2051
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2052
     * @psalm-return static<TKey,T>
2053
     * @psalm-mutation-free
2054
     */
2055 8
    public function diffReverse(array $array = []): self
2056
    {
2057 8
        return static::create(
2058 8
            \array_diff($array, $this->toArray()),
2059 8
            $this->iteratorClass,
2060 8
            false
2061
        );
2062
    }
2063
2064
    /**
2065
     * Divide an array into two arrays. One with keys and the other with values.
2066
     *
2067
     * @return static
2068
     *                <p>(Immutable)</p>
2069
     *
2070
     * @psalm-return static<TKey,T>
2071
     * @psalm-mutation-free
2072
     */
2073 1
    public function divide(): self
2074
    {
2075 1
        return static::create(
2076
            [
2077 1
                $this->keys(),
2078 1
                $this->values(),
2079
            ],
2080 1
            $this->iteratorClass,
2081 1
            false
2082
        );
2083
    }
2084
2085
    /**
2086
     * Iterate over the current array and modify the array's value.
2087
     *
2088
     * @param \Closure $closure
2089
     *
2090
     * @return static
2091
     *                <p>(Immutable)</p>
2092
     *
2093
     * @psalm-return static<TKey,T>
2094
     * @psalm-mutation-free
2095
     */
2096 5 View Code Duplication
    public function each(\Closure $closure): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2097
    {
2098
        // init
2099 5
        $array = [];
2100
2101 5
        foreach ($this->getGenerator() as $key => $value) {
2102 5
            $array[$key] = $closure($value, $key);
2103
        }
2104
2105 5
        return static::create(
2106 5
            $array,
2107 5
            $this->iteratorClass,
2108 5
            false
2109
        );
2110
    }
2111
2112
    /**
2113
     * Sets the internal iterator to the last element in the array and returns this element.
2114
     *
2115
     * @return mixed
2116
     */
2117
    public function end()
2118
    {
2119
        return \end($this->array);
2120
    }
2121
2122
    /**
2123
     * Check if a value is in the current array using a closure.
2124
     *
2125
     * @param \Closure $closure
2126
     *
2127
     * @return bool
2128
     *              <p>Returns true if the given value is found, false otherwise.</p>
2129
     */
2130 4
    public function exists(\Closure $closure): bool
2131
    {
2132
        // init
2133 4
        $isExists = false;
2134
2135 4
        foreach ($this->getGenerator() as $key => $value) {
2136 3
            if ($closure($value, $key)) {
2137 1
                $isExists = true;
2138
2139 1
                break;
2140
            }
2141
        }
2142
2143 4
        return $isExists;
2144
    }
2145
2146
    /**
2147
     * Fill the array until "$num" with "$default" values.
2148
     *
2149
     * @param int   $num
2150
     * @param mixed $default
2151
     *
2152
     * @return static
2153
     *                <p>(Immutable)</p>
2154
     *
2155
     * @psalm-return static<TKey,T>
2156
     * @psalm-mutation-free
2157
     */
2158 8
    public function fillWithDefaults(int $num, $default = null): self
2159
    {
2160 8
        if ($num < 0) {
2161 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2162
        }
2163
2164 7
        $this->generatorToArray();
2165
2166 7
        $tmpArray = $this->array;
2167
2168 7
        $count = \count($tmpArray);
2169
2170 7
        while ($count < $num) {
2171 4
            $tmpArray[] = $default;
2172 4
            ++$count;
2173
        }
2174
2175 7
        return static::create(
2176 7
            $tmpArray,
2177 7
            $this->iteratorClass,
2178 7
            false
2179
        );
2180
    }
2181
2182
    /**
2183
     * Find all items in an array that pass the truth test.
2184
     *
2185
     * @param \Closure|null $closure [optional] <p>
2186
     *                               The callback function to use
2187
     *                               </p>
2188
     *                               <p>
2189
     *                               If no callback is supplied, all entries of
2190
     *                               input equal to false (see
2191
     *                               converting to
2192
     *                               boolean) will be removed.
2193
     *                               </p>
2194
     * @param int           $flag    [optional] <p>
2195
     *                               Flag determining what arguments are sent to <i>callback</i>:
2196
     *                               </p><ul>
2197
     *                               <li>
2198
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
2199
     *                               to <i>callback</i> instead of the value</span>
2200
     *                               </li>
2201
     *                               <li>
2202
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
2203
     *                               arguments to <i>callback</i> instead of the value</span>
2204
     *                               </li>
2205
     *                               </ul>
2206
     *
2207
     * @return static
2208
     *                <p>(Immutable)</p>
2209
     *
2210
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
2211
     * @psalm-return static<TKey,T>
2212
     * @psalm-mutation-free
2213
     */
2214 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2215
    {
2216 12
        if (!$closure) {
2217 1
            return $this->clean();
2218
        }
2219
2220 12
        return static::create(
2221 12
            \array_filter($this->toArray(), $closure, $flag),
2222 12
            $this->iteratorClass,
2223 12
            false
2224
        );
2225
    }
2226
2227
    /**
2228
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2229
     * property within that.
2230
     *
2231
     * @param string          $property
2232
     * @param string|string[] $value
2233
     * @param string          $comparisonOp
2234
     *                                      <p>
2235
     *                                      'eq' (equals),<br />
2236
     *                                      'gt' (greater),<br />
2237
     *                                      'gte' || 'ge' (greater or equals),<br />
2238
     *                                      'lt' (less),<br />
2239
     *                                      'lte' || 'le' (less or equals),<br />
2240
     *                                      'ne' (not equals),<br />
2241
     *                                      'contains',<br />
2242
     *                                      'notContains',<br />
2243
     *                                      'newer' (via strtotime),<br />
2244
     *                                      'older' (via strtotime),<br />
2245
     *                                      </p>
2246
     *
2247
     * @return static
2248
     *                <p>(Immutable)</p>
2249
     *
2250
     * @psalm-return static<TKey,T>
2251
     * @psalm-mutation-free
2252
     *
2253
     * @psalm-suppress MissingClosureReturnType
2254
     * @psalm-suppress MissingClosureParamType
2255
     */
2256 1
    public function filterBy(
2257
        string $property,
2258
        $value,
2259
        string $comparisonOp = null
2260
    ): self {
2261 1
        if (!$comparisonOp) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $comparisonOp of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2262 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2263
        }
2264
2265
        $ops = [
2266
            'eq' => static function ($item, $prop, $value): bool {
2267 1
                return $item[$prop] === $value;
2268 1
            },
2269
            'gt' => static function ($item, $prop, $value): bool {
2270
                return $item[$prop] > $value;
2271 1
            },
2272
            'ge' => static function ($item, $prop, $value): bool {
2273
                return $item[$prop] >= $value;
2274 1
            },
2275
            'gte' => static function ($item, $prop, $value): bool {
2276
                return $item[$prop] >= $value;
2277 1
            },
2278
            'lt' => static function ($item, $prop, $value): bool {
2279 1
                return $item[$prop] < $value;
2280 1
            },
2281
            'le' => static function ($item, $prop, $value): bool {
2282
                return $item[$prop] <= $value;
2283 1
            },
2284
            'lte' => static function ($item, $prop, $value): bool {
2285
                return $item[$prop] <= $value;
2286 1
            },
2287
            'ne' => static function ($item, $prop, $value): bool {
2288
                return $item[$prop] !== $value;
2289 1
            },
2290
            'contains' => static function ($item, $prop, $value): bool {
2291 1
                return \in_array($item[$prop], (array) $value, true);
2292 1
            },
2293
            'notContains' => static function ($item, $prop, $value): bool {
2294
                return !\in_array($item[$prop], (array) $value, true);
2295 1
            },
2296
            'newer' => static function ($item, $prop, $value): bool {
2297
                return \strtotime($item[$prop]) > \strtotime($value);
2298 1
            },
2299
            'older' => static function ($item, $prop, $value): bool {
2300
                return \strtotime($item[$prop]) < \strtotime($value);
2301 1
            },
2302
        ];
2303
2304 1
        $result = \array_values(
2305 1
            \array_filter(
2306 1
                $this->toArray(false, true),
2307
                static function ($item) use (
2308 1
                    $property,
2309 1
                    $value,
2310 1
                    $ops,
2311 1
                    $comparisonOp
2312
                ) {
2313 1
                    $item = (array) $item;
2314 1
                    $itemArrayy = static::create($item);
2315 1
                    $item[$property] = $itemArrayy->get($property, []);
2316
2317 1
                    return $ops[$comparisonOp]($item, $property, $value);
2318 1
                }
2319
            )
2320
        );
2321
2322 1
        return static::create(
2323 1
            $result,
2324 1
            $this->iteratorClass,
2325 1
            false
2326
        );
2327
    }
2328
2329
    /**
2330
     * Find the first item in an array that passes the truth test,
2331
     *  otherwise return false
2332
     *
2333
     * @param \Closure $closure
2334
     *
2335
     * @return false|mixed
2336
     *                     <p>Return false if we did not find the value.</p>
2337
     */
2338 8 View Code Duplication
    public function find(\Closure $closure)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2339
    {
2340 8
        foreach ($this->getGenerator() as $key => $value) {
2341 6
            if ($closure($value, $key)) {
2342 5
                return $value;
2343
            }
2344
        }
2345
2346 3
        return false;
2347
    }
2348
2349
    /**
2350
     * find by ...
2351
     *
2352
     * @param string          $property
2353
     * @param string|string[] $value
2354
     * @param string          $comparisonOp
2355
     *
2356
     * @return static
2357
     *                <p>(Immutable)</p>
2358
     *
2359
     * @psalm-return static<TKey,T>
2360
     * @psalm-mutation-free
2361
     */
2362 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2363
    {
2364 1
        return $this->filterBy($property, $value, $comparisonOp);
2365
    }
2366
2367
    /**
2368
     * Get the first value from the current array.
2369
     *
2370
     * @return mixed
2371
     *               <p>Return null if there wasn't a element.</p>
2372
     */
2373 22
    public function first()
2374
    {
2375 22
        $key_first = $this->firstKey();
2376 22
        if ($key_first === null) {
2377 3
            return null;
2378
        }
2379
2380 19
        return $this->get($key_first);
2381
    }
2382
2383
    /**
2384
     * Get the first key from the current array.
2385
     *
2386
     * @return mixed
2387
     *               <p>Return null if there wasn't a element.</p>
2388
     * @psalm-mutation-free
2389
     */
2390 29
    public function firstKey()
2391
    {
2392 29
        $this->generatorToArray();
2393
2394 29
        return \array_key_first($this->array);
2395
    }
2396
2397
    /**
2398
     * Get the first value(s) from the current array.
2399
     * And will return an empty array if there was no first entry.
2400
     *
2401
     * @param int|null $number <p>How many values you will take?</p>
2402
     *
2403
     * @return static
2404
     *                <p>(Immutable)</p>
2405
     *
2406
     * @psalm-return static<TKey,T>
2407
     * @psalm-mutation-free
2408
     */
2409 37 View Code Duplication
    public function firstsImmutable(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2410
    {
2411 37
        $arrayTmp = $this->toArray();
2412
2413 37
        if ($number === null) {
2414 14
            $array = (array) \array_shift($arrayTmp);
2415
        } else {
2416 23
            $array = \array_splice($arrayTmp, 0, $number);
2417
        }
2418
2419 37
        return static::create(
2420 37
            $array,
2421 37
            $this->iteratorClass,
2422 37
            false
2423
        );
2424
    }
2425
2426
    /**
2427
     * Get the first value(s) from the current array.
2428
     * And will return an empty array if there was no first entry.
2429
     *
2430
     * @param int|null $number <p>How many values you will take?</p>
2431
     *
2432
     * @return static
2433
     *                <p>(Immutable)</p>
2434
     *
2435
     * @psalm-return static<TKey,T>
2436
     * @psalm-mutation-free
2437
     */
2438 3 View Code Duplication
    public function firstsKeys(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2439
    {
2440 3
        $arrayTmp = $this->keys()->toArray();
2441
2442 3
        if ($number === null) {
2443
            $array = (array) \array_shift($arrayTmp);
2444
        } else {
2445 3
            $array = \array_splice($arrayTmp, 0, $number);
2446
        }
2447
2448 3
        return static::create(
2449 3
            $array,
2450 3
            $this->iteratorClass,
2451 3
            false
2452
        );
2453
    }
2454
2455
    /**
2456
     * Get and rmove the first value(s) from the current array.
2457
     * And will return an empty array if there was no first entry.
2458
     *
2459
     * @param int|null $number <p>How many values you will take?</p>
2460
     *
2461
     * @return $this
2462
     *               <p>(Mutable)</p>
2463
     *
2464
     * @psalm-return static<TKey,T>
2465
     */
2466 34
    public function firstsMutable(int $number = null): self
2467
    {
2468 34
        $this->generatorToArray();
2469
2470 34
        if ($number === null) {
2471 19
            $this->array = (array) \array_shift($this->array);
2472
        } else {
2473 15
            $this->array = \array_splice($this->array, 0, $number);
2474
        }
2475
2476 34
        return $this;
2477
    }
2478
2479
    /**
2480
     * Exchanges all keys with their associated values in an array.
2481
     *
2482
     * @return static
2483
     *                <p>(Immutable)</p>
2484
     *
2485
     * @psalm-return static<TKey,T>
2486
     * @psalm-mutation-free
2487
     */
2488 1
    public function flip(): self
2489
    {
2490 1
        return static::create(
2491 1
            \array_flip($this->toArray()),
2492 1
            $this->iteratorClass,
2493 1
            false
2494
        );
2495
    }
2496
2497
    /**
2498
     * Get a value from an array (optional using dot-notation).
2499
     *
2500
     * @param mixed $key            <p>The key to look for.</p>
2501
     * @param mixed $fallback       <p>Value to fallback to.</p>
2502
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2503
     *                              class.</p>
2504
     * @param bool  $useByReference
2505
     *
2506
     * @return mixed|static
2507
     *
2508
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2509
     * @psalm-mutation-free
2510
     */
2511 242
    public function get(
2512
        $key,
2513
        $fallback = null,
2514
        array $array = null,
2515
        bool $useByReference = false
2516
    ) {
2517 242
        if ($array !== null) {
2518 4
            if ($useByReference) {
2519
                $usedArray = &$array;
2520
            } else {
2521 4
                $usedArray = $array;
2522
            }
2523
        } else {
2524 239
            $this->generatorToArray();
2525
2526 239
            if ($useByReference) {
2527 126
                $usedArray = &$this->array;
2528
            } else {
2529 131
                $usedArray = $this->array;
2530
            }
2531
        }
2532
2533 242
        if ($key === null) {
2534 1
            return static::create(
2535 1
                [],
2536 1
                $this->iteratorClass,
2537 1
                false
2538 1
            )->createByReference($usedArray);
2539
        }
2540
2541
        // php cast "bool"-index into "int"-index
2542 242
        if ((bool) $key === $key) {
2543 3
            $key = (int) $key;
2544
        }
2545
2546 242
        if (\array_key_exists($key, $usedArray) === true) {
2547 204
            if (\is_array($usedArray[$key]) === true) {
2548 19
                return static::create(
2549 19
                    [],
2550 19
                    $this->iteratorClass,
2551 19
                    false
2552 19
                )->createByReference($usedArray[$key]);
2553
            }
2554
2555 190
            return $usedArray[$key];
2556
        }
2557
2558
        // crawl through array, get key according to object or not
2559 61
        $usePath = false;
2560
        if (
2561 61
            $this->pathSeparator
2562
            &&
2563 61
            (string) $key === $key
2564
            &&
2565 61
            \strpos($key, $this->pathSeparator) !== false
2566
        ) {
2567 31
            $segments = \explode($this->pathSeparator, (string) $key);
2568 31
            if ($segments !== false) {
2569 31
                $usePath = true;
2570 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2571
2572 31
                foreach ($segments as $segment) {
2573
                    if (
2574
                        (
2575 31
                            \is_array($usedArrayTmp) === true
2576
                            ||
2577 31
                            $usedArrayTmp instanceof \ArrayAccess
2578
                        )
2579
                        &&
2580 31
                        isset($usedArrayTmp[$segment])
2581
                    ) {
2582 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2583
2584 30
                        continue;
2585
                    }
2586
2587
                    if (
2588 14
                        \is_object($usedArrayTmp) === true
2589
                        &&
2590 14
                        \property_exists($usedArrayTmp, $segment)
2591
                    ) {
2592 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2593
2594 1
                        continue;
2595
                    }
2596
2597 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2598 1
                        $segmentsTmp = $segments;
2599 1
                        unset($segmentsTmp[0]);
2600 1
                        $keyTmp = \implode('.', $segmentsTmp);
2601 1
                        $returnTmp = static::create(
2602 1
                            [],
2603 1
                            $this->iteratorClass,
2604 1
                            false
2605
                        );
2606 1
                        foreach ($this->getAll() as $dataTmp) {
2607 1
                            if ($dataTmp instanceof self) {
2608
                                $returnTmp->add($dataTmp->get($keyTmp));
2609
2610
                                continue;
2611
                            }
2612
2613
                            if (
2614
                                (
2615 1
                                    \is_array($dataTmp) === true
2616
                                    ||
2617 1
                                    $dataTmp instanceof \ArrayAccess
2618
                                )
2619
                                &&
2620 1
                                isset($dataTmp[$keyTmp])
2621
                            ) {
2622
                                $returnTmp->add($dataTmp[$keyTmp]);
2623
2624
                                continue;
2625
                            }
2626
2627
                            if (
2628 1
                                \is_object($dataTmp) === true
2629
                                &&
2630 1
                                \property_exists($dataTmp, $keyTmp)
2631
                            ) {
2632 1
                                $returnTmp->add($dataTmp->{$keyTmp});
2633
2634
                                /** @noinspection UnnecessaryContinueInspection */
2635 1
                                continue;
2636
                            }
2637
                        }
2638
2639 1
                        if ($returnTmp->count() > 0) {
2640 1
                            return $returnTmp;
2641
                        }
2642
                    }
2643
2644 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2645
                }
2646
            }
2647
        }
2648
2649 58
        if (isset($usedArrayTmp)) {
2650 28 View Code Duplication
            if (!$usePath && !isset($usedArrayTmp[$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2651
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2652
            }
2653
2654 28
            if (\is_array($usedArrayTmp) === true) {
2655 6
                return static::create(
2656 6
                    [],
2657 6
                    $this->iteratorClass,
2658 6
                    false
2659 6
                )->createByReference($usedArrayTmp);
2660
            }
2661
2662 28
            return $usedArrayTmp;
2663
        }
2664
2665 30 View Code Duplication
        if (!$usePath && !isset($usedArray[$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2666 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2667
        }
2668
2669
        return static::create(
2670
            [],
2671
            $this->iteratorClass,
2672
            false
2673
        )->createByReference($usedArray);
2674
    }
2675
2676
    /**
2677
     * alias: for "Arrayy->toArray()"
2678
     *
2679
     * @return array
2680
     *
2681
     * @see          Arrayy::getArray()
2682
     *
2683
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2684
     */
2685 15
    public function getAll(): array
2686
    {
2687 15
        return $this->toArray();
2688
    }
2689
2690
    /**
2691
     * Get the current array from the "Arrayy"-object.
2692
     *
2693
     * alias for "toArray()"
2694
     *
2695
     * @param bool $convertAllArrayyElements <p>
2696
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2697
     *                                       </p>
2698
     * @param bool $preserveKeys             <p>
2699
     *                                       e.g.: A generator maybe return the same key more then once,
2700
     *                                       so maybe you will ignore the keys.
2701
     *                                       </p>
2702
     *
2703
     * @return array
2704
     *
2705
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2706
     * @psalm-mutation-free
2707
     *
2708
     * @see Arrayy::toArray()
2709
     */
2710 500
    public function getArray(
2711
        bool $convertAllArrayyElements = false,
2712
        bool $preserveKeys = true
2713
    ): array {
2714 500
        return $this->toArray(
2715 500
            $convertAllArrayyElements,
2716 500
            $preserveKeys
2717
        );
2718
    }
2719
2720
    /**
2721
     * @param string $json
2722
     *
2723
     * @return $this
2724
     */
2725 3
    public static function createFromJsonMapper(string $json)
2726
    {
2727
        // init
2728 3
        $class = static::create();
2729
2730 3
        $jsonObject = \json_decode($json, false);
2731
2732 3
        $mapper = new \Arrayy\Mapper\Json();
2733 View Code Duplication
        $mapper->undefinedPropertyHandler = static function ($object, $key, $jsonValue) use ($class) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2734
            if ($class->checkPropertiesMismatchInConstructor) {
2735
                throw new \TypeError('Property mismatch - input: ' . \print_r(['key' => $key, 'jsonValue' => $jsonValue], true) . ' for object: ' . \get_class($object));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...' . \get_class($object).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2736
            }
2737
        };
2738
2739 3
        return $mapper->map($jsonObject, $class);
2740
    }
2741
2742
    /**
2743
     * @return array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
0 ignored issues
show
Documentation introduced by
The doc-type array<int|string,TypeChe...nterface>|TypeInterface could not be parsed: Expected "|" or "end of type", but got "<" at position 57. (view supported doc-types)

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

Loading history...
2744
     *
2745
     * @internal
2746
     */
2747 6
    public function getPhpDocPropertiesFromClass()
2748
    {
2749 6
        if ($this->properties === []) {
2750 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2751
        }
2752
2753 6
        return $this->properties;
2754
    }
2755
2756
    /**
2757
     * Get the current array from the "Arrayy"-object as list.
2758
     *
2759
     * alias for "toList()"
2760
     *
2761
     * @param bool $convertAllArrayyElements <p>
2762
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2763
     *                                       </p>
2764
     *
2765
     * @return array
2766
     *
2767
     * @psalm-return array<int,mixed>|array<int,T>
2768
     * @psalm-mutation-free
2769
     *
2770
     * @see Arrayy::toList()
2771
     */
2772 1
    public function getList(bool $convertAllArrayyElements = false): array
2773
    {
2774 1
        return $this->toList($convertAllArrayyElements);
2775
    }
2776
2777
    /**
2778
     * Returns the values from a single column of the input array, identified by
2779
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2780
     *
2781
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2782
     * array by the values from the $indexKey column in the input array.
2783
     *
2784
     * @param mixed $columnKey
2785
     * @param mixed $indexKey
2786
     *
2787
     * @return static
2788
     *                <p>(Immutable)</p>
2789
     *
2790
     * @psalm-return static<TKey,T>
2791
     * @psalm-mutation-free
2792
     */
2793 1
    public function getColumn($columnKey = null, $indexKey = null): self
2794
    {
2795 1
        return static::create(
2796 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2797 1
            $this->iteratorClass,
2798 1
            false
2799
        );
2800
    }
2801
2802
    /**
2803
     * Get the current array from the "Arrayy"-object as generator by reference.
2804
     *
2805
     * @return \Generator
2806
     *
2807
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2808
     */
2809 75
    public function &getGeneratorByReference(): \Generator
2810
    {
2811 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2812
            // -> false-positive -> see "&" from method
2813
            /** @noinspection YieldFromCanBeUsedInspection */
2814 17
            foreach ($this->generator as $key => $value) {
2815 17
                yield $key => $value;
2816
            }
2817
2818 5
            return;
2819
        }
2820
2821
        // -> false-positive -> see "&$value"
2822
        /** @noinspection YieldFromCanBeUsedInspection */
2823
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
2824 59
        foreach ($this->array as $key => &$value) {
2825 54
            yield $key => $value;
2826
        }
2827 35
    }
2828
2829
    /**
2830
     * Get the current array from the "Arrayy"-object as generator.
2831
     *
2832
     * @return \Generator
2833
     *
2834
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2835
     * @psalm-mutation-free
2836
     */
2837 996
    public function getGenerator(): \Generator
2838
    {
2839 996
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2840 25
            yield from $this->generator;
2841
2842 25
            return;
2843
        }
2844
2845 994
        yield from $this->array;
2846 968
    }
2847
2848
    /**
2849
     * alias: for "Arrayy->keys()"
2850
     *
2851
     * @return static
2852
     *                <p>(Immutable)</p>
2853
     *
2854
     * @see          Arrayy::keys()
2855
     *
2856
     * @psalm-return static<array-key,TKey>
2857
     * @psalm-mutation-free
2858
     */
2859 2
    public function getKeys()
2860
    {
2861 2
        return $this->keys();
2862
    }
2863
2864
    /**
2865
     * Get the current array from the "Arrayy"-object as object.
2866
     *
2867
     * @return \stdClass
2868
     */
2869 4
    public function getObject(): \stdClass
2870
    {
2871 4
        return self::arrayToObject($this->toArray());
2872
    }
2873
2874
    /**
2875
     * alias: for "Arrayy->randomImmutable()"
2876
     *
2877
     * @return static
2878
     *                <p>(Immutable)</p>
2879
     *
2880
     * @see          Arrayy::randomImmutable()
2881
     *
2882
     * @psalm-return static<int|array-key,T>
2883
     */
2884 4
    public function getRandom(): self
2885
    {
2886 4
        return $this->randomImmutable();
2887
    }
2888
2889
    /**
2890
     * alias: for "Arrayy->randomKey()"
2891
     *
2892
     * @return mixed
2893
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2894
     *
2895
     * @see Arrayy::randomKey()
2896
     */
2897 3
    public function getRandomKey()
2898
    {
2899 3
        return $this->randomKey();
2900
    }
2901
2902
    /**
2903
     * alias: for "Arrayy->randomKeys()"
2904
     *
2905
     * @param int $number
2906
     *
2907
     * @return static
2908
     *                <p>(Immutable)</p>
2909
     *
2910
     * @see          Arrayy::randomKeys()
2911
     *
2912
     * @psalm-return static<TKey,T>
2913
     */
2914 8
    public function getRandomKeys(int $number): self
2915
    {
2916 8
        return $this->randomKeys($number);
2917
    }
2918
2919
    /**
2920
     * alias: for "Arrayy->randomValue()"
2921
     *
2922
     * @return mixed
2923
     *               <p>Get a random value or null if there wasn't a value.</p>
2924
     *
2925
     * @see Arrayy::randomValue()
2926
     */
2927 3
    public function getRandomValue()
2928
    {
2929 3
        return $this->randomValue();
2930
    }
2931
2932
    /**
2933
     * alias: for "Arrayy->randomValues()"
2934
     *
2935
     * @param int $number
2936
     *
2937
     * @return static
2938
     *                <p>(Immutable)</p>
2939
     *
2940
     * @see          Arrayy::randomValues()
2941
     *
2942
     * @psalm-return static<TKey,T>
2943
     */
2944 6
    public function getRandomValues(int $number): self
2945
    {
2946 6
        return $this->randomValues($number);
2947
    }
2948
2949
    /**
2950
     * Gets all values.
2951
     *
2952
     * @return static
2953
     *                <p>The values of all elements in this array, in the order they
2954
     *                appear in the array.</p>
2955
     *
2956
     * @psalm-return static<TKey,T>
2957
     */
2958 4
    public function getValues()
2959
    {
2960 4
        $this->generatorToArray(false);
2961
2962 4
        return static::create(
2963 4
            \array_values($this->array),
2964 4
            $this->iteratorClass,
2965 4
            false
2966
        );
2967
    }
2968
2969
    /**
2970
     * Gets all values via Generator.
2971
     *
2972
     * @return \Generator
2973
     *                    <p>The values of all elements in this array, in the order they
2974
     *                    appear in the array as Generator.</p>
2975
     *
2976
     * @psalm-return \Generator<TKey,T>
2977
     */
2978 4
    public function getValuesYield(): \Generator
2979
    {
2980 4
        yield from $this->getGenerator();
2981 4
    }
2982
2983
    /**
2984
     * Group values from a array according to the results of a closure.
2985
     *
2986
     * @param callable|string $grouper  <p>A callable function name.</p>
2987
     * @param bool            $saveKeys
2988
     *
2989
     * @return static
2990
     *                <p>(Immutable)</p>
2991
     *
2992
     * @psalm-return static<TKey,T>
2993
     * @psalm-mutation-free
2994
     */
2995 4
    public function group($grouper, bool $saveKeys = false): self
2996
    {
2997
        // init
2998 4
        $result = [];
2999
3000
        // Iterate over values, group by property/results from closure.
3001 4
        foreach ($this->getGenerator() as $key => $value) {
3002 4
            if (\is_callable($grouper) === true) {
3003 3
                $groupKey = $grouper($value, $key);
3004
            } else {
3005 1
                $groupKey = $this->get($grouper);
3006
            }
3007
3008 4
            $newValue = $this->get($groupKey, null, $result);
3009
3010 4
            if ($groupKey instanceof self) {
3011
                $groupKey = $groupKey->toArray();
3012
            }
3013
3014 4
            if ($newValue instanceof self) {
3015 4
                $newValue = $newValue->toArray();
3016
            }
3017
3018
            // Add to results.
3019 4
            if ($groupKey !== null) {
3020 3
                if ($saveKeys) {
3021 2
                    $result[$groupKey] = $newValue;
3022 2
                    $result[$groupKey][$key] = $value;
3023
                } else {
3024 1
                    $result[$groupKey] = $newValue;
3025 1
                    $result[$groupKey][] = $value;
3026
                }
3027
            }
3028
        }
3029
3030 4
        return static::create(
3031 4
            $result,
3032 4
            $this->iteratorClass,
3033 4
            false
3034
        );
3035
    }
3036
3037
    /**
3038
     * Check if an array has a given key.
3039
     *
3040
     * @param mixed $key
3041
     *
3042
     * @return bool
3043
     */
3044 30
    public function has($key): bool
3045
    {
3046 30
        static $UN_FOUND = null;
3047
3048 30
        if ($UN_FOUND === null) {
3049
            // Generate unique string to use as marker.
3050 1
            $UN_FOUND = \uniqid('arrayy', true);
3051
        }
3052
3053 30
        if (\is_array($key)) {
3054 1
            if ($key === []) {
3055
                return false;
3056
            }
3057
3058 1
            foreach ($key as $keyTmp) {
3059 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3060 1
                if ($found === false) {
3061 1
                    return false;
3062
                }
3063
            }
3064
3065 1
            return true;
3066
        }
3067
3068 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3069
    }
3070
3071
    /**
3072
     * Check if an array has a given value.
3073
     *
3074
     * INFO: if you need to search recursive please use ```contains()```
3075
     *
3076
     * @param mixed $value
3077
     *
3078
     * @return bool
3079
     */
3080 1
    public function hasValue($value): bool
3081
    {
3082 1
        return $this->contains($value);
3083
    }
3084
3085
    /**
3086
     * Implodes the values of this array.
3087
     *
3088
     * @param string $glue
3089
     *
3090
     * @return string
3091
     * @psalm-mutation-free
3092
     */
3093 28
    public function implode(string $glue = ''): string
3094
    {
3095 28
        return $this->implode_recursive($glue, $this->toArray(), false);
3096
    }
3097
3098
    /**
3099
     * Implodes the keys of this array.
3100
     *
3101
     * @param string $glue
3102
     *
3103
     * @return string
3104
     * @psalm-mutation-free
3105
     */
3106 8
    public function implodeKeys(string $glue = ''): string
3107
    {
3108 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3109
    }
3110
3111
    /**
3112
     * Given a list and an iterate-function that returns
3113
     * a key for each element in the list (or a property name),
3114
     * returns an object with an index of each item.
3115
     *
3116
     * @param mixed $key
3117
     *
3118
     * @return static
3119
     *                <p>(Immutable)</p>
3120
     *
3121
     * @psalm-return static<TKey,T>
3122
     * @psalm-mutation-free
3123
     */
3124 4
    public function indexBy($key): self
3125
    {
3126
        // init
3127 4
        $results = [];
3128
3129 4
        foreach ($this->getGenerator() as $a) {
3130 4
            if (\array_key_exists($key, $a) === true) {
3131 3
                $results[$a[$key]] = $a;
3132
            }
3133
        }
3134
3135 4
        return static::create(
3136 4
            $results,
3137 4
            $this->iteratorClass,
3138 4
            false
3139
        );
3140
    }
3141
3142
    /**
3143
     * alias: for "Arrayy->searchIndex()"
3144
     *
3145
     * @param mixed $value <p>The value to search for.</p>
3146
     *
3147
     * @return false|mixed
3148
     *
3149
     * @see Arrayy::searchIndex()
3150
     */
3151 4
    public function indexOf($value)
3152
    {
3153 4
        return $this->searchIndex($value);
3154
    }
3155
3156
    /**
3157
     * Get everything but the last..$to items.
3158
     *
3159
     * @param int $to
3160
     *
3161
     * @return static
3162
     *                <p>(Immutable)</p>
3163
     *
3164
     * @psalm-return static<TKey,T>
3165
     * @psalm-mutation-free
3166
     */
3167 12
    public function initial(int $to = 1): self
3168
    {
3169 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3170
    }
3171
3172
    /**
3173
     * Return an array with all elements found in input array.
3174
     *
3175
     * @param array $search
3176
     * @param bool  $keepKeys
3177
     *
3178
     * @return static
3179
     *                <p>(Immutable)</p>
3180
     *
3181
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3182
     * @psalm-return static<TKey,T>
3183
     * @psalm-mutation-free
3184
     */
3185 4
    public function intersection(array $search, bool $keepKeys = false): self
3186
    {
3187 4
        if ($keepKeys) {
3188
            /**
3189
             * @psalm-suppress MissingClosureReturnType
3190
             * @psalm-suppress MissingClosureParamType
3191
             */
3192 1
            return static::create(
3193 1
                \array_uintersect(
3194 1
                    $this->toArray(),
3195 1
                    $search,
3196
                    static function ($a, $b) {
3197 1
                        return $a === $b ? 0 : -1;
3198 1
                    }
3199
                ),
3200 1
                $this->iteratorClass,
3201 1
                false
3202
            );
3203
        }
3204
3205 3
        return static::create(
3206 3
            \array_values(\array_intersect($this->toArray(), $search)),
3207 3
            $this->iteratorClass,
3208 3
            false
3209
        );
3210
    }
3211
3212
    /**
3213
     * Return an array with all elements found in input array.
3214
     *
3215
     * @param array ...$array
3216
     *
3217
     * @return static
3218
     *                <p>(Immutable)</p>
3219
     *
3220
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3221
     * @psalm-return static<TKey,T>
3222
     * @psalm-mutation-free
3223
     */
3224 1
    public function intersectionMulti(...$array): self
3225
    {
3226 1
        return static::create(
3227 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3228 1
            $this->iteratorClass,
3229 1
            false
3230
        );
3231
    }
3232
3233
    /**
3234
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3235
     *
3236
     * @param array $search
3237
     *
3238
     * @return bool
3239
     *
3240
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3241
     */
3242 1
    public function intersects(array $search): bool
3243
    {
3244 1
        return $this->intersection($search)->count() > 0;
3245
    }
3246
3247
    /**
3248
     * Invoke a function on all of an array's values.
3249
     *
3250
     * @param callable $callable
3251
     * @param mixed    $arguments
3252
     *
3253
     * @return static
3254
     *                <p>(Immutable)</p>
3255
     *
3256
     * @psalm-param  callable(T=,mixed):mixed $callable
3257
     * @psalm-return static<TKey,T>
3258
     * @psalm-mutation-free
3259
     */
3260 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...
3261
    {
3262
        // If one argument given for each iteration, create an array for it.
3263 1
        if (\is_array($arguments) === false) {
3264 1
            $arguments = \array_fill(
3265 1
                0,
3266 1
                $this->count(),
3267 1
                $arguments
3268
            );
3269
        }
3270
3271
        // If the callable has arguments, pass them.
3272 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...
3273 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3274
        } else {
3275 1
            $array = $this->map($callable);
3276
        }
3277
3278 1
        return static::create(
3279 1
            $array,
3280 1
            $this->iteratorClass,
3281 1
            false
3282
        );
3283
    }
3284
3285
    /**
3286
     * Check whether array is associative or not.
3287
     *
3288
     * @param bool $recursive
3289
     *
3290
     * @return bool
3291
     *              <p>Returns true if associative, false otherwise.</p>
3292
     */
3293 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...
3294
    {
3295 15
        if ($this->isEmpty()) {
3296 3
            return false;
3297
        }
3298
3299
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3300 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3301 13
            if ((string) $key !== $key) {
3302 11
                return false;
3303
            }
3304
        }
3305
3306 3
        return true;
3307
    }
3308
3309
    /**
3310
     * Check if a given key or keys are empty.
3311
     *
3312
     * @param int|int[]|string|string[]|null $keys
3313
     *
3314
     * @return bool
3315
     *              <p>Returns true if empty, false otherwise.</p>
3316
     * @psalm-mutation-free
3317
     */
3318 45
    public function isEmpty($keys = null): bool
3319
    {
3320 45
        if ($this->generator) {
3321
            return $this->toArray() === [];
3322
        }
3323
3324 45
        if ($keys === null) {
3325 43
            return $this->array === [];
3326
        }
3327
3328 2
        foreach ((array) $keys as $key) {
3329 2
            if (!empty($this->get($key))) {
3330 2
                return false;
3331
            }
3332
        }
3333
3334 2
        return true;
3335
    }
3336
3337
    /**
3338
     * Check if the current array is equal to the given "$array" or not.
3339
     *
3340
     * @param array $array
3341
     *
3342
     * @return bool
3343
     *
3344
     * @psalm-param array<mixed,mixed> $array
3345
     */
3346 1
    public function isEqual(array $array): bool
3347
    {
3348 1
        return $this->toArray() === $array;
3349
    }
3350
3351
    /**
3352
     * Check if the current array is a multi-array.
3353
     *
3354
     * @return bool
3355
     */
3356 22
    public function isMultiArray(): bool
3357
    {
3358
        return !(
3359 22
            \count($this->toArray(), \COUNT_NORMAL)
3360
            ===
3361 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3362
        );
3363
    }
3364
3365
    /**
3366
     * Check whether array is numeric or not.
3367
     *
3368
     * @return bool
3369
     *              <p>Returns true if numeric, false otherwise.</p>
3370
     */
3371 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...
3372
    {
3373 5
        if ($this->isEmpty()) {
3374 2
            return false;
3375
        }
3376
3377
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3378 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3379 4
            if ((int) $key !== $key) {
3380 2
                return false;
3381
            }
3382
        }
3383
3384 2
        return true;
3385
    }
3386
3387
    /**
3388
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3389
     *
3390
     * @param bool $recursive
3391
     *
3392
     * @return bool
3393
     * @psalm-mutation-free
3394
     */
3395 9
    public function isSequential(bool $recursive = false): bool
3396
    {
3397
3398
        // recursive
3399
3400 9
        if ($recursive === true) {
3401
            return $this->array_keys_recursive($this->toArray())
3402
                   ===
3403
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3404
        }
3405
3406
        // non recursive
3407
3408 9
        return \array_keys($this->toArray())
3409
               ===
3410 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3411
    }
3412
3413
    /**
3414
     * @return array
3415
     *
3416
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3417
     */
3418 2
    public function jsonSerialize(): array
3419
    {
3420 2
        return $this->toArray();
3421
    }
3422
3423
    /**
3424
     * Gets the key/index of the element at the current internal iterator position.
3425
     *
3426
     * @return int|string|null
3427
     */
3428
    public function key()
3429
    {
3430
        return \key($this->array);
3431
    }
3432
3433
    /**
3434
     * Checks if the given key exists in the provided array.
3435
     *
3436
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3437
     *       then you need to use "Arrayy->offsetExists()".
3438
     *
3439
     * @param int|string $key the key to look for
3440
     *
3441
     * @return bool
3442
     * @psalm-mutation-free
3443
     */
3444 162
    public function keyExists($key): bool
3445
    {
3446 162
        return \array_key_exists($key, $this->array);
3447
    }
3448
3449
    /**
3450
     * Get all keys from the current array.
3451
     *
3452
     * @param bool       $recursive     [optional] <p>
3453
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3454
     *                                  </p>
3455
     * @param mixed|null $search_values [optional] <p>
3456
     *                                  If specified, then only keys containing these values are returned.
3457
     *                                  </p>
3458
     * @param bool       $strict        [optional] <p>
3459
     *                                  Determines if strict comparison (===) should be used during the search.
3460
     *                                  </p>
3461
     *
3462
     * @return static
3463
     *                <p>(Immutable) An array of all the keys in input.</p>
3464
     *
3465
     * @psalm-return static<array-key,TKey>
3466
     * @psalm-mutation-free
3467
     */
3468 29
    public function keys(
3469
        bool $recursive = false,
3470
        $search_values = null,
3471
        bool $strict = true
3472
    ): self {
3473
3474
        // recursive
3475
3476 29
        if ($recursive === true) {
3477 4
            $array = $this->array_keys_recursive(
3478 4
                null,
3479 4
                $search_values,
3480 4
                $strict
3481
            );
3482
3483 4
            return static::create(
3484 4
                $array,
3485 4
                $this->iteratorClass,
3486 4
                false
3487
            );
3488
        }
3489
3490
        // non recursive
3491
3492 28
        if ($search_values === null) {
3493
            $arrayFunction = function (): \Generator {
3494 28
                foreach ($this->getGenerator() as $key => $value) {
3495 26
                    yield $key;
3496
                }
3497 28
            };
3498
        } else {
3499
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3500 1
                $is_array_tmp = \is_array($search_values);
3501
3502
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3503 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3504 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...
3505
                        (
3506 1
                            $is_array_tmp === false
3507
                            &&
3508 1
                            $strict === true
3509
                            &&
3510 1
                            $search_values === $value
3511
                        )
3512
                        ||
3513
                        (
3514 1
                            $is_array_tmp === false
3515
                            &&
3516 1
                            $strict === false
3517
                            &&
3518 1
                            $search_values == $value
3519
                        )
3520
                        ||
3521
                        (
3522 1
                            $is_array_tmp === true
3523
                            &&
3524 1
                            \in_array($value, $search_values, $strict)
3525
                        )
3526
                    ) {
3527 1
                        yield $key;
3528
                    }
3529
                }
3530 1
            };
3531
        }
3532
3533 28
        return static::create(
3534 28
            $arrayFunction,
3535 28
            $this->iteratorClass,
3536 28
            false
3537
        );
3538
    }
3539
3540
    /**
3541
     * Sort an array by key in reverse order.
3542
     *
3543
     * @param int $sort_flags [optional] <p>
3544
     *                        You may modify the behavior of the sort using the optional
3545
     *                        parameter sort_flags, for details
3546
     *                        see sort.
3547
     *                        </p>
3548
     *
3549
     * @return $this
3550
     *               <p>(Mutable) Return this Arrayy object.</p>
3551
     *
3552
     * @psalm-return static<TKey,T>
3553
     */
3554 4
    public function krsort(int $sort_flags = 0): self
3555
    {
3556 4
        $this->generatorToArray();
3557
3558 4
        \krsort($this->array, $sort_flags);
3559
3560 4
        return $this;
3561
    }
3562
3563
    /**
3564
     * Sort an array by key in reverse order.
3565
     *
3566
     * @param int $sort_flags [optional] <p>
3567
     *                        You may modify the behavior of the sort using the optional
3568
     *                        parameter sort_flags, for details
3569
     *                        see sort.
3570
     *                        </p>
3571
     *
3572
     * @return $this
3573
     *               <p>(Immutable) Return this Arrayy object.</p>
3574
     *
3575
     * @psalm-return static<TKey,T>
3576
     * @psalm-mutation-free
3577
     */
3578 4
    public function krsortImmutable(int $sort_flags = 0): self
3579
    {
3580 4
        $that = clone $this;
3581
3582
        /**
3583
         * @psalm-suppress ImpureMethodCall - object is already cloned
3584
         */
3585 4
        $that->krsort($sort_flags);
3586
3587 4
        return $that;
3588
    }
3589
3590
    /**
3591
     * Get the last value from the current array.
3592
     *
3593
     * @return mixed|null
3594
     *                    <p>Return null if there wasn't a element.</p>
3595
     * @psalm-mutation-free
3596
     */
3597 17
    public function last()
3598
    {
3599 17
        $key_last = $this->lastKey();
3600 17
        if ($key_last === null) {
3601 2
            return null;
3602
        }
3603
3604 15
        return $this->get($key_last);
3605
    }
3606
3607
    /**
3608
     * Get the last key from the current array.
3609
     *
3610
     * @return mixed|null
3611
     *                    <p>Return null if there wasn't a element.</p>
3612
     * @psalm-mutation-free
3613
     */
3614 21
    public function lastKey()
3615
    {
3616 21
        $this->generatorToArray();
3617
3618 21
        return \array_key_last($this->array);
3619
    }
3620
3621
    /**
3622
     * Get the last value(s) from the current array.
3623
     *
3624
     * @param int|null $number
3625
     *
3626
     * @return static
3627
     *                <p>(Immutable)</p>
3628
     *
3629
     * @psalm-return static<TKey,T>
3630
     * @psalm-mutation-free
3631
     */
3632 13
    public function lastsImmutable(int $number = null): self
3633
    {
3634 13
        if ($this->isEmpty()) {
3635 1
            return static::create(
3636 1
                [],
3637 1
                $this->iteratorClass,
3638 1
                false
3639
            );
3640
        }
3641
3642 12
        if ($number === null) {
3643 8
            $poppedValue = $this->last();
3644
3645 8
            if ($poppedValue === null) {
3646 1
                $poppedValue = [$poppedValue];
3647
            } else {
3648 7
                $poppedValue = (array) $poppedValue;
3649
            }
3650
3651 8
            $arrayy = static::create(
3652 8
                $poppedValue,
3653 8
                $this->iteratorClass,
3654 8
                false
3655
            );
3656
        } else {
3657 4
            $arrayy = $this->rest(-$number);
3658
        }
3659
3660 12
        return $arrayy;
3661
    }
3662
3663
    /**
3664
     * Get the last value(s) from the current array.
3665
     *
3666
     * @param int|null $number
3667
     *
3668
     * @return $this
3669
     *               <p>(Mutable)</p>
3670
     *
3671
     * @psalm-return static<TKey,T>
3672
     */
3673 13
    public function lastsMutable(int $number = null): self
3674
    {
3675 13
        if ($this->isEmpty()) {
3676 1
            return $this;
3677
        }
3678
3679 12
        if ($number === null) {
3680 8
            $poppedValue = $this->last();
3681
3682 8
            if ($poppedValue === null) {
3683 1
                $poppedValue = [$poppedValue];
3684
            } else {
3685 7
                $poppedValue = (array) $poppedValue;
3686
            }
3687
3688 8
            $this->array = static::create(
3689 8
                $poppedValue,
3690 8
                $this->iteratorClass,
3691 8
                false
3692 8
            )->toArray();
3693
        } else {
3694 4
            $this->array = $this->rest(-$number)->toArray();
3695
        }
3696
3697 12
        $this->generator = null;
3698
3699 12
        return $this;
3700
    }
3701
3702
    /**
3703
     * Count the values from the current array.
3704
     *
3705
     * alias: for "Arrayy->count()"
3706
     *
3707
     * @param int $mode
3708
     *
3709
     * @return int
3710
     *
3711
     * @see Arrayy::count()
3712
     */
3713 20
    public function length(int $mode = \COUNT_NORMAL): int
3714
    {
3715 20
        return $this->count($mode);
3716
    }
3717
3718
    /**
3719
     * Apply the given function to the every element of the array,
3720
     * collecting the results.
3721
     *
3722
     * @param callable $callable
3723
     * @param bool     $useKeyAsSecondParameter
3724
     * @param mixed    ...$arguments
3725
     *
3726
     * @return static
3727
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3728
     *
3729
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3730
     * @psalm-return static<TKey,T>
3731
     * @psalm-mutation-free
3732
     */
3733 5
    public function map(
3734
        callable $callable,
3735
        bool $useKeyAsSecondParameter = false,
3736
        ...$arguments
3737
    ) {
3738
        /**
3739
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3740
         */
3741 5
        $useArguments = \func_num_args() > 2;
3742
3743 5
        return static::create(
3744
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3745 5
                foreach ($this->getGenerator() as $key => $value) {
3746 4
                    if ($useArguments) {
3747 3
                        if ($useKeyAsSecondParameter) {
3748
                            yield $key => $callable($value, $key, ...$arguments);
3749
                        } else {
3750 3
                            yield $key => $callable($value, ...$arguments);
3751
                        }
3752
                    } else {
3753
                        /** @noinspection NestedPositiveIfStatementsInspection */
3754 4
                        if ($useKeyAsSecondParameter) {
3755
                            yield $key => $callable($value, $key);
3756
                        } else {
3757 4
                            yield $key => $callable($value);
3758
                        }
3759
                    }
3760
                }
3761 5
            },
3762 5
            $this->iteratorClass,
3763 5
            false
3764
        );
3765
    }
3766
3767
    /**
3768
     * Check if all items in current array match a truth test.
3769
     *
3770
     * @param \Closure $closure
3771
     *
3772
     * @return bool
3773
     */
3774 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...
3775
    {
3776 15
        if ($this->count() === 0) {
3777 2
            return false;
3778
        }
3779
3780 13
        foreach ($this->getGenerator() as $key => $value) {
3781 13
            $value = $closure($value, $key);
3782
3783 13
            if ($value === false) {
3784 7
                return false;
3785
            }
3786
        }
3787
3788 7
        return true;
3789
    }
3790
3791
    /**
3792
     * Check if any item in the current array matches a truth test.
3793
     *
3794
     * @param \Closure $closure
3795
     *
3796
     * @return bool
3797
     */
3798 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...
3799
    {
3800 14
        if ($this->count() === 0) {
3801 2
            return false;
3802
        }
3803
3804 12
        foreach ($this->getGenerator() as $key => $value) {
3805 12
            $value = $closure($value, $key);
3806
3807 12
            if ($value === true) {
3808 9
                return true;
3809
            }
3810
        }
3811
3812 4
        return false;
3813
    }
3814
3815
    /**
3816
     * Get the max value from an array.
3817
     *
3818
     * @return false|mixed
3819
     *                     <p>Will return false if there are no values.</p>
3820
     */
3821 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...
3822
    {
3823 10
        if ($this->count() === 0) {
3824 1
            return false;
3825
        }
3826
3827 9
        $max = false;
3828
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3829 9
        foreach ($this->getGeneratorByReference() as &$value) {
3830
            if (
3831 9
                $max === false
3832
                ||
3833 9
                $value > $max
3834
            ) {
3835 9
                $max = $value;
3836
            }
3837
        }
3838
3839 9
        return $max;
3840
    }
3841
3842
    /**
3843
     * Merge the new $array into the current array.
3844
     *
3845
     * - keep key,value from the current array, also if the index is in the new $array
3846
     *
3847
     * @param array $array
3848
     * @param bool  $recursive
3849
     *
3850
     * @return static
3851
     *                <p>(Immutable)</p>
3852
     *
3853
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3854
     * @psalm-return static<int|TKey,T>
3855
     * @psalm-mutation-free
3856
     */
3857 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...
3858
    {
3859 32
        if ($recursive === true) {
3860 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
3861 9
            $result = \array_replace_recursive($this->toArray(), $array);
3862
        } else {
3863 23
            $result = \array_replace($this->toArray(), $array);
3864
        }
3865
3866 32
        return static::create(
3867 32
            $result,
3868 32
            $this->iteratorClass,
3869 32
            false
3870
        );
3871
    }
3872
3873
    /**
3874
     * Merge the new $array into the current array.
3875
     *
3876
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3877
     * - create new indexes
3878
     *
3879
     * @param array $array
3880
     * @param bool  $recursive
3881
     *
3882
     * @return static
3883
     *                <p>(Immutable)</p>
3884
     *
3885
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3886
     * @psalm-return static<TKey,T>
3887
     * @psalm-mutation-free
3888
     */
3889 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...
3890
    {
3891 19
        if ($recursive === true) {
3892 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
3893 5
            $result = \array_merge_recursive($this->toArray(), $array);
3894
        } else {
3895 14
            $result = \array_merge($this->toArray(), $array);
3896
        }
3897
3898 19
        return static::create(
3899 19
            $result,
3900 19
            $this->iteratorClass,
3901 19
            false
3902
        );
3903
    }
3904
3905
    /**
3906
     * Merge the the current array into the $array.
3907
     *
3908
     * - use key,value from the new $array, also if the index is in the current array
3909
     *
3910
     * @param array $array
3911
     * @param bool  $recursive
3912
     *
3913
     * @return static
3914
     *                <p>(Immutable)</p>
3915
     *
3916
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3917
     * @psalm-return static<TKey,T>
3918
     * @psalm-mutation-free
3919
     */
3920 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...
3921
    {
3922 16
        if ($recursive === true) {
3923 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
3924 4
            $result = \array_replace_recursive($array, $this->toArray());
3925
        } else {
3926 12
            $result = \array_replace($array, $this->toArray());
3927
        }
3928
3929 16
        return static::create(
3930 16
            $result,
3931 16
            $this->iteratorClass,
3932 16
            false
3933
        );
3934
    }
3935
3936
    /**
3937
     * Merge the current array into the new $array.
3938
     *
3939
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3940
     * - create new indexes
3941
     *
3942
     * @param array $array
3943
     * @param bool  $recursive
3944
     *
3945
     * @return static
3946
     *                <p>(Immutable)</p>
3947
     *
3948
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3949
     * @psalm-return static<TKey,T>
3950
     * @psalm-mutation-free
3951
     */
3952 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...
3953
    {
3954 20
        if ($recursive === true) {
3955 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
3956 7
            $result = \array_merge_recursive($array, $this->toArray());
3957
        } else {
3958 13
            $result = \array_merge($array, $this->toArray());
3959
        }
3960
3961 20
        return static::create(
3962 20
            $result,
3963 20
            $this->iteratorClass,
3964 20
            false
3965
        );
3966
    }
3967
3968
    /**
3969
     * @return ArrayyMeta|static
3970
     */
3971 16
    public static function meta()
3972
    {
3973 16
        return (new ArrayyMeta())->getMetaObject(static::class);
3974
    }
3975
3976
    /**
3977
     * Get the min value from an array.
3978
     *
3979
     * @return false|mixed
3980
     *                     <p>Will return false if there are no values.</p>
3981
     */
3982 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...
3983
    {
3984 10
        if ($this->count() === 0) {
3985 1
            return false;
3986
        }
3987
3988 9
        $min = false;
3989
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3990 9
        foreach ($this->getGeneratorByReference() as &$value) {
3991
            if (
3992 9
                $min === false
3993
                ||
3994 9
                $value < $min
3995
            ) {
3996 9
                $min = $value;
3997
            }
3998
        }
3999
4000 9
        return $min;
4001
    }
4002
4003
    /**
4004
     * Get the most used value from the array.
4005
     *
4006
     * @return mixed|null
4007
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4008
     * @psalm-mutation-free
4009
     */
4010 3
    public function mostUsedValue()
4011
    {
4012 3
        return $this->countValues()->arsortImmutable()->firstKey();
4013
    }
4014
4015
    /**
4016
     * Get the most used value from the array.
4017
     *
4018
     * @param int|null $number <p>How many values you will take?</p>
4019
     *
4020
     * @return static
4021
     *                <p>(Immutable)</p>
4022
     *
4023
     * @psalm-return static<TKey,T>
4024
     * @psalm-mutation-free
4025
     */
4026 3
    public function mostUsedValues(int $number = null): self
4027
    {
4028 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4029
    }
4030
4031
    /**
4032
     * Move an array element to a new index.
4033
     *
4034
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
4035
     *
4036
     * @param int|string $from
4037
     * @param int        $to
4038
     *
4039
     * @return static
4040
     *                <p>(Immutable)</p>
4041
     *
4042
     * @psalm-return static<TKey,T>
4043
     * @psalm-mutation-free
4044
     */
4045 1
    public function moveElement($from, $to): self
4046
    {
4047 1
        $array = $this->toArray();
4048
4049 1
        if ((int) $from === $from) {
4050 1
            $tmp = \array_splice($array, $from, 1);
4051 1
            \array_splice($array, (int) $to, 0, $tmp);
4052 1
            $output = $array;
4053 1
        } elseif ((string) $from === $from) {
4054 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4055 1
            $itemToMove = $array[$from];
4056 1
            if ($indexToMove !== false) {
4057 1
                \array_splice($array, $indexToMove, 1);
4058
            }
4059 1
            $i = 0;
4060 1
            $output = [];
4061 1
            foreach ($array as $key => $item) {
4062 1
                if ($i === $to) {
4063 1
                    $output[$from] = $itemToMove;
4064
                }
4065 1
                $output[$key] = $item;
4066 1
                ++$i;
4067
            }
4068
        } else {
4069
            $output = [];
4070
        }
4071
4072 1
        return static::create(
4073 1
            $output,
4074 1
            $this->iteratorClass,
4075 1
            false
4076
        );
4077
    }
4078
4079
    /**
4080
     * Move an array element to the first place.
4081
     *
4082
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4083
     *       loss the keys of an indexed array.
4084
     *
4085
     * @param int|string $key
4086
     *
4087
     * @return static
4088
     *                <p>(Immutable)</p>
4089
     *
4090
     * @psalm-return static<TKey,T>
4091
     * @psalm-mutation-free
4092
     */
4093 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...
4094
    {
4095 1
        $array = $this->toArray();
4096
4097 1
        if ($this->offsetExists($key)) {
4098 1
            $tmpValue = $this->get($key);
4099 1
            unset($array[$key]);
4100 1
            $array = [$key => $tmpValue] + $array;
4101
        }
4102
4103 1
        return static::create(
4104 1
            $array,
4105 1
            $this->iteratorClass,
4106 1
            false
4107
        );
4108
    }
4109
4110
    /**
4111
     * Move an array element to the last place.
4112
     *
4113
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4114
     *       loss the keys of an indexed array.
4115
     *
4116
     * @param int|string $key
4117
     *
4118
     * @return static
4119
     *                <p>(Immutable)</p>
4120
     *
4121
     * @psalm-return static<TKey,T>
4122
     * @psalm-mutation-free
4123
     */
4124 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...
4125
    {
4126 1
        $array = $this->toArray();
4127
4128 1
        if ($this->offsetExists($key)) {
4129 1
            $tmpValue = $this->get($key);
4130 1
            unset($array[$key]);
4131 1
            $array += [$key => $tmpValue];
4132
        }
4133
4134 1
        return static::create(
4135 1
            $array,
4136 1
            $this->iteratorClass,
4137 1
            false
4138
        );
4139
    }
4140
4141
    /**
4142
     * Moves the internal iterator position to the next element and returns this element.
4143
     *
4144
     * @return false|mixed
4145
     *                     <p>(Mutable) Will return false if there are no values.</p>
4146
     */
4147
    public function next()
4148
    {
4149
        return \next($this->array);
4150
    }
4151
4152
    /**
4153
     * Get the next nth keys and values from the array.
4154
     *
4155
     * @param int $step
4156
     * @param int $offset
4157
     *
4158
     * @return static
4159
     *                <p>(Immutable)</p>
4160
     *
4161
     * @psalm-return static<TKey,T>
4162
     * @psalm-mutation-free
4163
     */
4164 1
    public function nth(int $step, int $offset = 0): self
4165
    {
4166
        $arrayFunction = function () use ($step, $offset): \Generator {
4167 1
            $position = 0;
4168 1
            foreach ($this->getGenerator() as $key => $value) {
4169 1
                if ($position++ % $step !== $offset) {
4170 1
                    continue;
4171
                }
4172
4173 1
                yield $key => $value;
4174
            }
4175 1
        };
4176
4177 1
        return static::create(
4178 1
            $arrayFunction,
4179 1
            $this->iteratorClass,
4180 1
            false
4181
        );
4182
    }
4183
4184
    /**
4185
     * Get a subset of the items from the given array.
4186
     *
4187
     * @param mixed[] $keys
4188
     *
4189
     * @return static
4190
     *                <p>(Immutable)</p>
4191
     *
4192
     * @psalm-return static<TKey,T>
4193
     * @psalm-mutation-free
4194
     */
4195 1
    public function only(array $keys): self
4196
    {
4197 1
        $array = $this->toArray();
4198
4199 1
        return static::create(
4200 1
            \array_intersect_key($array, \array_flip($keys)),
4201 1
            $this->iteratorClass,
4202 1
            false
4203
        );
4204
    }
4205
4206
    /**
4207
     * Pad array to the specified size with a given value.
4208
     *
4209
     * @param int   $size  <p>Size of the result array.</p>
4210
     * @param mixed $value <p>Empty value by default.</p>
4211
     *
4212
     * @return static
4213
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4214
     *
4215
     * @psalm-return static<TKey,T>
4216
     * @psalm-mutation-free
4217
     */
4218 5
    public function pad(int $size, $value): self
4219
    {
4220 5
        return static::create(
4221 5
            \array_pad($this->toArray(), $size, $value),
4222 5
            $this->iteratorClass,
4223 5
            false
4224
        );
4225
    }
4226
4227
    /**
4228
     * Partitions this array in two array according to a predicate.
4229
     * Keys are preserved in the resulting array.
4230
     *
4231
     * @param \Closure $closure
4232
     *                          <p>The predicate on which to partition.</p>
4233
     *
4234
     * @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...
4235
     *                    <p>An array with two elements. The first element contains the array
4236
     *                    of elements where the predicate returned TRUE, the second element
4237
     *                    contains the array of elements where the predicate returned FALSE.</p>
4238
     *
4239
     * @psalm-return array<int, static<TKey,T>>
4240
     */
4241 1
    public function partition(\Closure $closure): array
4242
    {
4243
        // init
4244 1
        $matches = [];
4245 1
        $noMatches = [];
4246
4247 1
        foreach ($this->array as $key => $value) {
4248 1
            if ($closure($value, $key)) {
4249 1
                $matches[$key] = $value;
4250
            } else {
4251 1
                $noMatches[$key] = $value;
4252
            }
4253
        }
4254
4255 1
        return [self::create($matches), self::create($noMatches)];
4256
    }
4257
4258
    /**
4259
     * Pop a specified value off the end of the current array.
4260
     *
4261
     * @return mixed|null
4262
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4263
     */
4264 5
    public function pop()
4265
    {
4266 5
        $this->generatorToArray();
4267
4268 5
        return \array_pop($this->array);
4269
    }
4270
4271
    /**
4272
     * Prepend a (key) + value to the current array.
4273
     *
4274
     * EXAMPLE: <code>
4275
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4276
     * </code>
4277
     *
4278
     * @param mixed $value
4279
     * @param mixed $key
4280
     *
4281
     * @return $this
4282
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4283
     *
4284
     * @psalm-return static<TKey,T>
4285
     */
4286 11
    public function prepend($value, $key = null)
4287
    {
4288 11
        $this->generatorToArray();
4289
4290 11
        if ($this->properties !== []) {
4291 3
            $this->checkType($key, $value);
4292
        }
4293
4294 9
        if ($key === null) {
4295 8
            \array_unshift($this->array, $value);
4296
        } else {
4297 2
            $this->array = [$key => $value] + $this->array;
4298
        }
4299
4300 9
        return $this;
4301
    }
4302
4303
    /**
4304
     * Add a suffix to each key.
4305
     *
4306
     * @param mixed $suffix
4307
     *
4308
     * @return static
4309
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4310
     *
4311
     * @psalm-return static<TKey,T>
4312
     * @psalm-mutation-free
4313
     */
4314 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...
4315
    {
4316
        // init
4317 10
        $result = [];
4318
4319 10
        foreach ($this->getGenerator() as $key => $item) {
4320 9
            if ($item instanceof self) {
4321
                $result[$key] = $item->prependToEachKey($suffix);
4322 9
            } elseif (\is_array($item) === true) {
4323
                $result[$key] = self::create(
4324
                    $item,
4325
                    $this->iteratorClass,
4326
                    false
4327
                )->prependToEachKey($suffix)
4328
                    ->toArray();
4329
            } else {
4330 9
                $result[$key . $suffix] = $item;
4331
            }
4332
        }
4333
4334 10
        return self::create(
4335 10
            $result,
4336 10
            $this->iteratorClass,
4337 10
            false
4338
        );
4339
    }
4340
4341
    /**
4342
     * Add a suffix to each value.
4343
     *
4344
     * @param mixed $suffix
4345
     *
4346
     * @return static
4347
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4348
     *
4349
     * @psalm-return static<TKey,T>
4350
     * @psalm-mutation-free
4351
     */
4352 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...
4353
    {
4354
        // init
4355 10
        $result = [];
4356
4357 10
        foreach ($this->getGenerator() as $key => $item) {
4358 9
            if ($item instanceof self) {
4359
                $result[$key] = $item->prependToEachValue($suffix);
4360 9
            } elseif (\is_array($item) === true) {
4361
                $result[$key] = self::create(
4362
                    $item,
4363
                    $this->iteratorClass,
4364
                    false
4365
                )->prependToEachValue($suffix)
4366
                    ->toArray();
4367 9
            } elseif (\is_object($item) === true) {
4368 1
                $result[$key] = $item;
4369
            } else {
4370 8
                $result[$key] = $item . $suffix;
4371
            }
4372
        }
4373
4374 10
        return self::create(
4375 10
            $result,
4376 10
            $this->iteratorClass,
4377 10
            false
4378
        );
4379
    }
4380
4381
    /**
4382
     * Return the value of a given key and
4383
     * delete the key.
4384
     *
4385
     * @param int|int[]|string|string[]|null $keyOrKeys
4386
     * @param mixed                          $fallback
4387
     *
4388
     * @return mixed
4389
     */
4390 5
    public function pull($keyOrKeys = null, $fallback = null)
4391
    {
4392 5
        if ($keyOrKeys === null) {
4393 1
            $array = $this->toArray();
4394 1
            $this->clear();
4395
4396 1
            return $array;
4397
        }
4398
4399 4
        if (\is_array($keyOrKeys) === true) {
4400 1
            $valueOrValues = [];
4401 1
            foreach ($keyOrKeys as $key) {
4402 1
                $valueOrValues[] = $this->get($key, $fallback);
4403 1
                $this->offsetUnset($key);
4404
            }
4405
        } else {
4406 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4407 4
            $this->offsetUnset($keyOrKeys);
4408
        }
4409
4410 4
        return $valueOrValues;
4411
    }
4412
4413
    /**
4414
     * Push one or more values onto the end of array at once.
4415
     *
4416
     * @param array ...$args
4417
     *
4418
     * @return $this
4419
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4420
     *
4421
     * @noinspection ReturnTypeCanBeDeclaredInspection
4422
     *
4423
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4424
     * @psalm-return static<TKey,T>
4425
     */
4426 7
    public function push(...$args)
4427
    {
4428 7
        $this->generatorToArray();
4429
4430
        if (
4431 7
            $this->checkPropertyTypes
4432
            &&
4433 7
            $this->properties !== []
4434
        ) {
4435 1
            foreach ($args as $key => $value) {
4436 1
                $this->checkType($key, $value);
4437
            }
4438
        }
4439
4440 7
        \array_push($this->array, ...$args);
4441
4442 7
        return $this;
4443
    }
4444
4445
    /**
4446
     * Get a random value from the current array.
4447
     *
4448
     * @param int|null $number <p>How many values you will take?</p>
4449
     *
4450
     * @return static
4451
     *                <p>(Immutable)</p>
4452
     *
4453
     * @psalm-return static<int|array-key,T>
4454
     */
4455 19
    public function randomImmutable(int $number = null): self
4456
    {
4457 19
        $this->generatorToArray();
4458
4459 19
        if ($this->count() === 0) {
4460 1
            return static::create(
4461 1
                [],
4462 1
                $this->iteratorClass,
4463 1
                false
4464
            );
4465
        }
4466
4467 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...
4468
            /** @noinspection NonSecureArrayRandUsageInspection */
4469 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4470
4471 13
            return static::create(
4472 13
                $arrayRandValue,
4473 13
                $this->iteratorClass,
4474 13
                false
4475
            );
4476
        }
4477
4478 6
        $arrayTmp = $this->array;
4479
        /** @noinspection NonSecureShuffleUsageInspection */
4480 6
        \shuffle($arrayTmp);
4481
4482 6
        return static::create(
4483 6
            $arrayTmp,
4484 6
            $this->iteratorClass,
4485 6
            false
4486 6
        )->firstsImmutable($number);
4487
    }
4488
4489
    /**
4490
     * Pick a random key/index from the keys of this array.
4491
     *
4492
     * @throws \RangeException If array is empty
4493
     *
4494
     * @return mixed
4495
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4496
     */
4497 4
    public function randomKey()
4498
    {
4499 4
        $result = $this->randomKeys(1);
4500
4501 4
        if (!isset($result[0])) {
4502
            $result[0] = null;
4503
        }
4504
4505 4
        return $result[0];
4506
    }
4507
4508
    /**
4509
     * Pick a given number of random keys/indexes out of this array.
4510
     *
4511
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4512
     *
4513
     * @throws \RangeException If array is empty
4514
     *
4515
     * @return static
4516
     *                <p>(Immutable)</p>
4517
     *
4518
     * @psalm-return static<TKey,T>
4519
     */
4520 13
    public function randomKeys(int $number): self
4521
    {
4522 13
        $this->generatorToArray();
4523
4524 13
        $count = $this->count();
4525
4526
        if (
4527 13
            $number === 0
4528
            ||
4529 13
            $number > $count
4530
        ) {
4531 2
            throw new \RangeException(
4532 2
                \sprintf(
4533 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4534 2
                    $number,
4535 2
                    $count
4536
                )
4537
            );
4538
        }
4539
4540 11
        $result = (array) \array_rand($this->array, $number);
4541
4542 11
        return static::create(
4543 11
            $result,
4544 11
            $this->iteratorClass,
4545 11
            false
4546
        );
4547
    }
4548
4549
    /**
4550
     * Get a random value from the current array.
4551
     *
4552
     * @param int|null $number <p>How many values you will take?</p>
4553
     *
4554
     * @return $this
4555
     *               <p>(Mutable) Return this Arrayy object.</p>
4556
     *
4557
     * @psalm-return static<TKey,T>
4558
     */
4559 17
    public function randomMutable(int $number = null): self
4560
    {
4561 17
        $this->generatorToArray();
4562
4563 17
        if ($this->count() === 0) {
4564
            return static::create(
4565
                [],
4566
                $this->iteratorClass,
4567
                false
4568
            );
4569
        }
4570
4571 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...
4572
            /** @noinspection NonSecureArrayRandUsageInspection */
4573 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4574 7
            $this->array = $arrayRandValue;
4575
4576 7
            return $this;
4577
        }
4578
4579
        /** @noinspection NonSecureShuffleUsageInspection */
4580 11
        \shuffle($this->array);
4581
4582 11
        return $this->firstsMutable($number);
4583
    }
4584
4585
    /**
4586
     * Pick a random value from the values of this array.
4587
     *
4588
     * @return mixed
4589
     *               <p>Get a random value or null if there wasn't a value.</p>
4590
     */
4591 4
    public function randomValue()
4592
    {
4593 4
        $result = $this->randomImmutable();
4594
4595 4
        if (!isset($result[0])) {
4596
            $result[0] = null;
4597
        }
4598
4599 4
        return $result[0];
4600
    }
4601
4602
    /**
4603
     * Pick a given number of random values out of this array.
4604
     *
4605
     * @param int $number
4606
     *
4607
     * @return static
4608
     *                <p>(Mutable)</p>
4609
     *
4610
     * @psalm-return static<TKey,T>
4611
     */
4612 7
    public function randomValues(int $number): self
4613
    {
4614 7
        return $this->randomMutable($number);
4615
    }
4616
4617
    /**
4618
     * Get a random value from an array, with the ability to skew the results.
4619
     *
4620
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4621
     *
4622
     * @param array    $array
4623
     * @param int|null $number <p>How many values you will take?</p>
4624
     *
4625
     * @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...
4626
     *                           <p>(Immutable)</p>
4627
     *
4628
     * @psalm-param  array<mixed,mixed> $array
4629
     * @psalm-return static<int|array-key,T>
4630
     */
4631 9
    public function randomWeighted(array $array, int $number = null): self
4632
    {
4633
        // init
4634 9
        $options = [];
4635
4636 9
        foreach ($array as $option => $weight) {
4637 9
            if ($this->searchIndex($option) !== false) {
4638 2
                for ($i = 0; $i < $weight; ++$i) {
4639 1
                    $options[] = $option;
4640
                }
4641
            }
4642
        }
4643
4644 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4645
    }
4646
4647
    /**
4648
     * Reduce the current array via callable e.g. anonymous-function.
4649
     *
4650
     * @param callable $callable
4651
     * @param mixed    $init
4652
     *
4653
     * @return static
4654
     *                <p>(Immutable)</p>
4655
     *
4656
     * @psalm-return static<TKey,T>
4657
     * @psalm-mutation-free
4658
     */
4659 18
    public function reduce($callable, $init = []): self
4660
    {
4661 18
        if ($this->generator) {
4662 1
            $result = $init;
4663
4664 1
            foreach ($this->getGenerator() as $value) {
4665 1
                $result = $callable($result, $value);
4666
            }
4667
4668 1
            return static::create(
4669 1
                $result,
4670 1
                $this->iteratorClass,
4671 1
                false
4672
            );
4673
        }
4674
4675 18
        $result = \array_reduce($this->array, $callable, $init);
4676
4677 18
        if ($result === null) {
4678
            $this->array = [];
4679
        } else {
4680 18
            $this->array = (array) $result;
4681
        }
4682
4683 18
        return static::create(
4684 18
            $this->array,
4685 18
            $this->iteratorClass,
4686 18
            false
4687
        );
4688
    }
4689
4690
    /**
4691
     * @param bool $unique
4692
     *
4693
     * @return static
4694
     *                <p>(Immutable)</p>
4695
     *
4696
     * @psalm-return static<TKey,T>
4697
     * @psalm-mutation-free
4698
     */
4699 14
    public function reduce_dimension(bool $unique = true): self
4700
    {
4701
        // init
4702 14
        $result = [];
4703
4704 14
        foreach ($this->getGenerator() as $val) {
4705 12
            if (\is_array($val) === true) {
4706 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4707
            } else {
4708 12
                $result[] = [$val];
4709
            }
4710
        }
4711
4712 14
        $result = $result === [] ? [] : \array_merge(...$result);
4713
4714 14
        $resultArrayy = new static($result);
4715
4716
        /**
4717
         * @psalm-suppress ImpureMethodCall - object is already re-created
4718
         * @psalm-suppress InvalidReturnStatement - why?
4719
         */
4720 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4721
    }
4722
4723
    /**
4724
     * Create a numerically re-indexed Arrayy object.
4725
     *
4726
     * @return $this
4727
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4728
     *
4729
     * @psalm-return static<TKey,T>
4730
     */
4731 9
    public function reindex(): self
4732
    {
4733 9
        $this->generatorToArray(false);
4734
4735 9
        $this->array = \array_values($this->array);
4736
4737 9
        return $this;
4738
    }
4739
4740
    /**
4741
     * Return all items that fail the truth test.
4742
     *
4743
     * @param \Closure $closure
4744
     *
4745
     * @return static
4746
     *                <p>(Immutable)</p>
4747
     *
4748
     * @psalm-return static<TKey,T>
4749
     * @psalm-mutation-free
4750
     */
4751 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...
4752
    {
4753
        // init
4754 1
        $filtered = [];
4755
4756 1
        foreach ($this->getGenerator() as $key => $value) {
4757 1
            if (!$closure($value, $key)) {
4758 1
                $filtered[$key] = $value;
4759
            }
4760
        }
4761
4762 1
        return static::create(
4763 1
            $filtered,
4764 1
            $this->iteratorClass,
4765 1
            false
4766
        );
4767
    }
4768
4769
    /**
4770
     * Remove a value from the current array (optional using dot-notation).
4771
     *
4772
     * @param mixed $key
4773
     *
4774
     * @return static
4775
     *                <p>(Mutable)</p>
4776
     *
4777
     * @psalm-param  TKey $key
4778
     * @psalm-return static<TKey,T>
4779
     */
4780 21
    public function remove($key)
4781
    {
4782
        // recursive call
4783 21
        if (\is_array($key) === true) {
4784
            foreach ($key as $k) {
4785
                $this->internalRemove($k);
4786
            }
4787
4788
            return static::create(
4789
                $this->toArray(),
4790
                $this->iteratorClass,
4791
                false
4792
            );
4793
        }
4794
4795 21
        $this->internalRemove($key);
4796
4797 21
        return static::create(
4798 21
            $this->toArray(),
4799 21
            $this->iteratorClass,
4800 21
            false
4801
        );
4802
    }
4803
4804
    /**
4805
     * alias: for "Arrayy->removeValue()"
4806
     *
4807
     * @param mixed $element
4808
     *
4809
     * @return static
4810
     *                <p>(Immutable)</p>
4811
     *
4812
     * @psalm-param  T $element
4813
     * @psalm-return static<TKey,T>
4814
     * @psalm-mutation-free
4815
     */
4816 8
    public function removeElement($element)
4817
    {
4818 8
        return $this->removeValue($element);
4819
    }
4820
4821
    /**
4822
     * Remove the first value from the current array.
4823
     *
4824
     * @return static
4825
     *                <p>(Immutable)</p>
4826
     *
4827
     * @psalm-return static<TKey,T>
4828
     * @psalm-mutation-free
4829
     */
4830 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...
4831
    {
4832 7
        $tmpArray = $this->toArray();
4833
4834 7
        \array_shift($tmpArray);
4835
4836 7
        return static::create(
4837 7
            $tmpArray,
4838 7
            $this->iteratorClass,
4839 7
            false
4840
        );
4841
    }
4842
4843
    /**
4844
     * Remove the last value from the current array.
4845
     *
4846
     * @return static
4847
     *                <p>(Immutable)</p>
4848
     *
4849
     * @psalm-return static<TKey,T>
4850
     * @psalm-mutation-free
4851
     */
4852 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...
4853
    {
4854 7
        $tmpArray = $this->toArray();
4855
4856 7
        \array_pop($tmpArray);
4857
4858 7
        return static::create(
4859 7
            $tmpArray,
4860 7
            $this->iteratorClass,
4861 7
            false
4862
        );
4863
    }
4864
4865
    /**
4866
     * Removes a particular value from an array (numeric or associative).
4867
     *
4868
     * @param mixed $value
4869
     *
4870
     * @return static
4871
     *                <p>(Immutable)</p>
4872
     *
4873
     * @psalm-param  T $value
4874
     * @psalm-return static<TKey,T>
4875
     * @psalm-mutation-free
4876
     */
4877 8
    public function removeValue($value): self
4878
    {
4879 8
        $this->generatorToArray();
4880
4881
        // init
4882 8
        $isSequentialArray = $this->isSequential();
4883
4884 8
        foreach ($this->array as $key => $item) {
4885 7
            if ($item === $value) {
4886 7
                unset($this->array[$key]);
4887
            }
4888
        }
4889
4890 8
        if ($isSequentialArray) {
4891 6
            $this->array = \array_values($this->array);
4892
        }
4893
4894 8
        return static::create(
4895 8
            $this->array,
4896 8
            $this->iteratorClass,
4897 8
            false
4898
        );
4899
    }
4900
4901
    /**
4902
     * Generate array of repeated arrays.
4903
     *
4904
     * @param int $times <p>How many times has to be repeated.</p>
4905
     *
4906
     * @return static
4907
     *                <p>(Immutable)</p>
4908
     *
4909
     * @psalm-return static<TKey,T>
4910
     * @psalm-mutation-free
4911
     */
4912 1
    public function repeat($times): self
4913
    {
4914 1
        if ($times === 0) {
4915 1
            return static::create([], $this->iteratorClass);
4916
        }
4917
4918 1
        return static::create(
4919 1
            \array_fill(0, (int) $times, $this->toArray()),
4920 1
            $this->iteratorClass,
4921 1
            false
4922
        );
4923
    }
4924
4925
    /**
4926
     * Replace a key with a new key/value pair.
4927
     *
4928
     * @param mixed $oldKey
4929
     * @param mixed $newKey
4930
     * @param mixed $newValue
4931
     *
4932
     * @return static
4933
     *                <p>(Immutable)</p>
4934
     *
4935
     * @psalm-return static<TKey,T>
4936
     * @psalm-mutation-free
4937
     */
4938 5
    public function replace($oldKey, $newKey, $newValue): self
4939
    {
4940 5
        $that = clone $this;
4941
4942
        /**
4943
         * @psalm-suppress ImpureMethodCall - object is already cloned
4944
         */
4945 5
        return $that->remove($oldKey)
4946 5
            ->set($newKey, $newValue);
4947
    }
4948
4949
    /**
4950
     * Create an array using the current array as values and the other array as keys.
4951
     *
4952
     * @param array $keys <p>An array of keys.</p>
4953
     *
4954
     * @return static
4955
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4956
     *
4957
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4958
     * @psalm-return static<TKey,T>
4959
     * @psalm-mutation-free
4960
     */
4961 2
    public function replaceAllKeys(array $keys): self
4962
    {
4963 2
        return static::create(
4964 2
            \array_combine($keys, $this->toArray()),
4965 2
            $this->iteratorClass,
4966 2
            false
4967
        );
4968
    }
4969
4970
    /**
4971
     * Create an array using the current array as keys and the other array as values.
4972
     *
4973
     * @param array $array <p>An array o values.</p>
4974
     *
4975
     * @return static
4976
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4977
     *
4978
     * @psalm-param  array<mixed,T> $array
4979
     * @psalm-return static<TKey,T>
4980
     * @psalm-mutation-free
4981
     */
4982 2
    public function replaceAllValues(array $array): self
4983
    {
4984 2
        return static::create(
4985 2
            \array_combine($this->array, $array),
4986 2
            $this->iteratorClass,
4987 2
            false
4988
        );
4989
    }
4990
4991
    /**
4992
     * Replace the keys in an array with another set.
4993
     *
4994
     * @param array $keys <p>An array of keys matching the array's size</p>
4995
     *
4996
     * @return static
4997
     *                <p>(Immutable)</p>
4998
     *
4999
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5000
     * @psalm-return static<TKey,T>
5001
     * @psalm-mutation-free
5002
     */
5003 1
    public function replaceKeys(array $keys): self
5004
    {
5005 1
        $values = \array_values($this->toArray());
5006 1
        $result = \array_combine($keys, $values);
5007
5008 1
        return static::create(
5009 1
            $result,
5010 1
            $this->iteratorClass,
5011 1
            false
5012
        );
5013
    }
5014
5015
    /**
5016
     * Replace the first matched value in an array.
5017
     *
5018
     * @param mixed $search      <p>The value to replace.</p>
5019
     * @param mixed $replacement <p>The value to replace.</p>
5020
     *
5021
     * @return static
5022
     *                <p>(Immutable)</p>
5023
     *
5024
     * @psalm-return static<TKey,T>
5025
     * @psalm-mutation-free
5026
     */
5027 3 View Code Duplication
    public function replaceOneValue($search, $replacement = ''): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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