Completed
Push — master ( 58ff94...55c956 )
by Lars
01:47
created

Arrayy::chunk()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 8
ccs 5
cts 5
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
                foreach ($values as $value) {
951 1
                    $this->array[$key] = $value;
952
                }
953
            }
954
        } else {
955
            foreach ($values as $value) {
956
                $this->array[] = $value;
957
            }
958
        }
959
960 1
        return $this;
961
    }
962
963
    /**
964
     * Add a suffix to each key.
965
     *
966
     * @param mixed $prefix
967
     *
968
     * @return static
969
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
970
     *
971
     * @psalm-return static<TKey,T>
972
     * @psalm-mutation-free
973
     */
974 10 View Code Duplication
    public function appendToEachKey($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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