Completed
Push — master ( 2444e2...58ff94 )
by Lars
01:53 queued 13s
created

Arrayy::findBy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 3
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/** @noinspection ReturnTypeCanBeDeclaredInspection */
4
/** @noinspection ClassReImplementsParentInterfaceInspection */
5
6
declare(strict_types=1);
7
8
namespace Arrayy;
9
10
use Arrayy\Type\TypeInterface;
11
use Arrayy\TypeCheck\TypeCheckArray;
12
use Arrayy\TypeCheck\TypeCheckInterface;
13
use Arrayy\TypeCheck\TypeCheckPhpDoc;
14
15
/**
16
 * Methods to manage arrays.
17
 *
18
 * For the full copyright and license information, please view the LICENSE
19
 * file that was distributed with this source code.
20
 *
21
 * @template TKey of array-key
22
 * @template T
23
 * @template-extends \ArrayObject<TKey,T>
24
 * @template-implements \IteratorAggregate<TKey,T>
25
 * @template-implements \ArrayAccess<TKey|null,T>
26
 */
27
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
28
{
29
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
30
31
    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 717
    public static function create(
1517
        $data = [],
1518
        string $iteratorClass = ArrayyIterator::class,
1519
        bool $checkPropertiesInConstructor = true
1520
    ) {
1521 717
        return new static(
1522 717
            $data,
1523 717
            $iteratorClass,
1524 717
            $checkPropertiesInConstructor
1525
        );
1526
    }
1527
1528
    /**
1529
     * Flatten an array with the given character as a key delimiter
1530
     *
1531
     * @param string     $delimiter
1532
     * @param string     $prepend
1533
     * @param array|null $items
1534
     *
1535
     * @return array
1536
     */
1537 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1538
    {
1539
        // init
1540 2
        $flatten = [];
1541
1542 2
        if ($items === null) {
1543 2
            $items = $this->array;
1544
        }
1545
1546 2
        foreach ($items as $key => $value) {
1547 2
            if (\is_array($value) && !empty($value)) {
1548 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1549
            } else {
1550 2
                $flatten[] = [$prepend . $key => $value];
1551
            }
1552
        }
1553
1554 2
        if (\count($flatten) === 0) {
1555
            return [];
1556
        }
1557
1558 2
        return \array_merge_recursive([], ...$flatten);
1559
    }
1560
1561
    /**
1562
     * WARNING: Creates an Arrayy object by reference.
1563
     *
1564
     * @param array $array
1565
     *
1566
     * @return $this
1567
     *               <p>(Mutable) Return this Arrayy object.</p>
1568
     *
1569
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1570
     */
1571 27
    public function createByReference(array &$array = []): self
1572
    {
1573 27
        $array = $this->fallbackForArray($array);
1574
1575 27
        $this->array = &$array;
1576
1577 27
        return $this;
1578
    }
1579
1580
    /**
1581
     * Create an new instance from a callable function which will return an Generator.
1582
     *
1583
     * @param callable $generatorFunction
1584
     *
1585
     * @return static
1586
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1587
     *
1588
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1589
     *
1590
     * @psalm-mutation-free
1591
     */
1592 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1593
    {
1594 7
        return self::create($generatorFunction);
1595
    }
1596
1597
    /**
1598
     * Create an new instance filled with a copy of values from a "Generator"-object.
1599
     *
1600
     * @param \Generator $generator
1601
     *
1602
     * @return static
1603
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1604
     *
1605
     * @psalm-param \Generator<array-key,mixed> $generator
1606
     *
1607
     * @psalm-mutation-free
1608
     */
1609 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1610
    {
1611 4
        return self::create(\iterator_to_array($generator, true));
1612
    }
1613
1614
    /**
1615
     * Create an new Arrayy object via JSON.
1616
     *
1617
     * @param string $json
1618
     *
1619
     * @return static
1620
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1621
     *
1622
     * @psalm-mutation-free
1623
     */
1624 5
    public static function createFromJson(string $json): self
1625
    {
1626 5
        return static::create(\json_decode($json, true));
1627
    }
1628
1629
    /**
1630
     * Create an new Arrayy object via JSON.
1631
     *
1632
     * @param array $array
1633
     *
1634
     * @return static
1635
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1636
     *
1637
     * @psalm-mutation-free
1638
     */
1639 1
    public static function createFromArray(array $array): self
1640
    {
1641 1
        return static::create($array);
1642
    }
1643
1644
    /**
1645
     * Create an new instance filled with values from an object that is iterable.
1646
     *
1647
     * @param \Traversable $object <p>iterable object</p>
1648
     *
1649
     * @return static
1650
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1651
     *
1652
     * @psalm-param \Traversable<array-key,mixed> $object
1653
     *
1654
     * @psalm-mutation-free
1655
     */
1656 4
    public static function createFromObject(\Traversable $object): self
1657
    {
1658
        // init
1659 4
        $arrayy = new static();
1660
1661 4
        if ($object instanceof self) {
1662 4
            $objectArray = $object->getGenerator();
1663
        } else {
1664
            $objectArray = $object;
1665
        }
1666
1667 4
        foreach ($objectArray as $key => $value) {
1668
            /**
1669
             * @psalm-suppress ImpureMethodCall - object is already re-created
1670
             */
1671 3
            $arrayy->internalSet($key, $value);
1672
        }
1673
1674 4
        return $arrayy;
1675
    }
1676
1677
    /**
1678
     * Create an new instance filled with values from an object.
1679
     *
1680
     * @param object $object
1681
     *
1682
     * @return static
1683
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1684
     *
1685
     * @psalm-mutation-free
1686
     */
1687 5
    public static function createFromObjectVars($object): self
1688
    {
1689 5
        return self::create(self::objectToArray($object));
1690
    }
1691
1692
    /**
1693
     * Create an new Arrayy object via string.
1694
     *
1695
     * @param string      $str       <p>The input string.</p>
1696
     * @param string|null $delimiter <p>The boundary string.</p>
1697
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1698
     *                               used.</p>
1699
     *
1700
     * @return static
1701
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1702
     *
1703
     * @psalm-mutation-free
1704
     */
1705 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1706
    {
1707 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1708 1
            \preg_match_all($regEx, $str, $array);
1709
1710 1
            if (!empty($array)) {
1711 1
                $array = $array[0];
1712
            }
1713
        } else {
1714
            /** @noinspection NestedPositiveIfStatementsInspection */
1715 9
            if ($delimiter !== null) {
1716 7
                $array = \explode($delimiter, $str);
1717
            } else {
1718 2
                $array = [$str];
1719
            }
1720
        }
1721
1722
        // trim all string in the array
1723
        /**
1724
         * @psalm-suppress MissingClosureParamType
1725
         */
1726 10
        \array_walk(
1727 10
            $array,
1728
            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<array-key,TKey>
2486
     * @psalm-mutation-free
2487
     */
2488 1
    public function flip(): self
2489
    {
2490
        $generator = function (): \Generator {
2491 1
            foreach ($this->getGenerator() as $key => $value) {
2492 1
                yield (string)$value => $key;
2493
            }
2494 1
        };
2495
2496 1
        return static::create(
2497 1
            $generator,
2498 1
            $this->iteratorClass,
2499 1
            false
2500
        );
2501
    }
2502
2503
    /**
2504
     * Get a value from an array (optional using dot-notation).
2505
     *
2506
     * @param mixed $key            <p>The key to look for.</p>
2507
     * @param mixed $fallback       <p>Value to fallback to.</p>
2508
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2509
     *                              class.</p>
2510
     * @param bool  $useByReference
2511
     *
2512
     * @return mixed|static
2513
     *
2514
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2515
     * @psalm-mutation-free
2516
     */
2517 242
    public function get(
2518
        $key,
2519
        $fallback = null,
2520
        array $array = null,
2521
        bool $useByReference = false
2522
    ) {
2523 242
        if ($array !== null) {
2524 4
            if ($useByReference) {
2525
                $usedArray = &$array;
2526
            } else {
2527 4
                $usedArray = $array;
2528
            }
2529
        } else {
2530 239
            $this->generatorToArray();
2531
2532 239
            if ($useByReference) {
2533 126
                $usedArray = &$this->array;
2534
            } else {
2535 131
                $usedArray = $this->array;
2536
            }
2537
        }
2538
2539 242
        if ($key === null) {
2540 1
            return static::create(
2541 1
                [],
2542 1
                $this->iteratorClass,
2543 1
                false
2544 1
            )->createByReference($usedArray);
2545
        }
2546
2547
        // php cast "bool"-index into "int"-index
2548 242
        if ((bool) $key === $key) {
2549 3
            $key = (int) $key;
2550
        }
2551
2552 242
        if (\array_key_exists($key, $usedArray) === true) {
2553 204 View Code Duplication
            if (\is_array($usedArray[$key]) === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2554 19
                return static::create(
2555 19
                    [],
2556 19
                    $this->iteratorClass,
2557 19
                    false
2558 19
                )->createByReference($usedArray[$key]);
2559
            }
2560
2561 190
            return $usedArray[$key];
2562
        }
2563
2564
        // crawl through array, get key according to object or not
2565 61
        $usePath = false;
2566
        if (
2567 61
            $this->pathSeparator
2568
            &&
2569 61
            (string) $key === $key
2570
            &&
2571 61
            \strpos($key, $this->pathSeparator) !== false
2572
        ) {
2573 31
            $segments = \explode($this->pathSeparator, (string) $key);
2574 31
            if ($segments !== false) {
2575 31
                $usePath = true;
2576 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2577
2578 31
                foreach ($segments as $segment) {
2579
                    if (
2580
                        (
2581 31
                            \is_array($usedArrayTmp) === true
2582
                            ||
2583 31
                            $usedArrayTmp instanceof \ArrayAccess
2584
                        )
2585
                        &&
2586 31
                        isset($usedArrayTmp[$segment])
2587
                    ) {
2588 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2589
2590 30
                        continue;
2591
                    }
2592
2593
                    if (
2594 14
                        \is_object($usedArrayTmp) === true
2595
                        &&
2596 14
                        \property_exists($usedArrayTmp, $segment)
2597
                    ) {
2598 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2599
2600 1
                        continue;
2601
                    }
2602
2603 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2604 1
                        $segmentsTmp = $segments;
2605 1
                        unset($segmentsTmp[0]);
2606 1
                        $keyTmp = \implode('.', $segmentsTmp);
2607 1
                        $returnTmp = static::create(
2608 1
                            [],
2609 1
                            $this->iteratorClass,
2610 1
                            false
2611
                        );
2612 1
                        foreach ($this->getAll() as $dataTmp) {
2613 1
                            if ($dataTmp instanceof self) {
2614
                                $returnTmp->add($dataTmp->get($keyTmp));
2615
2616
                                continue;
2617
                            }
2618
2619
                            if (
2620
                                (
2621 1
                                    \is_array($dataTmp) === true
2622
                                    ||
2623 1
                                    $dataTmp instanceof \ArrayAccess
2624
                                )
2625
                                &&
2626 1
                                isset($dataTmp[$keyTmp])
2627
                            ) {
2628
                                $returnTmp->add($dataTmp[$keyTmp]);
2629
2630
                                continue;
2631
                            }
2632
2633
                            if (
2634 1
                                \is_object($dataTmp) === true
2635
                                &&
2636 1
                                \property_exists($dataTmp, $keyTmp)
2637
                            ) {
2638 1
                                $returnTmp->add($dataTmp->{$keyTmp});
2639
2640
                                /** @noinspection UnnecessaryContinueInspection */
2641 1
                                continue;
2642
                            }
2643
                        }
2644
2645 1
                        if ($returnTmp->count() > 0) {
2646 1
                            return $returnTmp;
2647
                        }
2648
                    }
2649
2650 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2651
                }
2652
            }
2653
        }
2654
2655 58
        if (isset($usedArrayTmp)) {
2656 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...
2657
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2658
            }
2659
2660 28 View Code Duplication
            if (\is_array($usedArrayTmp) === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2661 6
                return static::create(
2662 6
                    [],
2663 6
                    $this->iteratorClass,
2664 6
                    false
2665 6
                )->createByReference($usedArrayTmp);
2666
            }
2667
2668 28
            return $usedArrayTmp;
2669
        }
2670
2671 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...
2672 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2673
        }
2674
2675
        return static::create(
2676
            [],
2677
            $this->iteratorClass,
2678
            false
2679
        )->createByReference($usedArray);
2680
    }
2681
2682
    /**
2683
     * alias: for "Arrayy->toArray()"
2684
     *
2685
     * @return array
2686
     *
2687
     * @see          Arrayy::getArray()
2688
     *
2689
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2690
     */
2691 15
    public function getAll(): array
2692
    {
2693 15
        return $this->toArray();
2694
    }
2695
2696
    /**
2697
     * Get the current array from the "Arrayy"-object.
2698
     *
2699
     * alias for "toArray()"
2700
     *
2701
     * @param bool $convertAllArrayyElements <p>
2702
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2703
     *                                       </p>
2704
     * @param bool $preserveKeys             <p>
2705
     *                                       e.g.: A generator maybe return the same key more then once,
2706
     *                                       so maybe you will ignore the keys.
2707
     *                                       </p>
2708
     *
2709
     * @return array
2710
     *
2711
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2712
     * @psalm-mutation-free
2713
     *
2714
     * @see Arrayy::toArray()
2715
     */
2716 500
    public function getArray(
2717
        bool $convertAllArrayyElements = false,
2718
        bool $preserveKeys = true
2719
    ): array {
2720 500
        return $this->toArray(
2721 500
            $convertAllArrayyElements,
2722 500
            $preserveKeys
2723
        );
2724
    }
2725
2726
    /**
2727
     * @param string $json
2728
     *
2729
     * @return $this
2730
     */
2731 3
    public static function createFromJsonMapper(string $json)
2732
    {
2733
        // init
2734 3
        $class = static::create();
2735
2736 3
        $jsonObject = \json_decode($json, false);
2737
2738 3
        $mapper = new \Arrayy\Mapper\Json();
2739 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...
2740
            if ($class->checkPropertiesMismatchInConstructor) {
2741
                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...
2742
            }
2743
        };
2744
2745 3
        return $mapper->map($jsonObject, $class);
2746
    }
2747
2748
    /**
2749
     * @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...
2750
     *
2751
     * @internal
2752
     */
2753 6
    public function getPhpDocPropertiesFromClass()
2754
    {
2755 6
        if ($this->properties === []) {
2756 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2757
        }
2758
2759 6
        return $this->properties;
2760
    }
2761
2762
    /**
2763
     * Get the current array from the "Arrayy"-object as list.
2764
     *
2765
     * alias for "toList()"
2766
     *
2767
     * @param bool $convertAllArrayyElements <p>
2768
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2769
     *                                       </p>
2770
     *
2771
     * @return array
2772
     *
2773
     * @psalm-return array<int,mixed>|array<int,T>
2774
     * @psalm-mutation-free
2775
     *
2776
     * @see Arrayy::toList()
2777
     */
2778 1
    public function getList(bool $convertAllArrayyElements = false): array
2779
    {
2780 1
        return $this->toList($convertAllArrayyElements);
2781
    }
2782
2783
    /**
2784
     * Returns the values from a single column of the input array, identified by
2785
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2786
     *
2787
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2788
     * array by the values from the $indexKey column in the input array.
2789
     *
2790
     * @param mixed $columnKey
2791
     * @param mixed $indexKey
2792
     *
2793
     * @return static
2794
     *                <p>(Immutable)</p>
2795
     *
2796
     * @psalm-return static<TKey,T>
2797
     * @psalm-mutation-free
2798
     */
2799 1
    public function getColumn($columnKey = null, $indexKey = null): self
2800
    {
2801 1
        return static::create(
2802 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2803 1
            $this->iteratorClass,
2804 1
            false
2805
        );
2806
    }
2807
2808
    /**
2809
     * Get the current array from the "Arrayy"-object as generator by reference.
2810
     *
2811
     * @return \Generator
2812
     *
2813
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2814
     */
2815 75
    public function &getGeneratorByReference(): \Generator
2816
    {
2817 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2818
            // -> false-positive -> see "&" from method
2819
            /** @noinspection YieldFromCanBeUsedInspection */
2820 17
            foreach ($this->generator as $key => $value) {
2821 17
                yield $key => $value;
2822
            }
2823
2824 5
            return;
2825
        }
2826
2827
        // -> false-positive -> see "&$value"
2828
        /** @noinspection YieldFromCanBeUsedInspection */
2829
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
2830 59
        foreach ($this->array as $key => &$value) {
2831 54
            yield $key => $value;
2832
        }
2833 35
    }
2834
2835
    /**
2836
     * Get the current array from the "Arrayy"-object as generator.
2837
     *
2838
     * @return \Generator
2839
     *
2840
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2841
     * @psalm-mutation-free
2842
     */
2843 996
    public function getGenerator(): \Generator
2844
    {
2845 996
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2846 27
            yield from $this->generator;
2847
2848 27
            return;
2849
        }
2850
2851 994
        yield from $this->array;
2852 968
    }
2853
2854
    /**
2855
     * alias: for "Arrayy->keys()"
2856
     *
2857
     * @return static
2858
     *                <p>(Immutable)</p>
2859
     *
2860
     * @see          Arrayy::keys()
2861
     *
2862
     * @psalm-return static<array-key,TKey>
2863
     * @psalm-mutation-free
2864
     */
2865 2
    public function getKeys()
2866
    {
2867 2
        return $this->keys();
2868
    }
2869
2870
    /**
2871
     * Get the current array from the "Arrayy"-object as object.
2872
     *
2873
     * @return \stdClass
2874
     */
2875 4
    public function getObject(): \stdClass
2876
    {
2877 4
        return self::arrayToObject($this->toArray());
2878
    }
2879
2880
    /**
2881
     * alias: for "Arrayy->randomImmutable()"
2882
     *
2883
     * @return static
2884
     *                <p>(Immutable)</p>
2885
     *
2886
     * @see          Arrayy::randomImmutable()
2887
     *
2888
     * @psalm-return static<int|array-key,T>
2889
     */
2890 4
    public function getRandom(): self
2891
    {
2892 4
        return $this->randomImmutable();
2893
    }
2894
2895
    /**
2896
     * alias: for "Arrayy->randomKey()"
2897
     *
2898
     * @return mixed
2899
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2900
     *
2901
     * @see Arrayy::randomKey()
2902
     */
2903 3
    public function getRandomKey()
2904
    {
2905 3
        return $this->randomKey();
2906
    }
2907
2908
    /**
2909
     * alias: for "Arrayy->randomKeys()"
2910
     *
2911
     * @param int $number
2912
     *
2913
     * @return static
2914
     *                <p>(Immutable)</p>
2915
     *
2916
     * @see          Arrayy::randomKeys()
2917
     *
2918
     * @psalm-return static<TKey,T>
2919
     */
2920 8
    public function getRandomKeys(int $number): self
2921
    {
2922 8
        return $this->randomKeys($number);
2923
    }
2924
2925
    /**
2926
     * alias: for "Arrayy->randomValue()"
2927
     *
2928
     * @return mixed
2929
     *               <p>Get a random value or null if there wasn't a value.</p>
2930
     *
2931
     * @see Arrayy::randomValue()
2932
     */
2933 3
    public function getRandomValue()
2934
    {
2935 3
        return $this->randomValue();
2936
    }
2937
2938
    /**
2939
     * alias: for "Arrayy->randomValues()"
2940
     *
2941
     * @param int $number
2942
     *
2943
     * @return static
2944
     *                <p>(Immutable)</p>
2945
     *
2946
     * @see          Arrayy::randomValues()
2947
     *
2948
     * @psalm-return static<TKey,T>
2949
     */
2950 6
    public function getRandomValues(int $number): self
2951
    {
2952 6
        return $this->randomValues($number);
2953
    }
2954
2955
    /**
2956
     * Gets all values.
2957
     *
2958
     * @return static
2959
     *                <p>The values of all elements in this array, in the order they
2960
     *                appear in the array.</p>
2961
     *
2962
     * @psalm-return static<TKey,T>
2963
     */
2964 4
    public function getValues()
2965
    {
2966 4
        $this->generatorToArray(false);
2967
2968 4
        return static::create(
2969 4
            \array_values($this->array),
2970 4
            $this->iteratorClass,
2971 4
            false
2972
        );
2973
    }
2974
2975
    /**
2976
     * Gets all values via Generator.
2977
     *
2978
     * @return \Generator
2979
     *                    <p>The values of all elements in this array, in the order they
2980
     *                    appear in the array as Generator.</p>
2981
     *
2982
     * @psalm-return \Generator<TKey,T>
2983
     */
2984 4
    public function getValuesYield(): \Generator
2985
    {
2986 4
        yield from $this->getGenerator();
2987 4
    }
2988
2989
    /**
2990
     * Group values from a array according to the results of a closure.
2991
     *
2992
     * @param callable|string $grouper  <p>A callable function name.</p>
2993
     * @param bool            $saveKeys
2994
     *
2995
     * @return static
2996
     *                <p>(Immutable)</p>
2997
     *
2998
     * @psalm-return static<TKey,T>
2999
     * @psalm-mutation-free
3000
     */
3001 4
    public function group($grouper, bool $saveKeys = false): self
3002
    {
3003
        // init
3004 4
        $result = [];
3005
3006
        // Iterate over values, group by property/results from closure.
3007 4
        foreach ($this->getGenerator() as $key => $value) {
3008 4
            if (\is_callable($grouper) === true) {
3009 3
                $groupKey = $grouper($value, $key);
3010
            } else {
3011 1
                $groupKey = $this->get($grouper);
3012
            }
3013
3014 4
            $newValue = $this->get($groupKey, null, $result);
3015
3016 4
            if ($groupKey instanceof self) {
3017
                $groupKey = $groupKey->toArray();
3018
            }
3019
3020 4
            if ($newValue instanceof self) {
3021 4
                $newValue = $newValue->toArray();
3022
            }
3023
3024
            // Add to results.
3025 4
            if ($groupKey !== null) {
3026 3
                if ($saveKeys) {
3027 2
                    $result[$groupKey] = $newValue;
3028 2
                    $result[$groupKey][$key] = $value;
3029
                } else {
3030 1
                    $result[$groupKey] = $newValue;
3031 1
                    $result[$groupKey][] = $value;
3032
                }
3033
            }
3034
        }
3035
3036 4
        return static::create(
3037 4
            $result,
3038 4
            $this->iteratorClass,
3039 4
            false
3040
        );
3041
    }
3042
3043
    /**
3044
     * Check if an array has a given key.
3045
     *
3046
     * @param mixed $key
3047
     *
3048
     * @return bool
3049
     */
3050 30
    public function has($key): bool
3051
    {
3052 30
        static $UN_FOUND = null;
3053
3054 30
        if ($UN_FOUND === null) {
3055
            // Generate unique string to use as marker.
3056 1
            $UN_FOUND = \uniqid('arrayy', true);
3057
        }
3058
3059 30
        if (\is_array($key)) {
3060 1
            if ($key === []) {
3061
                return false;
3062
            }
3063
3064 1
            foreach ($key as $keyTmp) {
3065 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3066 1
                if ($found === false) {
3067 1
                    return false;
3068
                }
3069
            }
3070
3071 1
            return true;
3072
        }
3073
3074 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3075
    }
3076
3077
    /**
3078
     * Check if an array has a given value.
3079
     *
3080
     * INFO: if you need to search recursive please use ```contains()```
3081
     *
3082
     * @param mixed $value
3083
     *
3084
     * @return bool
3085
     */
3086 1
    public function hasValue($value): bool
3087
    {
3088 1
        return $this->contains($value);
3089
    }
3090
3091
    /**
3092
     * Implodes the values of this array.
3093
     *
3094
     * @param string $glue
3095
     *
3096
     * @return string
3097
     * @psalm-mutation-free
3098
     */
3099 28
    public function implode(string $glue = ''): string
3100
    {
3101 28
        return $this->implode_recursive($glue, $this->toArray(), false);
3102
    }
3103
3104
    /**
3105
     * Implodes the keys of this array.
3106
     *
3107
     * @param string $glue
3108
     *
3109
     * @return string
3110
     * @psalm-mutation-free
3111
     */
3112 8
    public function implodeKeys(string $glue = ''): string
3113
    {
3114 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3115
    }
3116
3117
    /**
3118
     * Given a list and an iterate-function that returns
3119
     * a key for each element in the list (or a property name),
3120
     * returns an object with an index of each item.
3121
     *
3122
     * @param mixed $key
3123
     *
3124
     * @return static
3125
     *                <p>(Immutable)</p>
3126
     *
3127
     * @psalm-return static<TKey,T>
3128
     * @psalm-mutation-free
3129
     */
3130 4 View Code Duplication
    public function indexBy($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...
3131
    {
3132
        // init
3133 4
        $results = [];
3134
3135 4
        foreach ($this->getGenerator() as $a) {
3136 4
            if (\array_key_exists($key, $a) === true) {
3137 3
                $results[$a[$key]] = $a;
3138
            }
3139
        }
3140
3141 4
        return static::create(
3142 4
            $results,
3143 4
            $this->iteratorClass,
3144 4
            false
3145
        );
3146
    }
3147
3148
    /**
3149
     * alias: for "Arrayy->searchIndex()"
3150
     *
3151
     * @param mixed $value <p>The value to search for.</p>
3152
     *
3153
     * @return false|mixed
3154
     *
3155
     * @see Arrayy::searchIndex()
3156
     */
3157 4
    public function indexOf($value)
3158
    {
3159 4
        return $this->searchIndex($value);
3160
    }
3161
3162
    /**
3163
     * Get everything but the last..$to items.
3164
     *
3165
     * @param int $to
3166
     *
3167
     * @return static
3168
     *                <p>(Immutable)</p>
3169
     *
3170
     * @psalm-return static<TKey,T>
3171
     * @psalm-mutation-free
3172
     */
3173 12
    public function initial(int $to = 1): self
3174
    {
3175 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3176
    }
3177
3178
    /**
3179
     * Return an array with all elements found in input array.
3180
     *
3181
     * @param array $search
3182
     * @param bool  $keepKeys
3183
     *
3184
     * @return static
3185
     *                <p>(Immutable)</p>
3186
     *
3187
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3188
     * @psalm-return static<TKey,T>
3189
     * @psalm-mutation-free
3190
     */
3191 4
    public function intersection(array $search, bool $keepKeys = false): self
3192
    {
3193 4
        if ($keepKeys) {
3194
            /**
3195
             * @psalm-suppress MissingClosureReturnType
3196
             * @psalm-suppress MissingClosureParamType
3197
             */
3198 1
            return static::create(
3199 1
                \array_uintersect(
3200 1
                    $this->toArray(),
3201 1
                    $search,
3202
                    static function ($a, $b) {
3203 1
                        return $a === $b ? 0 : -1;
3204 1
                    }
3205
                ),
3206 1
                $this->iteratorClass,
3207 1
                false
3208
            );
3209
        }
3210
3211 3
        return static::create(
3212 3
            \array_values(\array_intersect($this->toArray(), $search)),
3213 3
            $this->iteratorClass,
3214 3
            false
3215
        );
3216
    }
3217
3218
    /**
3219
     * Return an array with all elements found in input array.
3220
     *
3221
     * @param array ...$array
3222
     *
3223
     * @return static
3224
     *                <p>(Immutable)</p>
3225
     *
3226
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3227
     * @psalm-return static<TKey,T>
3228
     * @psalm-mutation-free
3229
     */
3230 1
    public function intersectionMulti(...$array): self
3231
    {
3232 1
        return static::create(
3233 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3234 1
            $this->iteratorClass,
3235 1
            false
3236
        );
3237
    }
3238
3239
    /**
3240
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3241
     *
3242
     * @param array $search
3243
     *
3244
     * @return bool
3245
     *
3246
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3247
     */
3248 1
    public function intersects(array $search): bool
3249
    {
3250 1
        return $this->intersection($search)->count() > 0;
3251
    }
3252
3253
    /**
3254
     * Invoke a function on all of an array's values.
3255
     *
3256
     * @param callable $callable
3257
     * @param mixed    $arguments
3258
     *
3259
     * @return static
3260
     *                <p>(Immutable)</p>
3261
     *
3262
     * @psalm-param  callable(T=,mixed):mixed $callable
3263
     * @psalm-return static<TKey,T>
3264
     * @psalm-mutation-free
3265
     */
3266 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...
3267
    {
3268
        // If one argument given for each iteration, create an array for it.
3269 1
        if (\is_array($arguments) === false) {
3270 1
            $arguments = \array_fill(
3271 1
                0,
3272 1
                $this->count(),
3273 1
                $arguments
3274
            );
3275
        }
3276
3277
        // If the callable has arguments, pass them.
3278 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...
3279 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3280
        } else {
3281 1
            $array = $this->map($callable);
3282
        }
3283
3284 1
        return static::create(
3285 1
            $array,
3286 1
            $this->iteratorClass,
3287 1
            false
3288
        );
3289
    }
3290
3291
    /**
3292
     * Check whether array is associative or not.
3293
     *
3294
     * @param bool $recursive
3295
     *
3296
     * @return bool
3297
     *              <p>Returns true if associative, false otherwise.</p>
3298
     */
3299 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...
3300
    {
3301 15
        if ($this->isEmpty()) {
3302 3
            return false;
3303
        }
3304
3305
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3306 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3307 13
            if ((string) $key !== $key) {
3308 11
                return false;
3309
            }
3310
        }
3311
3312 3
        return true;
3313
    }
3314
3315
    /**
3316
     * Check if a given key or keys are empty.
3317
     *
3318
     * @param int|int[]|string|string[]|null $keys
3319
     *
3320
     * @return bool
3321
     *              <p>Returns true if empty, false otherwise.</p>
3322
     * @psalm-mutation-free
3323
     */
3324 45
    public function isEmpty($keys = null): bool
3325
    {
3326 45
        if ($this->generator) {
3327
            return $this->toArray() === [];
3328
        }
3329
3330 45
        if ($keys === null) {
3331 43
            return $this->array === [];
3332
        }
3333
3334 2
        foreach ((array) $keys as $key) {
3335 2
            if (!empty($this->get($key))) {
3336 2
                return false;
3337
            }
3338
        }
3339
3340 2
        return true;
3341
    }
3342
3343
    /**
3344
     * Check if the current array is equal to the given "$array" or not.
3345
     *
3346
     * @param array $array
3347
     *
3348
     * @return bool
3349
     *
3350
     * @psalm-param array<mixed,mixed> $array
3351
     */
3352 1
    public function isEqual(array $array): bool
3353
    {
3354 1
        return $this->toArray() === $array;
3355
    }
3356
3357
    /**
3358
     * Check if the current array is a multi-array.
3359
     *
3360
     * @return bool
3361
     */
3362 22
    public function isMultiArray(): bool
3363
    {
3364
        return !(
3365 22
            \count($this->toArray(), \COUNT_NORMAL)
3366
            ===
3367 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3368
        );
3369
    }
3370
3371
    /**
3372
     * Check whether array is numeric or not.
3373
     *
3374
     * @return bool
3375
     *              <p>Returns true if numeric, false otherwise.</p>
3376
     */
3377 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...
3378
    {
3379 5
        if ($this->isEmpty()) {
3380 2
            return false;
3381
        }
3382
3383
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3384 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3385 4
            if ((int) $key !== $key) {
3386 2
                return false;
3387
            }
3388
        }
3389
3390 2
        return true;
3391
    }
3392
3393
    /**
3394
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3395
     *
3396
     * @param bool $recursive
3397
     *
3398
     * @return bool
3399
     * @psalm-mutation-free
3400
     */
3401 9
    public function isSequential(bool $recursive = false): bool
3402
    {
3403
3404
        // recursive
3405
3406 9
        if ($recursive === true) {
3407
            return $this->array_keys_recursive($this->toArray())
3408
                   ===
3409
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3410
        }
3411
3412
        // non recursive
3413
3414 9
        return \array_keys($this->toArray())
3415
               ===
3416 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3417
    }
3418
3419
    /**
3420
     * @return array
3421
     *
3422
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3423
     */
3424 2
    public function jsonSerialize(): array
3425
    {
3426 2
        return $this->toArray();
3427
    }
3428
3429
    /**
3430
     * Gets the key/index of the element at the current internal iterator position.
3431
     *
3432
     * @return int|string|null
3433
     */
3434
    public function key()
3435
    {
3436
        return \key($this->array);
3437
    }
3438
3439
    /**
3440
     * Checks if the given key exists in the provided array.
3441
     *
3442
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3443
     *       then you need to use "Arrayy->offsetExists()".
3444
     *
3445
     * @param int|string $key the key to look for
3446
     *
3447
     * @return bool
3448
     * @psalm-mutation-free
3449
     */
3450 162
    public function keyExists($key): bool
3451
    {
3452 162
        return \array_key_exists($key, $this->array);
3453
    }
3454
3455
    /**
3456
     * Get all keys from the current array.
3457
     *
3458
     * @param bool       $recursive     [optional] <p>
3459
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3460
     *                                  </p>
3461
     * @param mixed|null $search_values [optional] <p>
3462
     *                                  If specified, then only keys containing these values are returned.
3463
     *                                  </p>
3464
     * @param bool       $strict        [optional] <p>
3465
     *                                  Determines if strict comparison (===) should be used during the search.
3466
     *                                  </p>
3467
     *
3468
     * @return static
3469
     *                <p>(Immutable) An array of all the keys in input.</p>
3470
     *
3471
     * @psalm-return static<array-key,TKey>
3472
     * @psalm-mutation-free
3473
     */
3474 29
    public function keys(
3475
        bool $recursive = false,
3476
        $search_values = null,
3477
        bool $strict = true
3478
    ): self {
3479
3480
        // recursive
3481
3482 29
        if ($recursive === true) {
3483 4
            $array = $this->array_keys_recursive(
3484 4
                null,
3485 4
                $search_values,
3486 4
                $strict
3487
            );
3488
3489 4
            return static::create(
3490 4
                $array,
3491 4
                $this->iteratorClass,
3492 4
                false
3493
            );
3494
        }
3495
3496
        // non recursive
3497
3498 28
        if ($search_values === null) {
3499
            $arrayFunction = function (): \Generator {
3500 28
                foreach ($this->getGenerator() as $key => $value) {
3501 26
                    yield $key;
3502
                }
3503 28
            };
3504
        } else {
3505
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3506 1
                $is_array_tmp = \is_array($search_values);
3507
3508
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3509 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3510 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...
3511
                        (
3512 1
                            $is_array_tmp === false
3513
                            &&
3514 1
                            $strict === true
3515
                            &&
3516 1
                            $search_values === $value
3517
                        )
3518
                        ||
3519
                        (
3520 1
                            $is_array_tmp === false
3521
                            &&
3522 1
                            $strict === false
3523
                            &&
3524 1
                            $search_values == $value
3525
                        )
3526
                        ||
3527
                        (
3528 1
                            $is_array_tmp === true
3529
                            &&
3530 1
                            \in_array($value, $search_values, $strict)
3531
                        )
3532
                    ) {
3533 1
                        yield $key;
3534
                    }
3535
                }
3536 1
            };
3537
        }
3538
3539 28
        return static::create(
3540 28
            $arrayFunction,
3541 28
            $this->iteratorClass,
3542 28
            false
3543
        );
3544
    }
3545
3546
    /**
3547
     * Sort an array by key in reverse order.
3548
     *
3549
     * @param int $sort_flags [optional] <p>
3550
     *                        You may modify the behavior of the sort using the optional
3551
     *                        parameter sort_flags, for details
3552
     *                        see sort.
3553
     *                        </p>
3554
     *
3555
     * @return $this
3556
     *               <p>(Mutable) Return this Arrayy object.</p>
3557
     *
3558
     * @psalm-return static<TKey,T>
3559
     */
3560 4
    public function krsort(int $sort_flags = 0): self
3561
    {
3562 4
        $this->generatorToArray();
3563
3564 4
        \krsort($this->array, $sort_flags);
3565
3566 4
        return $this;
3567
    }
3568
3569
    /**
3570
     * Sort an array by key in reverse order.
3571
     *
3572
     * @param int $sort_flags [optional] <p>
3573
     *                        You may modify the behavior of the sort using the optional
3574
     *                        parameter sort_flags, for details
3575
     *                        see sort.
3576
     *                        </p>
3577
     *
3578
     * @return $this
3579
     *               <p>(Immutable) Return this Arrayy object.</p>
3580
     *
3581
     * @psalm-return static<TKey,T>
3582
     * @psalm-mutation-free
3583
     */
3584 4
    public function krsortImmutable(int $sort_flags = 0): self
3585
    {
3586 4
        $that = clone $this;
3587
3588
        /**
3589
         * @psalm-suppress ImpureMethodCall - object is already cloned
3590
         */
3591 4
        $that->krsort($sort_flags);
3592
3593 4
        return $that;
3594
    }
3595
3596
    /**
3597
     * Get the last value from the current array.
3598
     *
3599
     * @return mixed|null
3600
     *                    <p>Return null if there wasn't a element.</p>
3601
     * @psalm-mutation-free
3602
     */
3603 17
    public function last()
3604
    {
3605 17
        $key_last = $this->lastKey();
3606 17
        if ($key_last === null) {
3607 2
            return null;
3608
        }
3609
3610 15
        return $this->get($key_last);
3611
    }
3612
3613
    /**
3614
     * Get the last key from the current array.
3615
     *
3616
     * @return mixed|null
3617
     *                    <p>Return null if there wasn't a element.</p>
3618
     * @psalm-mutation-free
3619
     */
3620 21
    public function lastKey()
3621
    {
3622 21
        $this->generatorToArray();
3623
3624 21
        return \array_key_last($this->array);
3625
    }
3626
3627
    /**
3628
     * Get the last value(s) from the current array.
3629
     *
3630
     * @param int|null $number
3631
     *
3632
     * @return static
3633
     *                <p>(Immutable)</p>
3634
     *
3635
     * @psalm-return static<TKey,T>
3636
     * @psalm-mutation-free
3637
     */
3638 13
    public function lastsImmutable(int $number = null): self
3639
    {
3640 13
        if ($this->isEmpty()) {
3641 1
            return static::create(
3642 1
                [],
3643 1
                $this->iteratorClass,
3644 1
                false
3645
            );
3646
        }
3647
3648 12
        if ($number === null) {
3649 8
            $poppedValue = $this->last();
3650
3651 8
            if ($poppedValue === null) {
3652 1
                $poppedValue = [$poppedValue];
3653
            } else {
3654 7
                $poppedValue = (array) $poppedValue;
3655
            }
3656
3657 8
            $arrayy = static::create(
3658 8
                $poppedValue,
3659 8
                $this->iteratorClass,
3660 8
                false
3661
            );
3662
        } else {
3663 4
            $arrayy = $this->rest(-$number);
3664
        }
3665
3666 12
        return $arrayy;
3667
    }
3668
3669
    /**
3670
     * Get the last value(s) from the current array.
3671
     *
3672
     * @param int|null $number
3673
     *
3674
     * @return $this
3675
     *               <p>(Mutable)</p>
3676
     *
3677
     * @psalm-return static<TKey,T>
3678
     */
3679 13
    public function lastsMutable(int $number = null): self
3680
    {
3681 13
        if ($this->isEmpty()) {
3682 1
            return $this;
3683
        }
3684
3685 12
        if ($number === null) {
3686 8
            $poppedValue = $this->last();
3687
3688 8
            if ($poppedValue === null) {
3689 1
                $poppedValue = [$poppedValue];
3690
            } else {
3691 7
                $poppedValue = (array) $poppedValue;
3692
            }
3693
3694 8
            $this->array = static::create(
3695 8
                $poppedValue,
3696 8
                $this->iteratorClass,
3697 8
                false
3698 8
            )->toArray();
3699
        } else {
3700 4
            $this->array = $this->rest(-$number)->toArray();
3701
        }
3702
3703 12
        $this->generator = null;
3704
3705 12
        return $this;
3706
    }
3707
3708
    /**
3709
     * Count the values from the current array.
3710
     *
3711
     * alias: for "Arrayy->count()"
3712
     *
3713
     * @param int $mode
3714
     *
3715
     * @return int
3716
     *
3717
     * @see Arrayy::count()
3718
     */
3719 20
    public function length(int $mode = \COUNT_NORMAL): int
3720
    {
3721 20
        return $this->count($mode);
3722
    }
3723
3724
    /**
3725
     * Apply the given function to the every element of the array,
3726
     * collecting the results.
3727
     *
3728
     * @param callable $callable
3729
     * @param bool     $useKeyAsSecondParameter
3730
     * @param mixed    ...$arguments
3731
     *
3732
     * @return static
3733
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3734
     *
3735
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3736
     * @psalm-return static<TKey,T>
3737
     * @psalm-mutation-free
3738
     */
3739 5
    public function map(
3740
        callable $callable,
3741
        bool $useKeyAsSecondParameter = false,
3742
        ...$arguments
3743
    ) {
3744
        /**
3745
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3746
         */
3747 5
        $useArguments = \func_num_args() > 2;
3748
3749 5
        return static::create(
3750
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3751 5
                foreach ($this->getGenerator() as $key => $value) {
3752 4
                    if ($useArguments) {
3753 3
                        if ($useKeyAsSecondParameter) {
3754
                            yield $key => $callable($value, $key, ...$arguments);
3755
                        } else {
3756 3
                            yield $key => $callable($value, ...$arguments);
3757
                        }
3758
                    } else {
3759
                        /** @noinspection NestedPositiveIfStatementsInspection */
3760 4
                        if ($useKeyAsSecondParameter) {
3761
                            yield $key => $callable($value, $key);
3762
                        } else {
3763 4
                            yield $key => $callable($value);
3764
                        }
3765
                    }
3766
                }
3767 5
            },
3768 5
            $this->iteratorClass,
3769 5
            false
3770
        );
3771
    }
3772
3773
    /**
3774
     * Check if all items in current array match a truth test.
3775
     *
3776
     * @param \Closure $closure
3777
     *
3778
     * @return bool
3779
     */
3780 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...
3781
    {
3782 15
        if ($this->count() === 0) {
3783 2
            return false;
3784
        }
3785
3786 13
        foreach ($this->getGenerator() as $key => $value) {
3787 13
            $value = $closure($value, $key);
3788
3789 13
            if ($value === false) {
3790 7
                return false;
3791
            }
3792
        }
3793
3794 7
        return true;
3795
    }
3796
3797
    /**
3798
     * Check if any item in the current array matches a truth test.
3799
     *
3800
     * @param \Closure $closure
3801
     *
3802
     * @return bool
3803
     */
3804 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...
3805
    {
3806 14
        if ($this->count() === 0) {
3807 2
            return false;
3808
        }
3809
3810 12
        foreach ($this->getGenerator() as $key => $value) {
3811 12
            $value = $closure($value, $key);
3812
3813 12
            if ($value === true) {
3814 9
                return true;
3815
            }
3816
        }
3817
3818 4
        return false;
3819
    }
3820
3821
    /**
3822
     * Get the max value from an array.
3823
     *
3824
     * @return false|mixed
3825
     *                     <p>Will return false if there are no values.</p>
3826
     */
3827 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...
3828
    {
3829 10
        if ($this->count() === 0) {
3830 1
            return false;
3831
        }
3832
3833 9
        $max = false;
3834
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3835 9
        foreach ($this->getGeneratorByReference() as &$value) {
3836
            if (
3837 9
                $max === false
3838
                ||
3839 9
                $value > $max
3840
            ) {
3841 9
                $max = $value;
3842
            }
3843
        }
3844
3845 9
        return $max;
3846
    }
3847
3848
    /**
3849
     * Merge the new $array into the current array.
3850
     *
3851
     * - keep key,value from the current array, also if the index is in the new $array
3852
     *
3853
     * @param array $array
3854
     * @param bool  $recursive
3855
     *
3856
     * @return static
3857
     *                <p>(Immutable)</p>
3858
     *
3859
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3860
     * @psalm-return static<int|TKey,T>
3861
     * @psalm-mutation-free
3862
     */
3863 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...
3864
    {
3865 32
        if ($recursive === true) {
3866 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
3867 9
            $result = \array_replace_recursive($this->toArray(), $array);
3868
        } else {
3869 23
            $result = \array_replace($this->toArray(), $array);
3870
        }
3871
3872 32
        return static::create(
3873 32
            $result,
3874 32
            $this->iteratorClass,
3875 32
            false
3876
        );
3877
    }
3878
3879
    /**
3880
     * Merge the new $array into the current array.
3881
     *
3882
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3883
     * - create new indexes
3884
     *
3885
     * @param array $array
3886
     * @param bool  $recursive
3887
     *
3888
     * @return static
3889
     *                <p>(Immutable)</p>
3890
     *
3891
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3892
     * @psalm-return static<TKey,T>
3893
     * @psalm-mutation-free
3894
     */
3895 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...
3896
    {
3897 19
        if ($recursive === true) {
3898 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
3899 5
            $result = \array_merge_recursive($this->toArray(), $array);
3900
        } else {
3901 14
            $result = \array_merge($this->toArray(), $array);
3902
        }
3903
3904 19
        return static::create(
3905 19
            $result,
3906 19
            $this->iteratorClass,
3907 19
            false
3908
        );
3909
    }
3910
3911
    /**
3912
     * Merge the the current array into the $array.
3913
     *
3914
     * - use key,value from the new $array, also if the index is in the current array
3915
     *
3916
     * @param array $array
3917
     * @param bool  $recursive
3918
     *
3919
     * @return static
3920
     *                <p>(Immutable)</p>
3921
     *
3922
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3923
     * @psalm-return static<TKey,T>
3924
     * @psalm-mutation-free
3925
     */
3926 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...
3927
    {
3928 16
        if ($recursive === true) {
3929 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
3930 4
            $result = \array_replace_recursive($array, $this->toArray());
3931
        } else {
3932 12
            $result = \array_replace($array, $this->toArray());
3933
        }
3934
3935 16
        return static::create(
3936 16
            $result,
3937 16
            $this->iteratorClass,
3938 16
            false
3939
        );
3940
    }
3941
3942
    /**
3943
     * Merge the current array into the new $array.
3944
     *
3945
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3946
     * - create new indexes
3947
     *
3948
     * @param array $array
3949
     * @param bool  $recursive
3950
     *
3951
     * @return static
3952
     *                <p>(Immutable)</p>
3953
     *
3954
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3955
     * @psalm-return static<TKey,T>
3956
     * @psalm-mutation-free
3957
     */
3958 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...
3959
    {
3960 20
        if ($recursive === true) {
3961 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
3962 7
            $result = \array_merge_recursive($array, $this->toArray());
3963
        } else {
3964 13
            $result = \array_merge($array, $this->toArray());
3965
        }
3966
3967 20
        return static::create(
3968 20
            $result,
3969 20
            $this->iteratorClass,
3970 20
            false
3971
        );
3972
    }
3973
3974
    /**
3975
     * @return ArrayyMeta|static
3976
     */
3977 16
    public static function meta()
3978
    {
3979 16
        return (new ArrayyMeta())->getMetaObject(static::class);
3980
    }
3981
3982
    /**
3983
     * Get the min value from an array.
3984
     *
3985
     * @return false|mixed
3986
     *                     <p>Will return false if there are no values.</p>
3987
     */
3988 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...
3989
    {
3990 10
        if ($this->count() === 0) {
3991 1
            return false;
3992
        }
3993
3994 9
        $min = false;
3995
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3996 9
        foreach ($this->getGeneratorByReference() as &$value) {
3997
            if (
3998 9
                $min === false
3999
                ||
4000 9
                $value < $min
4001
            ) {
4002 9
                $min = $value;
4003
            }
4004
        }
4005
4006 9
        return $min;
4007
    }
4008
4009
    /**
4010
     * Get the most used value from the array.
4011
     *
4012
     * @return mixed|null
4013
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4014
     * @psalm-mutation-free
4015
     */
4016 3
    public function mostUsedValue()
4017
    {
4018 3
        return $this->countValues()->arsortImmutable()->firstKey();
4019
    }
4020
4021
    /**
4022
     * Get the most used value from the array.
4023
     *
4024
     * @param int|null $number <p>How many values you will take?</p>
4025
     *
4026
     * @return static
4027
     *                <p>(Immutable)</p>
4028
     *
4029
     * @psalm-return static<TKey,T>
4030
     * @psalm-mutation-free
4031
     */
4032 3
    public function mostUsedValues(int $number = null): self
4033
    {
4034 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4035
    }
4036
4037
    /**
4038
     * Move an array element to a new index.
4039
     *
4040
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
4041
     *
4042
     * @param int|string $from
4043
     * @param int        $to
4044
     *
4045
     * @return static
4046
     *                <p>(Immutable)</p>
4047
     *
4048
     * @psalm-return static<TKey,T>
4049
     * @psalm-mutation-free
4050
     */
4051 1
    public function moveElement($from, $to): self
4052
    {
4053 1
        $array = $this->toArray();
4054
4055 1
        if ((int) $from === $from) {
4056 1
            $tmp = \array_splice($array, $from, 1);
4057 1
            \array_splice($array, (int) $to, 0, $tmp);
4058 1
            $output = $array;
4059 1
        } elseif ((string) $from === $from) {
4060 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4061 1
            $itemToMove = $array[$from];
4062 1
            if ($indexToMove !== false) {
4063 1
                \array_splice($array, $indexToMove, 1);
4064
            }
4065 1
            $i = 0;
4066 1
            $output = [];
4067 1
            foreach ($array as $key => $item) {
4068 1
                if ($i === $to) {
4069 1
                    $output[$from] = $itemToMove;
4070
                }
4071 1
                $output[$key] = $item;
4072 1
                ++$i;
4073
            }
4074
        } else {
4075
            $output = [];
4076
        }
4077
4078 1
        return static::create(
4079 1
            $output,
4080 1
            $this->iteratorClass,
4081 1
            false
4082
        );
4083
    }
4084
4085
    /**
4086
     * Move an array element to the first place.
4087
     *
4088
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4089
     *       loss the keys of an indexed array.
4090
     *
4091
     * @param int|string $key
4092
     *
4093
     * @return static
4094
     *                <p>(Immutable)</p>
4095
     *
4096
     * @psalm-return static<TKey,T>
4097
     * @psalm-mutation-free
4098
     */
4099 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...
4100
    {
4101 1
        $array = $this->toArray();
4102
4103 1
        if ($this->offsetExists($key)) {
4104 1
            $tmpValue = $this->get($key);
4105 1
            unset($array[$key]);
4106 1
            $array = [$key => $tmpValue] + $array;
4107
        }
4108
4109 1
        return static::create(
4110 1
            $array,
4111 1
            $this->iteratorClass,
4112 1
            false
4113
        );
4114
    }
4115
4116
    /**
4117
     * Move an array element to the last place.
4118
     *
4119
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4120
     *       loss the keys of an indexed array.
4121
     *
4122
     * @param int|string $key
4123
     *
4124
     * @return static
4125
     *                <p>(Immutable)</p>
4126
     *
4127
     * @psalm-return static<TKey,T>
4128
     * @psalm-mutation-free
4129
     */
4130 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...
4131
    {
4132 1
        $array = $this->toArray();
4133
4134 1
        if ($this->offsetExists($key)) {
4135 1
            $tmpValue = $this->get($key);
4136 1
            unset($array[$key]);
4137 1
            $array += [$key => $tmpValue];
4138
        }
4139
4140 1
        return static::create(
4141 1
            $array,
4142 1
            $this->iteratorClass,
4143 1
            false
4144
        );
4145
    }
4146
4147
    /**
4148
     * Moves the internal iterator position to the next element and returns this element.
4149
     *
4150
     * @return false|mixed
4151
     *                     <p>(Mutable) Will return false if there are no values.</p>
4152
     */
4153
    public function next()
4154
    {
4155
        return \next($this->array);
4156
    }
4157
4158
    /**
4159
     * Get the next nth keys and values from the array.
4160
     *
4161
     * @param int $step
4162
     * @param int $offset
4163
     *
4164
     * @return static
4165
     *                <p>(Immutable)</p>
4166
     *
4167
     * @psalm-return static<TKey,T>
4168
     * @psalm-mutation-free
4169
     */
4170 1
    public function nth(int $step, int $offset = 0): self
4171
    {
4172
        $arrayFunction = function () use ($step, $offset): \Generator {
4173 1
            $position = 0;
4174 1
            foreach ($this->getGenerator() as $key => $value) {
4175 1
                if ($position++ % $step !== $offset) {
4176 1
                    continue;
4177
                }
4178
4179 1
                yield $key => $value;
4180
            }
4181 1
        };
4182
4183 1
        return static::create(
4184 1
            $arrayFunction,
4185 1
            $this->iteratorClass,
4186 1
            false
4187
        );
4188
    }
4189
4190
    /**
4191
     * Get a subset of the items from the given array.
4192
     *
4193
     * @param string[]|int[] $keys
4194
     *
4195
     * @return static
4196
     *                <p>(Immutable)</p>
4197
     *
4198
     * @psalm-param array-key[] $keys
4199
     * @psalm-return static<TKey,T>
4200
     * @psalm-mutation-free
4201
     */
4202 1 View Code Duplication
    public function only(array $keys): 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...
4203
    {
4204 1
        $keys = \array_flip($keys);
4205
4206
        $generator = function () use ($keys): \Generator {
4207 1
            foreach ($this->getGenerator() as $key => $value) {
4208 1
                if (isset($keys[$key])) {
4209 1
                    yield $key => $value;
4210
                }
4211
            }
4212 1
        };
4213
4214 1
        return static::create(
4215 1
            $generator,
4216 1
            $this->iteratorClass,
4217 1
            false
4218
        );
4219
    }
4220
4221
    /**
4222
     * Pad array to the specified size with a given value.
4223
     *
4224
     * @param int   $size  <p>Size of the result array.</p>
4225
     * @param mixed $value <p>Empty value by default.</p>
4226
     *
4227
     * @return static
4228
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4229
     *
4230
     * @psalm-return static<TKey,T>
4231
     * @psalm-mutation-free
4232
     */
4233 5
    public function pad(int $size, $value): self
4234
    {
4235 5
        return static::create(
4236 5
            \array_pad($this->toArray(), $size, $value),
4237 5
            $this->iteratorClass,
4238 5
            false
4239
        );
4240
    }
4241
4242
    /**
4243
     * Partitions this array in two array according to a predicate.
4244
     * Keys are preserved in the resulting array.
4245
     *
4246
     * @param \Closure $closure
4247
     *                          <p>The predicate on which to partition.</p>
4248
     *
4249
     * @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...
4250
     *                    <p>An array with two elements. The first element contains the array
4251
     *                    of elements where the predicate returned TRUE, the second element
4252
     *                    contains the array of elements where the predicate returned FALSE.</p>
4253
     *
4254
     * @psalm-return array<int, static<TKey,T>>
4255
     */
4256 1
    public function partition(\Closure $closure): array
4257
    {
4258
        // init
4259 1
        $matches = [];
4260 1
        $noMatches = [];
4261
4262 1
        foreach ($this->array as $key => $value) {
4263 1
            if ($closure($value, $key)) {
4264 1
                $matches[$key] = $value;
4265
            } else {
4266 1
                $noMatches[$key] = $value;
4267
            }
4268
        }
4269
4270 1
        return [self::create($matches), self::create($noMatches)];
4271
    }
4272
4273
    /**
4274
     * Pop a specified value off the end of the current array.
4275
     *
4276
     * @return mixed|null
4277
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4278
     */
4279 5
    public function pop()
4280
    {
4281 5
        $this->generatorToArray();
4282
4283 5
        return \array_pop($this->array);
4284
    }
4285
4286
    /**
4287
     * Prepend a (key) + value to the current array.
4288
     *
4289
     * EXAMPLE: <code>
4290
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4291
     * </code>
4292
     *
4293
     * @param mixed $value
4294
     * @param mixed $key
4295
     *
4296
     * @return $this
4297
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4298
     *
4299
     * @psalm-return static<TKey,T>
4300
     */
4301 11
    public function prepend($value, $key = null)
4302
    {
4303 11
        $this->generatorToArray();
4304
4305 11
        if ($this->properties !== []) {
4306 3
            $this->checkType($key, $value);
4307
        }
4308
4309 9
        if ($key === null) {
4310 8
            \array_unshift($this->array, $value);
4311
        } else {
4312 2
            $this->array = [$key => $value] + $this->array;
4313
        }
4314
4315 9
        return $this;
4316
    }
4317
4318
    /**
4319
     * Add a suffix to each key.
4320
     *
4321
     * @param mixed $suffix
4322
     *
4323
     * @return static
4324
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4325
     *
4326
     * @psalm-return static<TKey,T>
4327
     * @psalm-mutation-free
4328
     */
4329 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...
4330
    {
4331
        // init
4332 10
        $result = [];
4333
4334 10
        foreach ($this->getGenerator() as $key => $item) {
4335 9
            if ($item instanceof self) {
4336
                $result[$key] = $item->prependToEachKey($suffix);
4337 9
            } elseif (\is_array($item) === true) {
4338
                $result[$key] = self::create(
4339
                    $item,
4340
                    $this->iteratorClass,
4341
                    false
4342
                )->prependToEachKey($suffix)
4343
                    ->toArray();
4344
            } else {
4345 9
                $result[$key . $suffix] = $item;
4346
            }
4347
        }
4348
4349 10
        return self::create(
4350 10
            $result,
4351 10
            $this->iteratorClass,
4352 10
            false
4353
        );
4354
    }
4355
4356
    /**
4357
     * Add a suffix to each value.
4358
     *
4359
     * @param mixed $suffix
4360
     *
4361
     * @return static
4362
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4363
     *
4364
     * @psalm-return static<TKey,T>
4365
     * @psalm-mutation-free
4366
     */
4367 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...
4368
    {
4369
        // init
4370 10
        $result = [];
4371
4372 10
        foreach ($this->getGenerator() as $key => $item) {
4373 9
            if ($item instanceof self) {
4374
                $result[$key] = $item->prependToEachValue($suffix);
4375 9
            } elseif (\is_array($item) === true) {
4376
                $result[$key] = self::create(
4377
                    $item,
4378
                    $this->iteratorClass,
4379
                    false
4380
                )->prependToEachValue($suffix)
4381
                    ->toArray();
4382 9
            } elseif (\is_object($item) === true) {
4383 1
                $result[$key] = $item;
4384
            } else {
4385 8
                $result[$key] = $item . $suffix;
4386
            }
4387
        }
4388
4389 10
        return self::create(
4390 10
            $result,
4391 10
            $this->iteratorClass,
4392 10
            false
4393
        );
4394
    }
4395
4396
    /**
4397
     * Return the value of a given key and
4398
     * delete the key.
4399
     *
4400
     * @param int|int[]|string|string[]|null $keyOrKeys
4401
     * @param mixed                          $fallback
4402
     *
4403
     * @return mixed
4404
     */
4405 5
    public function pull($keyOrKeys = null, $fallback = null)
4406
    {
4407 5
        if ($keyOrKeys === null) {
4408 1
            $array = $this->toArray();
4409 1
            $this->clear();
4410
4411 1
            return $array;
4412
        }
4413
4414 4
        if (\is_array($keyOrKeys) === true) {
4415 1
            $valueOrValues = [];
4416 1
            foreach ($keyOrKeys as $key) {
4417 1
                $valueOrValues[] = $this->get($key, $fallback);
4418 1
                $this->offsetUnset($key);
4419
            }
4420
        } else {
4421 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4422 4
            $this->offsetUnset($keyOrKeys);
4423
        }
4424
4425 4
        return $valueOrValues;
4426
    }
4427
4428
    /**
4429
     * Push one or more values onto the end of array at once.
4430
     *
4431
     * @param array ...$args
4432
     *
4433
     * @return $this
4434
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4435
     *
4436
     * @noinspection ReturnTypeCanBeDeclaredInspection
4437
     *
4438
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4439
     * @psalm-return static<TKey,T>
4440
     */
4441 7
    public function push(...$args)
4442
    {
4443 7
        $this->generatorToArray();
4444
4445
        if (
4446 7
            $this->checkPropertyTypes
4447
            &&
4448 7
            $this->properties !== []
4449
        ) {
4450 1
            foreach ($args as $key => $value) {
4451 1
                $this->checkType($key, $value);
4452
            }
4453
        }
4454
4455 7
        \array_push($this->array, ...$args);
4456
4457 7
        return $this;
4458
    }
4459
4460
    /**
4461
     * Get a random value from the current array.
4462
     *
4463
     * @param int|null $number <p>How many values you will take?</p>
4464
     *
4465
     * @return static
4466
     *                <p>(Immutable)</p>
4467
     *
4468
     * @psalm-return static<int|array-key,T>
4469
     */
4470 19
    public function randomImmutable(int $number = null): self
4471
    {
4472 19
        $this->generatorToArray();
4473
4474 19
        if ($this->count() === 0) {
4475 1
            return static::create(
4476 1
                [],
4477 1
                $this->iteratorClass,
4478 1
                false
4479
            );
4480
        }
4481
4482 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...
4483
            /** @noinspection NonSecureArrayRandUsageInspection */
4484 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4485
4486 13
            return static::create(
4487 13
                $arrayRandValue,
4488 13
                $this->iteratorClass,
4489 13
                false
4490
            );
4491
        }
4492
4493 6
        $arrayTmp = $this->array;
4494
        /** @noinspection NonSecureShuffleUsageInspection */
4495 6
        \shuffle($arrayTmp);
4496
4497 6
        return static::create(
4498 6
            $arrayTmp,
4499 6
            $this->iteratorClass,
4500 6
            false
4501 6
        )->firstsImmutable($number);
4502
    }
4503
4504
    /**
4505
     * Pick a random key/index from the keys of this array.
4506
     *
4507
     * @throws \RangeException If array is empty
4508
     *
4509
     * @return mixed
4510
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4511
     */
4512 4
    public function randomKey()
4513
    {
4514 4
        $result = $this->randomKeys(1);
4515
4516 4
        if (!isset($result[0])) {
4517
            $result[0] = null;
4518
        }
4519
4520 4
        return $result[0];
4521
    }
4522
4523
    /**
4524
     * Pick a given number of random keys/indexes out of this array.
4525
     *
4526
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4527
     *
4528
     * @throws \RangeException If array is empty
4529
     *
4530
     * @return static
4531
     *                <p>(Immutable)</p>
4532
     *
4533
     * @psalm-return static<TKey,T>
4534
     */
4535 13
    public function randomKeys(int $number): self
4536
    {
4537 13
        $this->generatorToArray();
4538
4539 13
        $count = $this->count();
4540
4541
        if (
4542 13
            $number === 0
4543
            ||
4544 13
            $number > $count
4545
        ) {
4546 2
            throw new \RangeException(
4547 2
                \sprintf(
4548 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4549 2
                    $number,
4550 2
                    $count
4551
                )
4552
            );
4553
        }
4554
4555 11
        $result = (array) \array_rand($this->array, $number);
4556
4557 11
        return static::create(
4558 11
            $result,
4559 11
            $this->iteratorClass,
4560 11
            false
4561
        );
4562
    }
4563
4564
    /**
4565
     * Get a random value from the current array.
4566
     *
4567
     * @param int|null $number <p>How many values you will take?</p>
4568
     *
4569
     * @return $this
4570
     *               <p>(Mutable) Return this Arrayy object.</p>
4571
     *
4572
     * @psalm-return static<TKey,T>
4573
     */
4574 17
    public function randomMutable(int $number = null): self
4575
    {
4576 17
        $this->generatorToArray();
4577
4578 17
        if ($this->count() === 0) {
4579
            return static::create(
4580
                [],
4581
                $this->iteratorClass,
4582
                false
4583
            );
4584
        }
4585
4586 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...
4587
            /** @noinspection NonSecureArrayRandUsageInspection */
4588 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4589 7
            $this->array = $arrayRandValue;
4590
4591 7
            return $this;
4592
        }
4593
4594
        /** @noinspection NonSecureShuffleUsageInspection */
4595 11
        \shuffle($this->array);
4596
4597 11
        return $this->firstsMutable($number);
4598
    }
4599
4600
    /**
4601
     * Pick a random value from the values of this array.
4602
     *
4603
     * @return mixed
4604
     *               <p>Get a random value or null if there wasn't a value.</p>
4605
     */
4606 4
    public function randomValue()
4607
    {
4608 4
        $result = $this->randomImmutable();
4609
4610 4
        if (!isset($result[0])) {
4611
            $result[0] = null;
4612
        }
4613
4614 4
        return $result[0];
4615
    }
4616
4617
    /**
4618
     * Pick a given number of random values out of this array.
4619
     *
4620
     * @param int $number
4621
     *
4622
     * @return static
4623
     *                <p>(Mutable)</p>
4624
     *
4625
     * @psalm-return static<TKey,T>
4626
     */
4627 7
    public function randomValues(int $number): self
4628
    {
4629 7
        return $this->randomMutable($number);
4630
    }
4631
4632
    /**
4633
     * Get a random value from an array, with the ability to skew the results.
4634
     *
4635
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4636
     *
4637
     * @param array    $array
4638
     * @param int|null $number <p>How many values you will take?</p>
4639
     *
4640
     * @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...
4641
     *                           <p>(Immutable)</p>
4642
     *
4643
     * @psalm-param  array<mixed,mixed> $array
4644
     * @psalm-return static<int|array-key,T>
4645
     */
4646 9
    public function randomWeighted(array $array, int $number = null): self
4647
    {
4648
        // init
4649 9
        $options = [];
4650
4651 9
        foreach ($array as $option => $weight) {
4652 9
            if ($this->searchIndex($option) !== false) {
4653 2
                for ($i = 0; $i < $weight; ++$i) {
4654 1
                    $options[] = $option;
4655
                }
4656
            }
4657
        }
4658
4659 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4660
    }
4661
4662
    /**
4663
     * Reduce the current array via callable e.g. anonymous-function.
4664
     *
4665
     * @param callable $callable
4666
     * @param mixed    $init
4667
     *
4668
     * @return static
4669
     *                <p>(Immutable)</p>
4670
     *
4671
     * @psalm-return static<TKey,T>
4672
     * @psalm-mutation-free
4673
     */
4674 18
    public function reduce($callable, $init = []): self
4675
    {
4676 18
        if ($this->generator) {
4677 1
            $result = $init;
4678
4679 1
            foreach ($this->getGenerator() as $value) {
4680 1
                $result = $callable($result, $value);
4681
            }
4682
4683 1
            return static::create(
4684 1
                $result,
4685 1
                $this->iteratorClass,
4686 1
                false
4687
            );
4688
        }
4689
4690 18
        $result = \array_reduce($this->array, $callable, $init);
4691
4692 18
        if ($result === null) {
4693
            $this->array = [];
4694
        } else {
4695 18
            $this->array = (array) $result;
4696
        }
4697
4698 18
        return static::create(
4699 18
            $this->array,
4700 18
            $this->iteratorClass,
4701 18
            false
4702
        );
4703
    }
4704
4705
    /**
4706
     * @param bool $unique
4707
     *
4708
     * @return static
4709
     *                <p>(Immutable)</p>
4710
     *
4711
     * @psalm-return static<TKey,T>
4712
     * @psalm-mutation-free
4713
     */
4714 14
    public function reduce_dimension(bool $unique = true): self
4715
    {
4716
        // init
4717 14
        $result = [];
4718
4719 14
        foreach ($this->getGenerator() as $val) {
4720 12
            if (\is_array($val) === true) {
4721 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4722
            } else {
4723 12
                $result[] = [$val];
4724
            }
4725
        }
4726
4727 14
        $result = $result === [] ? [] : \array_merge(...$result);
4728
4729 14
        $resultArrayy = new static($result);
4730
4731
        /**
4732
         * @psalm-suppress ImpureMethodCall - object is already re-created
4733
         * @psalm-suppress InvalidReturnStatement - why?
4734
         */
4735 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4736
    }
4737
4738
    /**
4739
     * Create a numerically re-indexed Arrayy object.
4740
     *
4741
     * @return $this
4742
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4743
     *
4744
     * @psalm-return static<TKey,T>
4745
     */
4746 9
    public function reindex(): self
4747
    {
4748 9
        $this->generatorToArray(false);
4749
4750 9
        $this->array = \array_values($this->array);
4751
4752 9
        return $this;
4753
    }
4754
4755
    /**
4756
     * Return all items that fail the truth test.
4757
     *
4758
     * @param \Closure $closure
4759
     *
4760
     * @return static
4761
     *                <p>(Immutable)</p>
4762
     *
4763
     * @psalm-return static<TKey,T>
4764
     * @psalm-mutation-free
4765
     */
4766 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...
4767
    {
4768
        // init
4769 1
        $filtered = [];
4770
4771 1
        foreach ($this->getGenerator() as $key => $value) {
4772 1
            if (!$closure($value, $key)) {
4773 1
                $filtered[$key] = $value;
4774
            }
4775
        }
4776
4777 1
        return static::create(
4778 1
            $filtered,
4779 1
            $this->iteratorClass,
4780 1
            false
4781
        );
4782
    }
4783
4784
    /**
4785
     * Remove a value from the current array (optional using dot-notation).
4786
     *
4787
     * @param mixed $key
4788
     *
4789
     * @return static
4790
     *                <p>(Mutable)</p>
4791
     *
4792
     * @psalm-param  TKey $key
4793
     * @psalm-return static<TKey,T>
4794
     */
4795 21
    public function remove($key)
4796
    {
4797
        // recursive call
4798 21
        if (\is_array($key) === true) {
4799
            foreach ($key as $k) {
4800
                $this->internalRemove($k);
4801
            }
4802
4803
            return static::create(
4804
                $this->toArray(),
4805
                $this->iteratorClass,
4806
                false
4807
            );
4808
        }
4809
4810 21
        $this->internalRemove($key);
4811
4812 21
        return static::create(
4813 21
            $this->toArray(),
4814 21
            $this->iteratorClass,
4815 21
            false
4816
        );
4817
    }
4818
4819
    /**
4820
     * alias: for "Arrayy->removeValue()"
4821
     *
4822
     * @param mixed $element
4823
     *
4824
     * @return static
4825
     *                <p>(Immutable)</p>
4826
     *
4827
     * @psalm-param  T $element
4828
     * @psalm-return static<TKey,T>
4829
     * @psalm-mutation-free
4830
     */
4831 8
    public function removeElement($element)
4832
    {
4833 8
        return $this->removeValue($element);
4834
    }
4835
4836
    /**
4837
     * Remove the first value from the current array.
4838
     *
4839
     * @return static
4840
     *                <p>(Immutable)</p>
4841
     *
4842
     * @psalm-return static<TKey,T>
4843
     * @psalm-mutation-free
4844
     */
4845 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...
4846
    {
4847 7
        $tmpArray = $this->toArray();
4848
4849 7
        \array_shift($tmpArray);
4850
4851 7
        return static::create(
4852 7
            $tmpArray,
4853 7
            $this->iteratorClass,
4854 7
            false
4855
        );
4856
    }
4857
4858
    /**
4859
     * Remove the last value from the current array.
4860
     *
4861
     * @return static
4862
     *                <p>(Immutable)</p>
4863
     *
4864
     * @psalm-return static<TKey,T>
4865
     * @psalm-mutation-free
4866
     */
4867 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...
4868
    {
4869 7
        $tmpArray = $this->toArray();
4870
4871 7
        \array_pop($tmpArray);
4872
4873 7
        return static::create(
4874 7
            $tmpArray,
4875 7
            $this->iteratorClass,
4876 7
            false
4877
        );
4878
    }
4879
4880
    /**
4881
     * Removes a particular value from an array (numeric or associative).
4882
     *
4883
     * @param mixed $value
4884
     *
4885
     * @return static
4886
     *                <p>(Immutable)</p>
4887
     *
4888
     * @psalm-param  T $value
4889
     * @psalm-return static<TKey,T>
4890
     * @psalm-mutation-free
4891
     */
4892 8
    public function removeValue($value): self
4893
    {
4894 8
        $this->generatorToArray();
4895
4896
        // init
4897 8
        $isSequentialArray = $this->isSequential();
4898
4899 8
        foreach ($this->array as $key => $item) {
4900 7
            if ($item === $value) {
4901 7
                unset($this->array[$key]);
4902
            }
4903
        }
4904
4905 8
        if ($isSequentialArray) {
4906 6
            $this->array = \array_values($this->array);
4907
        }
4908
4909 8
        return static::create(
4910 8
            $this->array,
4911 8
            $this->iteratorClass,
4912 8
            false
4913
        );
4914
    }
4915
4916
    /**
4917
     * Generate array of repeated arrays.
4918
     *
4919
     * @param int $times <p>How many times has to be repeated.</p>
4920
     *
4921
     * @return static
4922
     *                <p>(Immutable)</p>
4923
     *
4924
     * @psalm-return static<TKey,T>
4925
     * @psalm-mutation-free
4926
     */
4927 1
    public function repeat($times): self
4928
    {
4929 1
        if ($times === 0) {
4930 1
            return static::create([], $this->iteratorClass);
4931
        }
4932
4933 1
        return static::create(
4934 1
            \array_fill(0, (int) $times, $this->toArray()),
4935 1
            $this->iteratorClass,
4936 1
            false
4937
        );
4938
    }
4939
4940
    /**
4941
     * Replace a key with a new key/value pair.
4942
     *
4943
     * @param mixed $oldKey
4944
     * @param mixed $newKey
4945
     * @param mixed $newValue
4946
     *
4947
     * @return static
4948
     *                <p>(Immutable)</p>
4949
     *
4950
     * @psalm-return static<TKey,T>
4951
     * @psalm-mutation-free
4952
     */
4953 5
    public function replace($oldKey, $newKey, $newValue): self
4954
    {
4955 5
        $that = clone $this;
4956
4957
        /**
4958
         * @psalm-suppress ImpureMethodCall - object is already cloned
4959
         */
4960 5
        return $that->remove($oldKey)
4961 5
            ->set($newKey, $newValue);
4962
    }
4963
4964
    /**
4965
     * Create an array using the current array as values and the other array as keys.
4966
     *
4967
     * @param array $keys <p>An array of keys.</p>
4968
     *
4969
     * @return static
4970
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4971
     *
4972
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4973
     * @psalm-return static<TKey,T>
4974
     * @psalm-mutation-free
4975
     */
4976 2
    public function replaceAllKeys(array $keys): self
4977
    {
4978 2
        return static::create(
4979 2
            \array_combine($keys, $this->toArray()),
4980 2
            $this->iteratorClass,
4981 2
            false
4982
        );
4983
    }
4984
4985
    /**
4986
     * Create an array using the current array as keys and the other array as values.
4987
     *
4988
     * @param array $array <p>An array o values.</p>
4989
     *
4990
     * @return static
4991
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4992
     *
4993
     * @psalm-param  array<mixed,T> $array
4994
     * @psalm-return static<TKey,T>
4995
     * @psalm-mutation-free
4996
     */
4997 2
    public function replaceAllValues(array $array): self
4998
    {
4999 2
        return static::create(
5000 2
            \array_combine($this->array, $array),
5001 2
            $this->iteratorClass,
5002 2
            false
5003
        );
5004
    }
5005
5006
    /**
5007
     * Replace the keys in an array with another set.
5008
     *
5009
     * @param array $keys <p>An array of keys matching the array's size</p>
5010
     *
5011
     * @return static
5012
     *                <p>(Immutable)</p>
5013
     *
5014
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5015
     * @psalm-return static<TKey,T>
5016
     * @psalm-mutation-free
5017
     */
5018 1
    public function replaceKeys(array $keys): self
5019
    {
5020 1
        $values = \array_values($this->toArray());
5021 1
        $result = \array_combine($keys, $values);
5022
5023 1
        return static::create(
5024 1
            $result,
5025 1
            $this->iteratorClass,
5026 1
            false
5027
        );
5028
    }
5029
5030
    /**
5031
     * Replace the first matched value in an array.
5032
     *
5033
     * @param mixed $search      <p>The value to replace.</p>
5034
     * @param mixed $replacement <p>The value to replace.</p>
5035
     *
5036
     * @return static
5037
     *                <p>(Immutable)</p>
5038
     *
5039
     * @psalm-return static<TKey,T>
5040
     * @psalm-mutation-free
5041
     */
5042 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...
5043
    {
5044 3
        $array = $this->toArray();
5045 3
        $key = \array_search($search, $array, true);
5046
5047 3
        if ($key !== false) {
5048 3
            $array[$key] = $replacement;
5049
        }
5050
5051 3
        return static::create(
5052 3
            $array,
5053 3
            $this->iteratorClass,
5054 3
            false
5055
        );
5056
    }
5057
5058
    /**
5059
     * Replace values in the current array.
5060
     *
5061
     * @param mixed $search      <p>The value to replace.</p>
5062
     * @param mixed $replacement <p>What to replace it with.</p>
5063
     *
5064
     * @return static
5065
     *                <p>(Immutable)</p>
5066
     *
5067
     * @psalm-return static<TKey,T>
5068
     * @psalm-mutation-free
5069
     */
5070 1
    public function replaceValues($search, $replacement = ''): self
5071
    {
5072
        /**
5073
         * @psalm-suppress MissingClosureReturnType
5074
         * @psalm-suppress MissingClosureParamType
5075
         */
5076 1
        return $this->each(
5077
            static function ($value) use ($search, $replacement) {
5078 1
                return \str_replace($search, $replacement, $value);
5079 1
            }
5080
        );
5081
    }
5082
5083
    /**
5084
     * Get the last elements from index $from until the end of this array.
5085
     *
5086
     * @param int $from
5087
     *
5088
     * @return static
5089
     *                <p>(Immutable)</p>
5090
     *
5091
     * @psalm-return static<TKey,T>
5092
     * @psalm-mutation-free
5093
     */
5094 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...
5095
    {
5096 15
        $tmpArray = $this->toArray();
5097
5098 15
        return static::create(
5099 15
            \array_splice($tmpArray, $from),
5100 15
            $this->iteratorClass,
5101 15
            false
5102
        );
5103
    }
5104
5105
    /**
5106
     * Return the array in the reverse order.
5107
     *
5108
     * @return $this
5109
     *               <p>(Mutable) Return this Arrayy object.</p>
5110
     *
5111
     * @psalm-return static<TKey,T>
5112
     */
5113 9
    public function reverse(): self
5114
    {
5115 9
        $this->generatorToArray();
5116
5117 9
        $this->array = \array_reverse($this->array);
5118
5119 9
        return $this;
5120
    }
5121
5122
    /**
5123
     * Sort an array in reverse order.
5124
     *
5125
     * @param int $sort_flags [optional] <p>
5126
     *                        You may modify the behavior of the sort using the optional
5127
     *                        parameter sort_flags, for details
5128
     *                        see sort.
5129
     *                        </p>
5130
     *
5131
     * @return $this
5132
     *               <p>(Mutable) Return this Arrayy object.</p>
5133
     *
5134
     * @psalm-return static<TKey,T>
5135
     */
5136 4
    public function rsort(int $sort_flags = 0): self
5137
    {
5138 4
        $this->generatorToArray();
5139
5140 4
        \rsort($this->array, $sort_flags);
5141
5142 4
        return $this;
5143
    }
5144
5145
    /**
5146
     * Sort an array in reverse order.
5147
     *
5148
     * @param int $sort_flags [optional] <p>
5149
     *                        You may modify the behavior of the sort using the optional
5150
     *                        parameter sort_flags, for details
5151
     *                        see sort.
5152
     *                        </p>
5153
     *
5154
     * @return $this
5155
     *               <p>(Immutable) Return this Arrayy object.</p>
5156
     *
5157
     * @psalm-return static<TKey,T>
5158
     * @psalm-mutation-free
5159
     */
5160 4
    public function rsortImmutable(int $sort_flags = 0): self
5161
    {
5162 4
        $that = clone $this;
5163
5164
        /**
5165
         * @psalm-suppress ImpureMethodCall - object is already cloned
5166
         */
5167 4
        $that->rsort($sort_flags);
5168
5169 4
        return $that;
5170
    }
5171
5172
    /**
5173
     * Search for the first index of the current array via $value.
5174
     *
5175
     * @param mixed $value
5176
     *
5177
     * @return false|float|int|string
5178
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5179
     * @psalm-mutation-free
5180
     */
5181 21
    public function searchIndex($value)
5182
    {
5183 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5184 20
            if ($value === $valueFromArray) {
5185 10
                return $keyFromArray;
5186
            }
5187
        }
5188
5189 11
        return false;
5190
    }
5191
5192
    /**
5193
     * Search for the value of the current array via $index.
5194
     *
5195
     * @param mixed $index
5196
     *
5197
     * @return static
5198
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5199
     *
5200
     * @psalm-return static<TKey,T>
5201
     * @psalm-mutation-free
5202
     */
5203 9
    public function searchValue($index): self
5204
    {
5205 9
        $this->generatorToArray();
5206
5207
        // init
5208 9
        $return = [];
5209
5210 9
        if ($this->array === []) {
5211
            return static::create(
5212
                [],
5213
                $this->iteratorClass,
5214
                false
5215
            );
5216
        }
5217
5218
        // php cast "bool"-index into "int"-index
5219 9
        if ((bool) $index === $index) {
5220 1
            $index = (int) $index;
5221
        }
5222
5223 9
        if ($this->offsetExists($index)) {
5224 7
            $return = [$this->array[$index]];
5225
        }
5226
5227 9
        return static::create(
5228 9
            $return,
5229 9
            $this->iteratorClass,
5230 9
            false
5231
        );
5232
    }
5233
5234
    /**
5235
     * Set a value for the current array (optional using dot-notation).
5236
     *
5237
     * @param string $key   <p>The key to set.</p>
5238
     * @param mixed  $value <p>Its value.</p>
5239
     *
5240
     * @return $this
5241
     *               <p>(Mutable) Return this Arrayy object.</p>
5242
     *
5243
     * @psalm-param  TKey $key
5244
     * @psalm-param  T $value
5245
     * @psalm-return static<TKey,T>
5246
     */
5247 28
    public function set($key, $value): self
5248
    {
5249 28
        $this->internalSet($key, $value);
5250
5251 27
        return $this;
5252
    }
5253
5254
    /**
5255
     * Get a value from a array and set it if it was not.
5256
     *
5257
     * WARNING: this method only set the value, if the $key is not already set
5258
     *
5259
     * @param mixed $key      <p>The key</p>
5260
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5261
     *
5262
     * @return mixed
5263
     *               <p>(Mutable)</p>
5264
     */
5265 11
    public function setAndGet($key, $fallback = null)
5266
    {
5267 11
        $this->generatorToArray();
5268
5269
        // If the key doesn't exist, set it.
5270 11
        if (!$this->has($key)) {
5271 4
            $this->array = $this->set($key, $fallback)->toArray();
5272
        }
5273
5274 11
        return $this->get($key);
5275
    }
5276
5277
    /**
5278
     * Shifts a specified value off the beginning of array.
5279
     *
5280
     * @return mixed
5281
     *               <p>(Mutable) A shifted element from the current array.</p>
5282
     */
5283 5
    public function shift()
5284
    {
5285 5
        $this->generatorToArray();
5286
5287 5
        return \array_shift($this->array);
5288
    }
5289
5290
    /**
5291
     * Shuffle the current array.
5292
     *
5293
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5294
     * @param array $array  [optional]
5295
     *
5296
     * @return static
5297
     *                <p>(Immutable)</p>
5298
     *
5299
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5300
     * @psalm-return static<TKey,T>
5301
     *
5302
     * @noinspection BadExceptionsProcessingInspection
5303
     * @noinspection RandomApiMigrationInspection
5304
     * @noinspection NonSecureShuffleUsageInspection
5305
     */
5306 2
    public function shuffle(bool $secure = false, array $array = null): self
5307
    {
5308 2
        if ($array === null) {
5309 2
            $array = $this->toArray(false);
5310
        }
5311
5312 2
        if ($secure !== true) {
5313 2
            \shuffle($array);
5314
        } else {
5315 1
            $size = \count($array, \COUNT_NORMAL);
5316 1
            $keys = \array_keys($array);
5317 1
            for ($i = $size - 1; $i > 0; --$i) {
5318
                try {
5319 1
                    $r = \random_int(0, $i);
5320
                } catch (\Exception $e) {
5321
                    $r = \mt_rand(0, $i);
5322
                }
5323 1
                if ($r !== $i) {
5324
                    $temp = $array[$keys[$r]];
5325
                    $array[$keys[$r]] = $array[$keys[$i]];
5326
                    $array[$keys[$i]] = $temp;
5327
                }
5328
            }
5329
        }
5330
5331 2
        foreach ($array as $key => $value) {
5332
            // check if recursive is needed
5333 2
            if (\is_array($value) === true) {
5334
                $array[$key] = $this->shuffle($secure, $value);
5335
            }
5336
        }
5337
5338 2
        return static::create(
5339 2
            $array,
5340 2
            $this->iteratorClass,
5341 2
            false
5342
        );
5343
    }
5344
5345
    /**
5346
     * Count the values from the current array.
5347
     *
5348
     * alias: for "Arrayy->count()"
5349
     *
5350
     * @param int $mode
5351
     *
5352
     * @return int
5353
     */
5354 20
    public function size(int $mode = \COUNT_NORMAL): int
5355
    {
5356 20
        return $this->count($mode);
5357
    }
5358
5359
    /**
5360
     * Checks whether array has exactly $size items.
5361
     *
5362
     * @param int $size
5363
     *
5364
     * @return bool
5365
     */
5366 1
    public function sizeIs(int $size): bool
5367
    {
5368
        // init
5369 1
        $itemsTempCount = 0;
5370
5371
        /** @noinspection PhpUnusedLocalVariableInspection */
5372
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
5373 1
        foreach ($this->getGeneratorByReference() as &$value) {
5374 1
            ++$itemsTempCount;
5375 1
            if ($itemsTempCount > $size) {
5376 1
                return false;
5377
            }
5378
        }
5379
5380 1
        return $itemsTempCount === $size;
5381
    }
5382
5383
    /**
5384
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5385
     * smaller than $fromSize.
5386
     *
5387
     * @param int $fromSize
5388
     * @param int $toSize
5389
     *
5390
     * @return bool
5391
     */
5392 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5393
    {
5394 1
        if ($fromSize > $toSize) {
5395 1
            $tmp = $toSize;
5396 1
            $toSize = $fromSize;
5397 1
            $fromSize = $tmp;
5398
        }
5399
5400
        // init
5401 1
        $itemsTempCount = 0;
5402
5403 1
        foreach ($this->getGenerator() as $key => $value) {
5404 1
            ++$itemsTempCount;
5405 1
            if ($itemsTempCount > $toSize) {
5406 1
                return false;
5407
            }
5408
        }
5409
5410 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5411
    }
5412
5413
    /**
5414
     * Checks whether array has more than $size items.
5415
     *
5416
     * @param int $size
5417
     *
5418
     * @return bool
5419
     */
5420 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...
5421
    {
5422
        // init
5423 1
        $itemsTempCount = 0;
5424
5425 1
        foreach ($this->getGenerator() as $key => $value) {
5426 1
            ++$itemsTempCount;
5427 1
            if ($itemsTempCount > $size) {
5428 1
                return true;
5429
            }
5430
        }
5431
5432 1
        return $itemsTempCount > $size;
5433
    }
5434
5435
    /**
5436
     * Checks whether array has less than $size items.
5437
     *
5438
     * @param int $size
5439
     *
5440
     * @return bool
5441
     */
5442 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...
5443
    {
5444
        // init
5445 1
        $itemsTempCount = 0;
5446
5447 1
        foreach ($this->getGenerator() as $key => $value) {
5448 1
            ++$itemsTempCount;
5449 1
            if ($itemsTempCount > $size) {
5450 1
                return false;
5451
            }
5452
        }
5453
5454 1
        return $itemsTempCount < $size;
5455
    }
5456
5457
    /**
5458
     * Counts all elements in an array, or something in an object.
5459
     *
5460
     * <p>
5461
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5462
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5463
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5464
     * implemented and used in PHP.
5465
     * </p>
5466
     *
5467
     * @return int
5468
     *             <p>
5469
     *             The number of elements in var, which is
5470
     *             typically an array, since anything else will have one
5471
     *             element.
5472
     *             </p>
5473
     *             <p>
5474
     *             If var is not an array or an object with
5475
     *             implemented Countable interface,
5476
     *             1 will be returned.
5477
     *             There is one exception, if var is &null;,
5478
     *             0 will be returned.
5479
     *             </p>
5480
     *             <p>
5481
     *             Caution: count may return 0 for a variable that isn't set,
5482
     *             but it may also return 0 for a variable that has been initialized with an
5483
     *             empty array. Use isset to test if a variable is set.
5484
     *             </p>
5485
     */
5486 10
    public function sizeRecursive(): int
5487
    {
5488 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5489
    }
5490
5491
    /**
5492
     * Extract a slice of the array.
5493
     *
5494
     * @param int      $offset       <p>Slice begin index.</p>
5495
     * @param int|null $length       <p>Length of the slice.</p>
5496
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5497
     *
5498
     * @return static
5499
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5500
     *
5501
     * @psalm-return static<TKey,T>
5502
     * @psalm-mutation-free
5503
     */
5504 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5505
    {
5506 5
        return static::create(
5507 5
            \array_slice(
5508 5
                $this->toArray(),
5509 5
                $offset,
5510 5
                $length,
5511 5
                $preserveKeys
5512
            ),
5513 5
            $this->iteratorClass,
5514 5
            false
5515
        );
5516
    }
5517
5518
    /**
5519
     * Sort the current array and optional you can keep the keys.
5520
     *
5521
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5522
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5523
     *                              <strong>SORT_NATURAL</strong></p>
5524
     * @param bool       $keepKeys
5525
     *
5526
     * @return static
5527
     *                <p>(Mutable) Return this Arrayy object.</p>
5528
     *
5529
     * @psalm-return static<TKey,T>
5530
     */
5531 20
    public function sort(
5532
        $direction = \SORT_ASC,
5533
        int $strategy = \SORT_REGULAR,
5534
        bool $keepKeys = false
5535
    ): self {
5536 20
        $this->generatorToArray();
5537
5538 20
        return $this->sorting(
5539 20
            $this->array,
5540 20
            $direction,
5541 20
            $strategy,
5542 20
            $keepKeys
5543
        );
5544
    }
5545
5546
    /**
5547
     * Sort the current array and optional you can keep the keys.
5548
     *
5549
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5550
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5551
     *                              <strong>SORT_NATURAL</strong></p>
5552
     * @param bool       $keepKeys
5553
     *
5554
     * @return static
5555
     *                <p>(Immutable) Return this Arrayy object.</p>
5556
     *
5557
     * @psalm-return static<TKey,T>
5558
     */
5559 12
    public function sortImmutable(
5560
        $direction = \SORT_ASC,
5561
        int $strategy = \SORT_REGULAR,
5562
        bool $keepKeys = false
5563
    ): self {
5564 12
        $that = clone $this;
5565
5566 12
        $that->generatorToArray();
5567
5568 12
        return $that->sorting(
5569 12
            $that->array,
5570 12
            $direction,
5571 12
            $strategy,
5572 12
            $keepKeys
5573
        );
5574
    }
5575
5576
    /**
5577
     * Sort the current array by key.
5578
     *
5579
     * @see          http://php.net/manual/en/function.ksort.php
5580
     * @see          http://php.net/manual/en/function.krsort.php
5581
     *
5582
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5583
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5584
     *                              <strong>SORT_NATURAL</strong></p>
5585
     *
5586
     * @return $this
5587
     *               <p>(Mutable) Return this Arrayy object.</p>
5588
     *
5589
     * @psalm-return static<TKey,T>
5590
     */
5591 18
    public function sortKeys(
5592
        $direction = \SORT_ASC,
5593
        int $strategy = \SORT_REGULAR
5594
    ): self {
5595 18
        $this->generatorToArray();
5596
5597 18
        $this->sorterKeys($this->array, $direction, $strategy);
5598
5599 18
        return $this;
5600
    }
5601
5602
    /**
5603
     * Sort the current array by key.
5604
     *
5605
     * @see          http://php.net/manual/en/function.ksort.php
5606
     * @see          http://php.net/manual/en/function.krsort.php
5607
     *
5608
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5609
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5610
     *                              <strong>SORT_NATURAL</strong></p>
5611
     *
5612
     * @return $this
5613
     *               <p>(Immutable) Return this Arrayy object.</p>
5614
     *
5615
     * @psalm-return static<TKey,T>
5616
     * @psalm-mutation-free
5617
     */
5618 8
    public function sortKeysImmutable(
5619
        $direction = \SORT_ASC,
5620
        int $strategy = \SORT_REGULAR
5621
    ): self {
5622 8
        $that = clone $this;
5623
5624
        /**
5625
         * @psalm-suppress ImpureMethodCall - object is already cloned
5626
         */
5627 8
        $that->sortKeys($direction, $strategy);
5628
5629 8
        return $that;
5630
    }
5631
5632
    /**
5633
     * Sort the current array by value.
5634
     *
5635
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5636
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5637
     *                              <strong>SORT_NATURAL</strong></p>
5638
     *
5639
     * @return static
5640
     *                <p>(Mutable)</p>
5641
     *
5642
     * @psalm-return static<TKey,T>
5643
     */
5644 1
    public function sortValueKeepIndex(
5645
        $direction = \SORT_ASC,
5646
        int $strategy = \SORT_REGULAR
5647
    ): self {
5648 1
        return $this->sort($direction, $strategy, true);
5649
    }
5650
5651
    /**
5652
     * Sort the current array by value.
5653
     *
5654
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5655
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5656
     *                              <strong>SORT_NATURAL</strong></p>
5657
     *
5658
     * @return static
5659
     *                <p>(Mutable)</p>
5660
     *
5661
     * @psalm-return static<TKey,T>
5662
     */
5663 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5664
    {
5665 1
        return $this->sort($direction, $strategy, false);
5666
    }
5667
5668
    /**
5669
     * Sort a array by value, by a closure or by a property.
5670
     *
5671
     * - If the sorter is null, the array is sorted naturally.
5672
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5673
     *
5674
     * @param callable|string|null $sorter
5675
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5676
     *                                        <strong>SORT_DESC</strong></p>
5677
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5678
     *                                        <strong>SORT_NATURAL</strong></p>
5679
     *
5680
     * @return static
5681
     *                <p>(Immutable)</p>
5682
     *
5683
     * @psalm-return static<TKey,T>
5684
     * @psalm-mutation-free
5685
     */
5686 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5687
    {
5688 1
        $array = $this->toArray();
5689 1
        $direction = $this->getDirection($direction);
5690
5691
        // Transform all values into their results.
5692 1
        if ($sorter) {
5693 1
            $arrayy = static::create(
5694 1
                $array,
5695 1
                $this->iteratorClass,
5696 1
                false
5697
            );
5698
5699
            /**
5700
             * @psalm-suppress MissingClosureReturnType
5701
             * @psalm-suppress MissingClosureParamType
5702
             */
5703 1
            $results = $arrayy->each(
5704
                function ($value) use ($sorter) {
5705 1
                    if (\is_callable($sorter) === true) {
5706 1
                        return $sorter($value);
5707
                    }
5708
5709 1
                    return $this->get($sorter);
5710 1
                }
5711
            );
5712
5713 1
            $results = $results->toArray();
5714
        } else {
5715 1
            $results = $array;
5716
        }
5717
5718
        // Sort by the results and replace by original values
5719 1
        \array_multisort($results, $direction, $strategy, $array);
5720
5721 1
        return static::create(
5722 1
            $array,
5723 1
            $this->iteratorClass,
5724 1
            false
5725
        );
5726
    }
5727
5728
    /**
5729
     * @param int      $offset
5730
     * @param int|null $length
5731
     * @param array    $replacement
5732
     *
5733
     * @return static
5734
     *                <p>(Immutable)</p>
5735
     *
5736
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5737
     * @psalm-return static<TKey,T>
5738
     * @psalm-mutation-free
5739
     */
5740 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5741
    {
5742 1
        $tmpArray = $this->toArray();
5743
5744 1
        \array_splice(
5745 1
            $tmpArray,
5746 1
            $offset,
5747 1
            $length ?? $this->count(),
5748 1
            $replacement
5749
        );
5750
5751 1
        return static::create(
5752 1
            $tmpArray,
5753 1
            $this->iteratorClass,
5754 1
            false
5755
        );
5756
    }
5757
5758
    /**
5759
     * Split an array in the given amount of pieces.
5760
     *
5761
     * @param int  $numberOfPieces
5762
     * @param bool $keepKeys
5763
     *
5764
     * @return static
5765
     *                <p>(Immutable)</p>
5766
     *
5767
     * @psalm-return static<TKey,T>
5768
     * @psalm-mutation-free
5769
     */
5770 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5771
    {
5772 1
        $this->generatorToArray();
5773
5774 1
        $count = $this->count();
5775
5776 1
        if ($count === 0) {
5777 1
            $result = [];
5778
        } else {
5779 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5780 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5781
        }
5782
5783 1
        return static::create(
5784 1
            $result,
5785 1
            $this->iteratorClass,
5786 1
            false
5787
        );
5788
    }
5789
5790
    /**
5791
     * Stripe all empty items.
5792
     *
5793
     * @return static
5794
     *                <p>(Immutable)</p>
5795
     *
5796
     * @psalm-return static<TKey,T>
5797
     * @psalm-mutation-free
5798
     */
5799 1
    public function stripEmpty(): self
5800
    {
5801 1
        return $this->filter(
5802
            static function ($item) {
5803 1
                if ($item === null) {
5804 1
                    return false;
5805
                }
5806
5807 1
                return (bool) \trim((string) $item);
5808 1
            }
5809
        );
5810
    }
5811
5812
    /**
5813
     * Swap two values between positions by key.
5814
     *
5815
     * @param int|string $swapA <p>a key in the array</p>
5816
     * @param int|string $swapB <p>a key in the array</p>
5817
     *
5818
     * @return static
5819
     *                <p>(Immutable)</p>
5820
     *
5821
     * @psalm-return static<TKey,T>
5822
     * @psalm-mutation-free
5823
     */
5824 1
    public function swap($swapA, $swapB): self
5825
    {
5826 1
        $array = $this->toArray();
5827
5828 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5829
5830 1
        return static::create(
5831 1
            $array,
5832 1
            $this->iteratorClass,
5833 1
            false
5834
        );
5835
    }
5836
5837
    /**
5838
     * Get the current array from the "Arrayy"-object.
5839
     * alias for "getArray()"
5840
     *
5841
     * @param bool $convertAllArrayyElements <p>
5842
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5843
     *                                       </p>
5844
     * @param bool $preserveKeys             <p>
5845
     *                                       e.g.: A generator maybe return the same key more then once,
5846
     *                                       so maybe you will ignore the keys.
5847
     *                                       </p>
5848
     *
5849
     * @return array
5850
     *
5851
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5852
     * @psalm-mutation-free
5853
     */
5854 945
    public function toArray(
5855
        bool $convertAllArrayyElements = false,
5856
        bool $preserveKeys = true
5857
    ): array {
5858
        // init
5859 945
        $array = [];
5860
5861 945
        if ($convertAllArrayyElements) {
5862 2
            foreach ($this->getGenerator() as $key => $value) {
5863 2
                if ($value instanceof self) {
5864 1
                    $value = $value->toArray(true);
5865
                }
5866
5867 2
                if ($preserveKeys) {
5868 1
                    $array[$key] = $value;
5869
                } else {
5870 1
                    $array[] = $value;
5871
                }
5872
            }
5873
        } else {
5874 945
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5875
        }
5876
5877 945
        return $array;
5878
    }
5879
5880
    /**
5881
     * Get the current array from the "Arrayy"-object as list.
5882
     *
5883
     * @param bool $convertAllArrayyElements <p>
5884
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5885
     *                                       </p>
5886
     *
5887
     * @return array
5888
     *
5889
     * @psalm-return list<array<TKey,T>>
5890
     * @psalm-mutation-free
5891
     */
5892 1
    public function toList(bool $convertAllArrayyElements = false): array
5893
    {
5894 1
        return $this->toArray(
5895 1
            $convertAllArrayyElements,
5896 1
            false
5897
        );
5898
    }
5899
5900
    /**
5901
     * Convert the current array to JSON.
5902
     *
5903
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
5904
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
5905
     *
5906
     * @return string
5907
     */
5908 12
    public function toJson(int $options = 0, int $depth = 512): string
5909
    {
5910 12
        $return = \json_encode($this->toArray(), $options, $depth);
5911 12
        if ($return === false) {
5912
            return '';
5913
        }
5914
5915 12
        return $return;
5916
    }
5917
5918
    /**
5919
     * @param string[]|null $items  [optional]
5920
     * @param string[]      $helper [optional]
5921
     *
5922
     * @return static|static[]
5923
     *
5924
     * @psalm-return static<int, static<TKey,T>>
5925
     */
5926 1
    public function toPermutation(array $items = null, array $helper = []): self
5927
    {
5928
        // init
5929 1
        $return = [];
5930
5931 1
        if ($items === null) {
5932 1
            $items = $this->toArray();
5933
        }
5934
5935 1
        if (empty($items)) {
5936 1
            $return[] = $helper;
5937
        } else {
5938 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5939 1
                $new_items = $items;
5940 1
                $new_helper = $helper;
5941 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5942
                /** @noinspection PhpSillyAssignmentInspection */
5943
                /** @var string[] $new_items */
5944 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...
5945 1
                \array_unshift($new_helper, $tmp_helper);
5946
                /** @noinspection SlowArrayOperationsInLoopInspection */
5947 1
                $return = \array_merge(
5948 1
                    $return,
5949 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5950
                );
5951
            }
5952
        }
5953
5954 1
        return static::create(
5955 1
            $return,
5956 1
            $this->iteratorClass,
5957 1
            false
5958
        );
5959
    }
5960
5961
    /**
5962
     * Implodes array to a string with specified separator.
5963
     *
5964
     * @param string $separator [optional] <p>The element's separator.</p>
5965
     *
5966
     * @return string
5967
     *                <p>The string representation of array, separated by ",".</p>
5968
     */
5969 19
    public function toString(string $separator = ','): string
5970
    {
5971 19
        return $this->implode($separator);
5972
    }
5973
5974
    /**
5975
     * Return a duplicate free copy of the current array.
5976
     *
5977
     * @return $this
5978
     *               <p>(Mutable)</p>
5979
     *
5980
     * @psalm-return static<TKey,T>
5981
     */
5982 13
    public function unique(): self
5983
    {
5984
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5985
5986
        /**
5987
         * @psalm-suppress MissingClosureReturnType
5988
         * @psalm-suppress MissingClosureParamType
5989
         */
5990 13
        $this->array = $this->reduce(
5991
            static function ($resultArray, $value) {
5992 12
                if (!\in_array($value, $resultArray, true)) {
5993 12
                    $resultArray[] = $value;
5994
                }
5995
5996 12
                return $resultArray;
5997 13
            },
5998 13
            []
5999 13
        )->toArray();
6000 13
        $this->generator = null;
6001
6002 13
        return $this;
6003
    }
6004
6005
    /**
6006
     * Return a duplicate free copy of the current array. (with the old keys)
6007
     *
6008
     * @return $this
6009
     *               <p>(Mutable)</p>
6010
     *
6011
     * @psalm-return static<TKey,T>
6012
     */
6013 11
    public function uniqueKeepIndex(): self
6014
    {
6015
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6016
6017
        // init
6018 11
        $array = $this->toArray();
6019
6020
        /**
6021
         * @psalm-suppress MissingClosureReturnType
6022
         * @psalm-suppress MissingClosureParamType
6023
         */
6024 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...
6025 11
            \array_keys($array),
6026
            static function ($resultArray, $key) use ($array) {
6027 10
                if (!\in_array($array[$key], $resultArray, true)) {
6028 10
                    $resultArray[$key] = $array[$key];
6029
                }
6030
6031 10
                return $resultArray;
6032 11
            },
6033 11
            []
6034
        );
6035 11
        $this->generator = null;
6036
6037 11
        return $this;
6038
    }
6039
6040
    /**
6041
     * alias: for "Arrayy->unique()"
6042
     *
6043
     * @return static
6044
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6045
     *
6046
     * @see          Arrayy::unique()
6047
     *
6048
     * @psalm-return static<TKey,T>
6049
     */
6050 10
    public function uniqueNewIndex(): self
6051
    {
6052 10
        return $this->unique();
6053
    }
6054
6055
    /**
6056
     * Prepends one or more values to the beginning of array at once.
6057
     *
6058
     * @param array ...$args
6059
     *
6060
     * @return $this
6061
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6062
     *
6063
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
6064
     * @psalm-return static<TKey,T>
6065
     */
6066 4
    public function unshift(...$args): self
6067
    {
6068 4
        $this->generatorToArray();
6069
6070 4
        \array_unshift($this->array, ...$args);
6071
6072 4
        return $this;
6073
    }
6074
6075
    /**
6076
     * Tests whether the given closure return something valid for all elements of this array.
6077
     *
6078
     * @param \Closure $closure the predicate
6079
     *
6080
     * @return bool
6081
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6082
     */
6083 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...
6084
    {
6085 1
        foreach ($this->getGenerator() as $key => $value) {
6086 1
            if (!$closure($value, $key)) {
6087 1
                return false;
6088
            }
6089
        }
6090
6091 1
        return true;
6092
    }
6093
6094
    /**
6095
     * Get all values from a array.
6096
     *
6097
     * @return static
6098
     *                <p>(Immutable)</p>
6099
     *
6100
     * @psalm-return static<TKey,T>
6101
     * @psalm-mutation-free
6102
     */
6103 2
    public function values(): self
6104
    {
6105 2
        return static::create(
6106
            function () {
6107
                /** @noinspection YieldFromCanBeUsedInspection */
6108 2
                foreach ($this->getGenerator() as $value) {
6109 2
                    yield $value;
6110
                }
6111 2
            },
6112 2
            $this->iteratorClass,
6113 2
            false
6114
        );
6115
    }
6116
6117
    /**
6118
     * Apply the given function to every element in the array, discarding the results.
6119
     *
6120
     * @param callable $callable
6121
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6122
     * @param mixed    $userData  [optional] <p>
6123
     *                            If the optional $userData parameter is supplied,
6124
     *                            it will be passed as the third parameter to the $callable.
6125
     *                            </p>
6126
     *
6127
     * @return $this
6128
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6129
     *
6130
     * @psalm-return static<TKey,T>
6131
     */
6132 12
    public function walk($callable, bool $recursive = false, $userData = self::ARRAYY_HELPER_WALK): self
6133
    {
6134 12
        $this->generatorToArray();
6135
6136 12
        if ($this->array !== []) {
6137 10
            if ($recursive === true) {
6138 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6139
                    \array_walk_recursive($this->array, $callable, $userData);
6140
                } else {
6141 5
                    \array_walk_recursive($this->array, $callable);
6142
                }
6143
            } else {
6144
                /** @noinspection NestedPositiveIfStatementsInspection */
6145 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6146
                    \array_walk($this->array, $callable, $userData);
6147
                } else {
6148 5
                    \array_walk($this->array, $callable);
6149
                }
6150
            }
6151
        }
6152
6153 12
        return $this;
6154
    }
6155
6156
    /**
6157
     * Returns a collection of matching items.
6158
     *
6159
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
6160
     * @param mixed  $value                 the value to match
6161
     *
6162
     * @throws \InvalidArgumentException if property or method is not defined
6163
     *
6164
     * @return static
6165
     *
6166
     * @psalm-param  T $value
6167
     * @psalm-return static<TKey,T>
6168
     */
6169 1
    public function where(string $keyOrPropertyOrMethod, $value): self
6170
    {
6171 1
        return $this->filter(
6172
            function ($item) use ($keyOrPropertyOrMethod, $value) {
6173 1
                $accessorValue = $this->extractValue(
6174 1
                    $item,
6175 1
                    $keyOrPropertyOrMethod
6176
                );
6177
6178 1
                return $accessorValue === $value;
6179 1
            }
6180
        );
6181
    }
6182
6183
    /**
6184
     * Convert an array into a object.
6185
     *
6186
     * @param array $array
6187
     *
6188
     * @return \stdClass
6189
     *
6190
     * @psalm-param array<mixed,mixed> $array
6191
     */
6192 4
    final protected static function arrayToObject(array $array = []): \stdClass
6193
    {
6194
        // init
6195 4
        $object = new \stdClass();
6196
6197 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6198 1
            return $object;
6199
        }
6200
6201 3
        foreach ($array as $name => $value) {
6202 3
            if (\is_array($value) === true) {
6203 1
                $object->{$name} = static::arrayToObject($value);
6204
            } else {
6205 3
                $object->{$name} = $value;
6206
            }
6207
        }
6208
6209 3
        return $object;
6210
    }
6211
6212
    /**
6213
     * @param array|\Generator|null $input         <p>
6214
     *                                             An array containing keys to return.
6215
     *                                             </p>
6216
     * @param mixed|null            $search_values [optional] <p>
6217
     *                                             If specified, then only keys containing these values are returned.
6218
     *                                             </p>
6219
     * @param bool                  $strict        [optional] <p>
6220
     *                                             Determines if strict comparison (===) should be used during the
6221
     *                                             search.
6222
     *                                             </p>
6223
     *
6224
     * @return array
6225
     *               <p>an array of all the keys in input</p>
6226
     *
6227
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6228
     * @psalm-return array<TKey|mixed>
6229
     * @psalm-mutation-free
6230
     */
6231 11
    protected function array_keys_recursive(
6232
        $input = null,
6233
        $search_values = null,
6234
        bool $strict = true
6235
    ): array {
6236
        // init
6237 11
        $keys = [];
6238 11
        $keysTmp = [];
6239
6240 11
        if ($input === null) {
6241 4
            $input = $this->getGenerator();
6242
        }
6243
6244 11
        if ($search_values === null) {
6245 11
            foreach ($input as $key => $value) {
6246 11
                $keys[] = $key;
6247
6248
                // check if recursive is needed
6249 11
                if (\is_array($value) === true) {
6250 4
                    $keysTmp[] = $this->array_keys_recursive($value);
6251
                }
6252
            }
6253
        } else {
6254 1
            $is_array_tmp = \is_array($search_values);
6255
6256 1
            foreach ($input as $key => $value) {
6257 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...
6258
                    (
6259 1
                        $is_array_tmp === false
6260
                        &&
6261 1
                        $strict === true
6262
                        &&
6263 1
                        $search_values === $value
6264
                    )
6265
                    ||
6266
                    (
6267 1
                        $is_array_tmp === false
6268
                        &&
6269 1
                        $strict === false
6270
                        &&
6271 1
                        $search_values == $value
6272
                    )
6273
                    ||
6274
                    (
6275 1
                        $is_array_tmp === true
6276
                        &&
6277 1
                        \in_array($value, $search_values, $strict)
6278
                    )
6279
                ) {
6280 1
                    $keys[] = $key;
6281
                }
6282
6283
                // check if recursive is needed
6284 1
                if (\is_array($value) === true) {
6285 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6286
                }
6287
            }
6288
        }
6289
6290 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6291
    }
6292
6293
    /**
6294
     * @param mixed      $path
6295
     * @param callable   $callable
6296
     * @param array|null $currentOffset
6297
     *
6298
     * @return void
6299
     *
6300
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6301
     * @psalm-mutation-free
6302
     */
6303 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6304
    {
6305 10
        $this->generatorToArray();
6306
6307 10
        if ($currentOffset === null) {
6308 10
            $currentOffset = &$this->array;
6309
        }
6310
6311 10
        $explodedPath = \explode($this->pathSeparator, $path);
6312 10
        if ($explodedPath === false) {
6313
            return;
6314
        }
6315
6316 10
        $nextPath = \array_shift($explodedPath);
6317
6318 10
        if (!isset($currentOffset[$nextPath])) {
6319 1
            return;
6320
        }
6321
6322 9
        if (!empty($explodedPath)) {
6323 1
            $this->callAtPath(
6324 1
                \implode($this->pathSeparator, $explodedPath),
6325 1
                $callable,
6326 1
                $currentOffset[$nextPath]
6327
            );
6328
        } else {
6329 9
            $callable($currentOffset[$nextPath]);
6330
        }
6331 9
    }
6332
6333
    /**
6334
     * Extracts the value of the given property or method from the object.
6335
     *
6336
     * @param static $object                <p>The object to extract the value from.</p>
6337
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
6338
     *                                      value should be extracted.</p>
6339
     *
6340
     * @throws \InvalidArgumentException if the method or property is not defined
6341
     *
6342
     * @return mixed
6343
     *               <p>The value extracted from the specified property or method.</p>
6344
     *
6345
     * @psalm-param self<TKey,T> $object
6346
     */
6347 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
6348
    {
6349 2
        if (isset($object[$keyOrPropertyOrMethod])) {
6350 2
            $return = $object->get($keyOrPropertyOrMethod);
6351
6352 2
            if ($return instanceof self) {
6353 1
                return $return->toArray();
6354
            }
6355
6356 1
            return $return;
6357
        }
6358
6359
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
6360
            return $object->{$keyOrPropertyOrMethod};
6361
        }
6362
6363
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
6364
            return $object->{$keyOrPropertyOrMethod}();
6365
        }
6366
6367
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6368
    }
6369
6370
    /**
6371
     * create a fallback for array
6372
     *
6373
     * 1. use the current array, if it's a array
6374
     * 2. fallback to empty array, if there is nothing
6375
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6376
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6377
     * 5. call "__toArray()" on object, if the method exists
6378
     * 6. cast a string or object with "__toString()" into an array
6379
     * 7. throw a "InvalidArgumentException"-Exception
6380
     *
6381
     * @param mixed $data
6382
     *
6383
     * @throws \InvalidArgumentException
6384
     *
6385
     * @return array
6386
     *
6387
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6388
     */
6389 1198
    protected function fallbackForArray(&$data): array
6390
    {
6391 1198
        $data = $this->internalGetArray($data);
6392
6393 1198
        if ($data === null) {
6394 2
            throw new \InvalidArgumentException('Passed value should be a array');
6395
        }
6396
6397 1196
        return $data;
6398
    }
6399
6400
    /**
6401
     * @param bool $preserveKeys <p>
6402
     *                           e.g.: A generator maybe return the same key more then once,
6403
     *                           so maybe you will ignore the keys.
6404
     *                           </p>
6405
     *
6406
     * @return bool
6407
     *
6408
     * @noinspection ReturnTypeCanBeDeclaredInspection
6409
     * @psalm-mutation-free :/
6410
     */
6411 1110
    protected function generatorToArray(bool $preserveKeys = true)
6412
    {
6413 1110
        if ($this->generator) {
6414 2
            $this->array = $this->toArray(false, $preserveKeys);
6415 2
            $this->generator = null;
6416
6417 2
            return true;
6418
        }
6419
6420 1110
        return false;
6421
    }
6422
6423
    /**
6424
     * Get correct PHP constant for direction.
6425
     *
6426
     * @param int|string $direction
6427
     *
6428
     * @return int
6429
     * @psalm-mutation-free
6430
     */
6431 43
    protected function getDirection($direction): int
6432
    {
6433 43
        if ((string) $direction === $direction) {
6434 10
            $direction = \strtolower($direction);
6435
6436 10
            if ($direction === 'desc') {
6437 2
                $direction = \SORT_DESC;
6438
            } else {
6439 8
                $direction = \SORT_ASC;
6440
            }
6441
        }
6442
6443
        if (
6444 43
            $direction !== \SORT_DESC
6445
            &&
6446 43
            $direction !== \SORT_ASC
6447
        ) {
6448
            $direction = \SORT_ASC;
6449
        }
6450
6451 43
        return $direction;
6452
    }
6453
6454
    /**
6455
     * @return TypeCheckInterface[]
6456
     *
6457
     * @noinspection ReturnTypeCanBeDeclaredInspection
6458
     */
6459 22
    protected function getPropertiesFromPhpDoc()
6460
    {
6461 22
        static $PROPERTY_CACHE = [];
6462 22
        $cacheKey = 'Class::' . static::class;
6463
6464 22
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6465 21
            return $PROPERTY_CACHE[$cacheKey];
6466
        }
6467
6468
        // init
6469 3
        $properties = [];
6470
6471 3
        $reflector = new \ReflectionClass($this);
6472 3
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6473 3
        $docComment = $reflector->getDocComment();
6474 3
        if ($docComment) {
6475 2
            $docblock = $factory->create($docComment);
6476
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6477 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6478 2
                $typeName = $tag->getVariableName();
6479
                /** @var null|string $typeName */
6480 2
                if ($typeName !== null) {
6481 2
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
6482 2
                    if ($typeCheckPhpDoc !== null) {
6483 2
                        $properties[$typeName] = $typeCheckPhpDoc;
6484
                    }
6485
                }
6486
            }
6487
        }
6488
6489 3
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6490
    }
6491
6492
    /**
6493
     * @param mixed $glue
6494
     * @param mixed $pieces
6495
     * @param bool  $useKeys
6496
     *
6497
     * @return string
6498
     * @psalm-mutation-free
6499
     */
6500 36
    protected function implode_recursive(
6501
        $glue = '',
6502
        $pieces = [],
6503
        bool $useKeys = false
6504
    ): string {
6505 36
        if ($pieces instanceof self) {
6506 1
            $pieces = $pieces->toArray();
6507
        }
6508
6509 36
        if (\is_array($pieces) === true) {
6510 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6511 36
            $pieces_count_not_zero = $pieces_count > 0;
6512
6513 36
            return \implode(
6514 36
                $glue,
6515 36
                \array_map(
6516 36
                    [$this, 'implode_recursive'],
6517 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6518 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6519
                )
6520
            );
6521
        }
6522
6523
        if (
6524 36
            \is_scalar($pieces) === true
6525
            ||
6526 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6527
        ) {
6528 32
            return (string) $pieces;
6529
        }
6530
6531 8
        return '';
6532
    }
6533
6534
    /**
6535
     * @param mixed                 $needle   <p>
6536
     *                                        The searched value.
6537
     *                                        </p>
6538
     *                                        <p>
6539
     *                                        If needle is a string, the comparison is done
6540
     *                                        in a case-sensitive manner.
6541
     *                                        </p>
6542
     * @param array|\Generator|null $haystack <p>
6543
     *                                        The array.
6544
     *                                        </p>
6545
     * @param bool                  $strict   [optional] <p>
6546
     *                                        If the third parameter strict is set to true
6547
     *                                        then the in_array function will also check the
6548
     *                                        types of the
6549
     *                                        needle in the haystack.
6550
     *                                        </p>
6551
     *
6552
     * @return bool
6553
     *              <p>true if needle is found in the array, false otherwise</p>
6554
     *
6555
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6556
     * @psalm-mutation-free
6557
     */
6558 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6559
    {
6560 18
        if ($haystack === null) {
6561
            $haystack = $this->getGenerator();
6562
        }
6563
6564 18
        foreach ($haystack as $item) {
6565 14
            if (\is_array($item) === true) {
6566 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6567
            } else {
6568
                /** @noinspection NestedPositiveIfStatementsInspection */
6569 14
                if ($strict === true) {
6570 14
                    $returnTmp = $item === $needle;
6571
                } else {
6572
                    $returnTmp = $item == $needle;
6573
                }
6574
            }
6575
6576 14
            if ($returnTmp === true) {
6577 10
                return true;
6578
            }
6579
        }
6580
6581 8
        return false;
6582
    }
6583
6584
    /**
6585
     * @param mixed $data
6586
     *
6587
     * @return array|null
6588
     *
6589
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6590
     */
6591 1198
    protected function internalGetArray(&$data)
6592
    {
6593 1198
        if (\is_array($data) === true) {
6594 1192
            return $data;
6595
        }
6596
6597 58
        if (!$data) {
6598 6
            return [];
6599
        }
6600
6601 57
        if (\is_object($data) === true) {
6602 51
            if ($data instanceof \ArrayObject) {
6603 5
                return $data->getArrayCopy();
6604
            }
6605
6606 47
            if ($data instanceof \Generator) {
6607
                return static::createFromGeneratorImmutable($data)->toArray();
6608
            }
6609
6610 47
            if ($data instanceof \Traversable) {
6611
                return static::createFromObject($data)->toArray();
6612
            }
6613
6614 47
            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...
6615
                return (array) $data->jsonSerialize();
6616
            }
6617
6618 47
            if (\method_exists($data, '__toArray')) {
6619
                return (array) $data->__toArray();
6620
            }
6621
6622 47
            if (\method_exists($data, '__toString')) {
6623
                return [(string) $data];
6624
            }
6625
        }
6626
6627 53
        if (\is_callable($data)) {
6628
            /**
6629
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6630
             */
6631 45
            $this->generator = new ArrayyRewindableGenerator($data);
6632
6633 45
            return [];
6634
        }
6635
6636 10
        if (\is_scalar($data)) {
6637 8
            return [$data];
6638
        }
6639
6640 2
        return null;
6641
    }
6642
6643
    /**
6644
     * Internal mechanics of remove method.
6645
     *
6646
     * @param mixed $key
6647
     *
6648
     * @return bool
6649
     */
6650 21
    protected function internalRemove($key): bool
6651
    {
6652 21
        $this->generatorToArray();
6653
6654
        if (
6655 21
            $this->pathSeparator
6656
            &&
6657 21
            (string) $key === $key
6658
            &&
6659 21
            \strpos($key, $this->pathSeparator) !== false
6660
        ) {
6661
            $path = \explode($this->pathSeparator, (string) $key);
6662
6663
            if ($path !== false) {
6664
                // crawl though the keys
6665
                while (\count($path, \COUNT_NORMAL) > 1) {
6666
                    $key = \array_shift($path);
6667
6668
                    if (!$this->has($key)) {
6669
                        return false;
6670
                    }
6671
6672
                    $this->array = &$this->array[$key];
6673
                }
6674
6675
                $key = \array_shift($path);
6676
            }
6677
        }
6678
6679 21
        unset($this->array[$key]);
6680
6681 21
        return true;
6682
    }
6683
6684
    /**
6685
     * Internal mechanic of set method.
6686
     *
6687
     * @param int|string|null $key
6688
     * @param mixed           $value
6689
     * @param bool            $checkProperties
6690
     *
6691
     * @return bool
6692
     */
6693 1048
    protected function internalSet(
6694
        $key,
6695
        &$value,
6696
        bool $checkProperties = true
6697
    ): bool {
6698
        if (
6699 1048
            $checkProperties === true
6700
            &&
6701 1048
            $this->properties !== []
6702
        ) {
6703 109
            $this->checkType($key, $value);
6704
        }
6705
6706 1046
        if ($key === null) {
6707
            return false;
6708
        }
6709
6710 1046
        $this->generatorToArray();
6711
6712
        /** @psalm-var array<int|string,mixed> $array */
6713 1046
        $array = &$this->array;
6714
6715
        /**
6716
         * https://github.com/vimeo/psalm/issues/2536
6717
         *
6718
         * @psalm-suppress PossiblyInvalidArgument
6719
         * @psalm-suppress InvalidScalarArgument
6720
         */
6721
        if (
6722 1046
            $this->pathSeparator
6723
            &&
6724 1046
            (string) $key === $key
6725
            &&
6726 1046
            \strpos($key, $this->pathSeparator) !== false
6727
        ) {
6728 9
            $path = \explode($this->pathSeparator, (string) $key);
6729
6730 9
            if ($path !== false) {
6731
                // crawl through the keys
6732 9
                while (\count($path, \COUNT_NORMAL) > 1) {
6733 9
                    $key = \array_shift($path);
6734
6735 9
                    $array = &$array[$key];
6736
                }
6737
6738 9
                $key = \array_shift($path);
6739
            }
6740
        }
6741
6742 1046
        if ($array === null) {
6743 4
            $array = [];
6744 1043
        } elseif (!\is_array($array)) {
6745 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
6746
        }
6747
6748 1046
        $array[$key] = $value;
6749
6750 1046
        return true;
6751
    }
6752
6753
    /**
6754
     * Convert a object into an array.
6755
     *
6756
     * @param mixed|object $object
6757
     *
6758
     * @return array|mixed
6759
     *
6760
     * @psalm-mutation-free
6761
     */
6762 5
    protected static function objectToArray($object)
6763
    {
6764 5
        if (!\is_object($object)) {
6765 4
            return $object;
6766
        }
6767
6768 5
        $object = \get_object_vars($object);
6769
6770
        /**
6771
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6772
         */
6773 5
        return \array_map(['static', 'objectToArray'], $object);
6774
    }
6775
6776
    /**
6777
     * @param array $data
6778
     * @param bool  $checkPropertiesInConstructor
6779
     *
6780
     * @return void
6781
     *
6782
     * @psalm-param array<mixed,T> $data
6783
     */
6784 1196
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6785
    {
6786 1196
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6787
                                        &&
6788 1196
                                        $checkPropertiesInConstructor === true;
6789
6790 1196
        if ($this->properties !== []) {
6791 98
            foreach ($data as $key => &$valueInner) {
6792 98
                $this->internalSet(
6793 98
                    $key,
6794 98
                    $valueInner,
6795 98
                    $checkPropertiesInConstructor
6796
                );
6797
            }
6798
        } else {
6799
            if (
6800 1117
                $this->checkPropertyTypes === true
6801
                ||
6802 1117
                $checkPropertiesInConstructor === true
6803
            ) {
6804 21
                $this->properties = $this->getPropertiesFromPhpDoc();
6805
            }
6806
6807
            /** @var TypeCheckInterface[] $properties */
6808 1117
            $properties = $this->properties;
6809
6810
            if (
6811 1117
                $this->checkPropertiesMismatchInConstructor === true
6812
                &&
6813 1117
                \count($data) !== 0
6814
                &&
6815 1117
                \count(\array_diff_key($properties, $data)) > 0
6816
            ) {
6817 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...
6818
            }
6819
6820 1116
            foreach ($data as $key => &$valueInner) {
6821 945
                $this->internalSet(
6822 945
                    $key,
6823 945
                    $valueInner,
6824 945
                    $checkPropertiesInConstructor
6825
                );
6826
            }
6827
        }
6828 1189
    }
6829
6830
    /**
6831
     * sorting keys
6832
     *
6833
     * @param array      $elements
6834
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6835
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6836
     *                              <strong>SORT_NATURAL</strong></p>
6837
     *
6838
     * @return $this
6839
     *               <p>(Mutable) Return this Arrayy object.</p>
6840
     *
6841
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6842
     * @psalm-return static<TKey,T>
6843
     */
6844 18
    protected function sorterKeys(
6845
        array &$elements,
6846
        $direction = \SORT_ASC,
6847
        int $strategy = \SORT_REGULAR
6848
    ): self {
6849 18
        $direction = $this->getDirection($direction);
6850
6851 18
        switch ($direction) {
6852 18
            case 'desc':
6853
            case \SORT_DESC:
6854 6
                \krsort($elements, $strategy);
6855
6856 6
                break;
6857 13
            case 'asc':
6858 13
            case \SORT_ASC:
6859
            default:
6860 13
                \ksort($elements, $strategy);
6861
        }
6862
6863 18
        return $this;
6864
    }
6865
6866
    /**
6867
     * @param array      $elements  <p>Warning: used as reference</p>
6868
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6869
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6870
     *                              <strong>SORT_NATURAL</strong></p>
6871
     * @param bool       $keepKeys
6872
     *
6873
     * @return $this
6874
     *               <p>(Mutable) Return this Arrayy object.</p>
6875
     *
6876
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6877
     * @psalm-return static<TKey,T>
6878
     */
6879 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6880
    {
6881 24
        $direction = $this->getDirection($direction);
6882
6883 24
        if (!$strategy) {
6884 24
            $strategy = \SORT_REGULAR;
6885
        }
6886
6887 24
        switch ($direction) {
6888 24
            case 'desc':
6889
            case \SORT_DESC:
6890 13
                if ($keepKeys) {
6891 9
                    \arsort($elements, $strategy);
6892
                } else {
6893 4
                    \rsort($elements, $strategy);
6894
                }
6895
6896 13
                break;
6897 11
            case 'asc':
6898 11
            case \SORT_ASC:
6899
            default:
6900 11
                if ($keepKeys) {
6901 4
                    \asort($elements, $strategy);
6902
                } else {
6903 7
                    \sort($elements, $strategy);
6904
                }
6905
        }
6906
6907 24
        return $this;
6908
    }
6909
6910
    /**
6911
     * @param array $array
6912
     *
6913
     * @return array
6914
     *
6915
     * @psalm-mutation-free
6916
     */
6917 25
    private function getArrayRecursiveHelperArrayy(array $array)
6918
    {
6919 25
        if ($array === []) {
6920
            return [];
6921
        }
6922
6923 25
        \array_walk_recursive(
6924 25
            $array,
6925
            /**
6926
             * @param array|self $item
6927
             *
6928
             * @return void
6929
             */
6930
            static function (&$item) {
6931 25
                if ($item instanceof self) {
6932 1
                    $item = $item->getArray();
6933
                }
6934 25
            }
6935
        );
6936
6937 25
        return $array;
6938
    }
6939
6940
    /**
6941
     * @param int|string|null $key
6942
     * @param mixed           $value
6943
     *
6944
     * @return void
6945
     */
6946 109
    private function checkType($key, $value)
6947
    {
6948
        if (
6949 109
            $key !== null
6950
            &&
6951 109
            isset($this->properties[$key]) === false
6952
            &&
6953 109
            $this->checkPropertiesMismatch === true
6954
        ) {
6955
            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...
6956
        }
6957
6958 109
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6959 96
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6960 22
        } elseif ($key !== null && isset($this->properties[$key])) {
6961 22
            $this->properties[$key]->checkType($value);
6962
        }
6963 107
    }
6964
}
6965