Completed
Push — master ( c9d7a9...2ca203 )
by Lars
02:12 queued 24s
created

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
1006
    {
1007
        // init
1008 10
        $result = [];
1009
1010 10
        foreach ($this->getGenerator() as $key => $item) {
1011 9
            if ($item instanceof self) {
1012
                $result[$key] = $item->appendToEachValue($prefix);
1013 9
            } elseif (\is_array($item) === true) {
1014
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1015 9
            } elseif (\is_object($item) === true) {
1016 1
                $result[$key] = $item;
1017
            } else {
1018 9
                $result[$key] = $prefix . $item;
1019
            }
1020
        }
1021
1022 10
        return self::create($result, $this->iteratorClass, false);
1023
    }
1024
1025
    /**
1026
     * Sort an array in reverse order and maintain index association.
1027
     *
1028
     * @return $this
1029
     *               <p>(Mutable) Return this Arrayy object.</p>
1030
     *
1031
     * @psalm-return static<TKey,T>
1032
     */
1033 4
    public function arsort(): self
1034
    {
1035 4
        $this->generatorToArray();
1036
1037 4
        \arsort($this->array);
1038
1039 4
        return $this;
1040
    }
1041
1042
    /**
1043
     * Sort an array in reverse order and maintain index association.
1044
     *
1045
     * @return $this
1046
     *               <p>(Immutable) Return this Arrayy object.</p>
1047
     *
1048
     * @psalm-return static<TKey,T>
1049
     * @psalm-mutation-free
1050
     */
1051 10
    public function arsortImmutable(): self
1052
    {
1053 10
        $that = clone $this;
1054
1055 10
        $that->generatorToArray();
1056
1057 10
        \arsort($that->array);
1058
1059 10
        return $that;
1060
    }
1061
1062
    /**
1063
     * Iterate over the current array and execute a callback for each loop.
1064
     *
1065
     * EXAMPLE: <code>
1066
     * $result = A::create();
1067
     * $closure = function ($value, $key) use ($result) {
1068
     *     $result[$key] = ':' . $value . ':';
1069
     * };
1070
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1071
     * </code>
1072
     *
1073
     * @param \Closure $closure
1074
     *
1075
     * @return static
1076
     *                <p>(Immutable)</p>
1077
     *
1078
     * @psalm-return static<TKey,T>
1079
     * @psalm-mutation-free
1080
     */
1081 3
    public function at(\Closure $closure): self
1082
    {
1083 3
        $that = clone $this;
1084
1085 3
        foreach ($that->getGenerator() as $key => $value) {
1086 3
            $closure($value, $key);
1087
        }
1088
1089 3
        return static::create(
1090 3
            $that->toArray(),
1091 3
            $this->iteratorClass,
1092 3
            false
1093
        );
1094
    }
1095
1096
    /**
1097
     * Returns the average value of the current array.
1098
     *
1099
     * EXAMPLE: <code>
1100
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1101
     * </code>
1102
     *
1103
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1104
     *
1105
     * @return float|int
1106
     *                   <p>The average value.</p>
1107
     * @psalm-mutation-free
1108
     */
1109 10
    public function average($decimals = 0)
1110
    {
1111 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1112
1113 10
        if (!$count) {
1114 2
            return 0;
1115
        }
1116
1117 8
        if ((int) $decimals !== $decimals) {
1118 3
            $decimals = 0;
1119
        }
1120
1121 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1122
    }
1123
1124
    /**
1125
     * Changes all keys in an array.
1126
     *
1127
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1128
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1129
     *
1130
     * @return static
1131
     *                <p>(Immutable)</p>
1132
     *
1133
     * @psalm-return static<TKey,T>
1134
     * @psalm-mutation-free
1135
     */
1136 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1137
    {
1138
        if (
1139 1
            $case !== \CASE_LOWER
1140
            &&
1141 1
            $case !== \CASE_UPPER
1142
        ) {
1143
            $case = \CASE_LOWER;
1144
        }
1145
1146 1
        $return = [];
1147 1
        foreach ($this->getGenerator() as $key => $value) {
1148 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1149
1150 1
            if ($case === \CASE_LOWER) {
1151 1
                $key = \mb_strtolower((string) $key);
1152
            } else {
1153 1
                $key = \mb_strtoupper((string) $key);
1154
            }
1155
1156 1
            $return[$key] = $value;
1157
        }
1158
1159 1
        return static::create(
1160 1
            $return,
1161 1
            $this->iteratorClass,
1162 1
            false
1163
        );
1164
    }
1165
1166
    /**
1167
     * Change the path separator of the array wrapper.
1168
     *
1169
     * By default, the separator is: "."
1170
     *
1171
     * @param string $separator <p>Separator to set.</p>
1172
     *
1173
     * @return $this
1174
     *               <p>(Mutable) Return this Arrayy object.</p>
1175
     *
1176
     * @psalm-return static<TKey,T>
1177
     */
1178 11
    public function changeSeparator($separator): self
1179
    {
1180 11
        $this->pathSeparator = $separator;
1181
1182 11
        return $this;
1183
    }
1184
1185
    /**
1186
     * Create a chunked version of the current array.
1187
     *
1188
     * EXAMPLE: <code>
1189
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1190
     * </code>
1191
     *
1192
     * @param int  $size         <p>Size of each chunk.</p>
1193
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1194
     *
1195
     * @return static
1196
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1197
     *
1198
     * @psalm-return static<TKey,T>
1199
     * @psalm-mutation-free
1200
     */
1201 5
    public function chunk($size, $preserveKeys = false): self
1202
    {
1203 5
        return static::create(
1204 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1205 5
            $this->iteratorClass,
1206 5
            false
1207
        );
1208
    }
1209
1210
    /**
1211
     * Clean all falsy values from the current array.
1212
     *
1213
     * EXAMPLE: <code>
1214
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1215
     * </code>
1216
     *
1217
     * @return static
1218
     *                <p>(Immutable)</p>
1219
     *
1220
     * @psalm-return static<TKey,T>
1221
     * @psalm-mutation-free
1222
     */
1223 8
    public function clean(): self
1224
    {
1225 8
        return $this->filter(
1226 8
            static function ($value) {
1227 7
                return (bool) $value;
1228 8
            }
1229
        );
1230
    }
1231
1232
    /**
1233
     * WARNING!!! -> Clear the current full array or a $key of it.
1234
     *
1235
     * EXAMPLE: <code>
1236
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1237
     * </code>
1238
     *
1239
     * @param int|int[]|string|string[]|null $key
1240
     *
1241
     * @return $this
1242
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1243
     *
1244
     * @psalm-return static<TKey,T>
1245
     */
1246 10
    public function clear($key = null): self
1247
    {
1248 10
        if ($key !== null) {
1249 3
            if (\is_array($key)) {
1250 1
                foreach ($key as $keyTmp) {
1251 1
                    $this->offsetUnset($keyTmp);
1252
                }
1253
            } else {
1254 2
                $this->offsetUnset($key);
1255
            }
1256
1257 3
            return $this;
1258
        }
1259
1260 7
        $this->array = [];
1261 7
        $this->generator = null;
1262
1263 7
        return $this;
1264
    }
1265
1266
    /**
1267
     * Check if an item is in the current array.
1268
     *
1269
     * EXAMPLE: <code>
1270
     * a([1, true])->contains(true); // true
1271
     * </code>
1272
     *
1273
     * @param float|int|string $value
1274
     * @param bool             $recursive
1275
     * @param bool             $strict
1276
     *
1277
     * @return bool
1278
     * @psalm-mutation-free
1279
     */
1280 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1281
    {
1282 23
        if ($recursive === true) {
1283 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1284
        }
1285
1286
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1287 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1288 11
            if ($strict) {
1289 11
                if ($value === $valueFromArray) {
1290 11
                    return true;
1291
                }
1292
            } else {
1293
                /** @noinspection NestedPositiveIfStatementsInspection */
1294
                if ($value == $valueFromArray) {
1295 7
                    return true;
1296
                }
1297
            }
1298
        }
1299
1300 7
        return false;
1301
    }
1302
1303
    /**
1304
     * Check if an (case-insensitive) string is in the current array.
1305
     *
1306
     * EXAMPLE: <code>
1307
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1308
     * </code>
1309
     *
1310
     * @param mixed $value
1311
     * @param bool  $recursive
1312
     *
1313
     * @return bool
1314
     * @psalm-mutation-free
1315
     *
1316
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1317
     */
1318 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1319
    {
1320 26
        if ($value === null) {
1321 2
            return false;
1322
        }
1323
1324 24
        if ($recursive === true) {
1325
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1326 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1327 22
                if (\is_array($valueTmp) === true) {
1328 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1329 5
                    if ($return === true) {
1330 5
                        return $return;
1331
                    }
1332 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1333 22
                    return true;
1334
                }
1335
            }
1336
1337 8
            return false;
1338
        }
1339
1340
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1341 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1342 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1343 11
                return true;
1344
            }
1345
        }
1346
1347 4
        return false;
1348
    }
1349
1350
    /**
1351
     * Check if the given key/index exists in the array.
1352
     *
1353
     * EXAMPLE: <code>
1354
     * a([1 => true])->containsKey(1); // true
1355
     * </code>
1356
     *
1357
     * @param int|string $key <p>key/index to search for</p>
1358
     *
1359
     * @return bool
1360
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1361
     *
1362
     * @psalm-mutation-free
1363
     */
1364 4
    public function containsKey($key): bool
1365
    {
1366 4
        return $this->offsetExists($key);
1367
    }
1368
1369
    /**
1370
     * Check if all given needles are present in the array as key/index.
1371
     *
1372
     * EXAMPLE: <code>
1373
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1374
     * </code>
1375
     *
1376
     * @param array $needles   <p>The keys you are searching for.</p>
1377
     * @param bool  $recursive
1378
     *
1379
     * @return bool
1380
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1381
     *
1382
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1383
     * @psalm-mutation-free
1384
     */
1385 2
    public function containsKeys(array $needles, $recursive = false): bool
1386
    {
1387 2
        if ($recursive === true) {
1388
            return
1389 2
                \count(
1390 2
                    \array_intersect(
1391 2
                        $needles,
1392 2
                        $this->keys(true)->toArray()
1393
                    ),
1394 2
                    \COUNT_RECURSIVE
1395
                )
1396
                ===
1397 2
                \count(
1398 2
                    $needles,
1399 2
                    \COUNT_RECURSIVE
1400
                );
1401
        }
1402
1403 1
        return \count(
1404 1
            \array_intersect($needles, $this->keys()->toArray()),
1405 1
            \COUNT_NORMAL
1406
        )
1407
               ===
1408 1
               \count(
1409 1
                   $needles,
1410 1
                   \COUNT_NORMAL
1411
               );
1412
    }
1413
1414
    /**
1415
     * Check if all given needles are present in the array as key/index.
1416
     *
1417
     * @param array $needles <p>The keys you are searching for.</p>
1418
     *
1419
     * @return bool
1420
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1421
     *
1422
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1423
     * @psalm-mutation-free
1424
     */
1425 1
    public function containsKeysRecursive(array $needles): bool
1426
    {
1427 1
        return $this->containsKeys($needles, true);
1428
    }
1429
1430
    /**
1431
     * alias: for "Arrayy->contains()"
1432
     *
1433
     * @param float|int|string $value
1434
     *
1435
     * @return bool
1436
     *
1437
     * @see Arrayy::contains()
1438
     * @psalm-mutation-free
1439
     */
1440 9
    public function containsValue($value): bool
1441
    {
1442 9
        return $this->contains($value);
1443
    }
1444
1445
    /**
1446
     * alias: for "Arrayy->contains($value, true)"
1447
     *
1448
     * @param float|int|string $value
1449
     *
1450
     * @return bool
1451
     *
1452
     * @see Arrayy::contains()
1453
     * @psalm-mutation-free
1454
     */
1455 18
    public function containsValueRecursive($value): bool
1456
    {
1457 18
        return $this->contains($value, true);
1458
    }
1459
1460
    /**
1461
     * Check if all given needles are present in the array.
1462
     *
1463
     * EXAMPLE: <code>
1464
     * a([1, true])->containsValues(array(1, true)); // true
1465
     * </code>
1466
     *
1467
     * @param array $needles
1468
     *
1469
     * @return bool
1470
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1471
     *
1472
     * @psalm-param array<mixed>|array<T> $needles
1473
     * @psalm-mutation-free
1474
     */
1475 1
    public function containsValues(array $needles): bool
1476
    {
1477 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1478
               ===
1479 1
               \count($needles, \COUNT_NORMAL);
1480
    }
1481
1482
    /**
1483
     * Counts all the values of an array
1484
     *
1485
     * @see          http://php.net/manual/en/function.array-count-values.php
1486
     *
1487
     * @return static
1488
     *                <p>
1489
     *                (Immutable)
1490
     *                An associative Arrayy-object of values from input as
1491
     *                keys and their count as value.
1492
     *                </p>
1493
     *
1494
     * @psalm-return static<TKey,T>
1495
     * @psalm-mutation-free
1496
     */
1497 7
    public function countValues(): self
1498
    {
1499 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1500
    }
1501
1502
    /**
1503
     * Creates an Arrayy object.
1504
     *
1505
     * @param mixed  $data
1506
     * @param string $iteratorClass
1507
     * @param bool   $checkPropertiesInConstructor
1508
     *
1509
     * @return static
1510
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1511
     *
1512
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1513
     *
1514
     * @psalm-mutation-free
1515
     */
1516 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 10
            static function (&$val) {
1729 10
                if ((string) $val === $val) {
1730 10
                    $val = \trim($val);
1731
                }
1732 10
            }
1733
        );
1734
1735 10
        return static::create($array);
1736
    }
1737
1738
    /**
1739
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1740
     *
1741
     * @param \Traversable $traversable
1742
     *
1743
     * @return static
1744
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1745
     *
1746
     * @psalm-param \Traversable<array-key,mixed> $traversable
1747
     *
1748
     * @psalm-mutation-free
1749
     */
1750 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1751
    {
1752 1
        return self::create(\iterator_to_array($traversable, true));
1753
    }
1754
1755
    /**
1756
     * Create an new instance containing a range of elements.
1757
     *
1758
     * @param float|int|string $low  <p>First value of the sequence.</p>
1759
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1760
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1761
     *
1762
     * @return static
1763
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1764
     *
1765
     * @psalm-mutation-free
1766
     */
1767 2
    public static function createWithRange($low, $high, $step = 1): self
1768
    {
1769 2
        return static::create(\range($low, $high, $step));
1770
    }
1771
1772
    /**
1773
     * Gets the element of the array at the current internal iterator position.
1774
     *
1775
     * @return false|mixed
1776
     */
1777
    public function current()
1778
    {
1779
        return \current($this->array);
1780
    }
1781
1782
    /**
1783
     * Custom sort by index via "uksort".
1784
     *
1785
     * EXAMPLE: <code>
1786
     * $callable = function ($a, $b) {
1787
     *     if ($a == $b) {
1788
     *         return 0;
1789
     *     }
1790
     *     return ($a > $b) ? 1 : -1;
1791
     * };
1792
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1793
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1794
     * </code>
1795
     *
1796
     * @see          http://php.net/manual/en/function.uksort.php
1797
     *
1798
     * @param callable $function
1799
     *
1800
     * @throws \InvalidArgumentException
1801
     *
1802
     * @return $this
1803
     *               <p>(Mutable) Return this Arrayy object.</p>
1804
     *
1805
     * @psalm-return static<TKey,T>
1806
     */
1807 5
    public function customSortKeys(callable $function): self
1808
    {
1809 5
        $this->generatorToArray();
1810
1811 5
        \uksort($this->array, $function);
1812
1813 5
        return $this;
1814
    }
1815
1816
    /**
1817
     * Custom sort by index via "uksort".
1818
     *
1819
     * @see          http://php.net/manual/en/function.uksort.php
1820
     *
1821
     * @param callable $function
1822
     *
1823
     * @throws \InvalidArgumentException
1824
     *
1825
     * @return $this
1826
     *               <p>(Immutable) Return this Arrayy object.</p>
1827
     *
1828
     * @psalm-return static<TKey,T>
1829
     * @psalm-mutation-free
1830
     */
1831 1
    public function customSortKeysImmutable(callable $function): self
1832
    {
1833 1
        $that = clone $this;
1834
1835 1
        $that->generatorToArray();
1836
1837
        /**
1838
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1839
         */
1840 1
        \uksort($that->array, $function);
1841
1842 1
        return $that;
1843
    }
1844
1845
    /**
1846
     * Custom sort by value via "usort".
1847
     *
1848
     * EXAMPLE: <code>
1849
     * $callable = function ($a, $b) {
1850
     *     if ($a == $b) {
1851
     *         return 0;
1852
     *     }
1853
     *     return ($a > $b) ? 1 : -1;
1854
     * };
1855
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1856
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
1857
     * </code>
1858
     *
1859
     * @see          http://php.net/manual/en/function.usort.php
1860
     *
1861
     * @param callable $function
1862
     *
1863
     * @throws \InvalidArgumentException
1864
     *
1865
     * @return $this
1866
     *               <p>(Mutable) Return this Arrayy object.</p>
1867
     *
1868
     * @psalm-return static<TKey,T>
1869
     */
1870 10 View Code Duplication
    public function customSortValues($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
2439
    {
2440 3
        $arrayTmp = $this->keys()->toArray();
2441
2442 3
        if ($number === null) {
2443
            $array = (array) \array_shift($arrayTmp);
2444
        } else {
2445 3
            $array = \array_splice($arrayTmp, 0, $number);
2446
        }
2447
2448 3
        return static::create(
2449 3
            $array,
2450 3
            $this->iteratorClass,
2451 3
            false
2452
        );
2453
    }
2454
2455
    /**
2456
     * Get and rmove the first value(s) from the current array.
2457
     * And will return an empty array if there was no first entry.
2458
     *
2459
     * @param int|null $number <p>How many values you will take?</p>
2460
     *
2461
     * @return $this
2462
     *               <p>(Mutable)</p>
2463
     *
2464
     * @psalm-return static<TKey,T>
2465
     */
2466 34
    public function firstsMutable(int $number = null): self
2467
    {
2468 34
        $this->generatorToArray();
2469
2470 34
        if ($number === null) {
2471 19
            $this->array = (array) \array_shift($this->array);
2472
        } else {
2473 15
            $this->array = \array_splice($this->array, 0, $number);
2474
        }
2475
2476 34
        return $this;
2477
    }
2478
2479
    /**
2480
     * Exchanges all keys with their associated values in an array.
2481
     *
2482
     * @return static
2483
     *                <p>(Immutable)</p>
2484
     *
2485
     * @psalm-return static<TKey,T>
2486
     * @psalm-mutation-free
2487
     */
2488 1
    public function flip(): self
2489
    {
2490 1
        return static::create(
2491 1
            \array_flip($this->toArray()),
2492 1
            $this->iteratorClass,
2493 1
            false
2494
        );
2495
    }
2496
2497
    /**
2498
     * Get a value from an array (optional using dot-notation).
2499
     *
2500
     * @param mixed $key            <p>The key to look for.</p>
2501
     * @param mixed $fallback       <p>Value to fallback to.</p>
2502
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2503
     *                              class.</p>
2504
     * @param bool  $useByReference
2505
     *
2506
     * @return mixed|static
2507
     *
2508
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2509
     * @psalm-mutation-free
2510
     */
2511 242
    public function get(
2512
        $key,
2513
        $fallback = null,
2514
        array $array = null,
2515
        bool $useByReference = false
2516
    ) {
2517 242
        if ($array !== null) {
2518 4
            if ($useByReference) {
2519
                $usedArray = &$array;
2520
            } else {
2521 4
                $usedArray = $array;
2522
            }
2523
        } else {
2524 239
            $this->generatorToArray();
2525
2526 239
            if ($useByReference) {
2527 126
                $usedArray = &$this->array;
2528
            } else {
2529 132
                $usedArray = $this->array;
2530
            }
2531
        }
2532
2533 242
        if ($key === null) {
2534 1
            return static::create(
2535 1
                [],
2536 1
                $this->iteratorClass,
2537 1
                false
2538 1
            )->createByReference($usedArray);
2539
        }
2540
2541
        // php cast "bool"-index into "int"-index
2542 242
        if ((bool) $key === $key) {
2543 3
            $key = (int) $key;
2544
        }
2545
2546 242
        if (\array_key_exists($key, $usedArray) === true) {
2547 204 View Code Duplication
            if (\is_array($usedArray[$key]) === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
6585
                return (array) $data->jsonSerialize();
6586
            }
6587
6588 45
            if (\method_exists($data, '__toArray')) {
6589
                return (array) $data->__toArray();
6590
            }
6591
6592 45
            if (\method_exists($data, '__toString')) {
6593
                return [(string) $data];
6594
            }
6595
        }
6596
6597 51
        if (\is_callable($data)) {
6598
            /**
6599
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6600
             */
6601 43
            $this->generator = new ArrayyRewindableGenerator($data);
6602
6603 43
            return [];
6604
        }
6605
6606 10
        if (\is_scalar($data)) {
6607 8
            return [$data];
6608
        }
6609
6610 2
        return null;
6611
    }
6612
6613
    /**
6614
     * Internal mechanics of remove method.
6615
     *
6616
     * @param mixed $key
6617
     *
6618
     * @return bool
6619
     */
6620 21
    protected function internalRemove($key): bool
6621
    {
6622 21
        $this->generatorToArray();
6623
6624
        if (
6625 21
            $this->pathSeparator
6626
            &&
6627 21
            (string) $key === $key
6628
            &&
6629 21
            \strpos($key, $this->pathSeparator) !== false
6630
        ) {
6631
            $path = \explode($this->pathSeparator, (string) $key);
6632
6633
            if ($path !== false) {
6634
                // crawl though the keys
6635
                while (\count($path, \COUNT_NORMAL) > 1) {
6636
                    $key = \array_shift($path);
6637
6638
                    if (!$this->has($key)) {
6639
                        return false;
6640
                    }
6641
6642
                    $this->array = &$this->array[$key];
6643
                }
6644
6645
                $key = \array_shift($path);
6646
            }
6647
        }
6648
6649 21
        unset($this->array[$key]);
6650
6651 21
        return true;
6652
    }
6653
6654
    /**
6655
     * Internal mechanic of set method.
6656
     *
6657
     * @param int|string|null $key
6658
     * @param mixed           $value
6659
     * @param bool            $checkProperties
6660
     *
6661
     * @return bool
6662
     */
6663 1048
    protected function internalSet(
6664
        $key,
6665
        &$value,
6666
        bool $checkProperties = true
6667
    ): bool {
6668
        if (
6669 1048
            $checkProperties === true
6670
            &&
6671 1048
            $this->properties !== []
6672
        ) {
6673 109
            $this->checkType($key, $value);
6674
        }
6675
6676 1046
        if ($key === null) {
6677
            return false;
6678
        }
6679
6680 1046
        $this->generatorToArray();
6681
6682
        /** @psalm-var array<int|string,mixed> $array */
6683 1046
        $array = &$this->array;
6684
6685
        /**
6686
         * https://github.com/vimeo/psalm/issues/2536
6687
         *
6688
         * @psalm-suppress PossiblyInvalidArgument
6689
         * @psalm-suppress InvalidScalarArgument
6690
         */
6691
        if (
6692 1046
            $this->pathSeparator
6693
            &&
6694 1046
            (string) $key === $key
6695
            &&
6696 1046
            \strpos($key, $this->pathSeparator) !== false
6697
        ) {
6698 9
            $path = \explode($this->pathSeparator, (string) $key);
6699
6700 9
            if ($path !== false) {
6701
                // crawl through the keys
6702 9
                while (\count($path, \COUNT_NORMAL) > 1) {
6703 9
                    $key = \array_shift($path);
6704
6705 9
                    $array = &$array[$key];
6706
                }
6707
6708 9
                $key = \array_shift($path);
6709
            }
6710
        }
6711
6712 1046
        if ($array === null) {
6713 4
            $array = [];
6714 1043
        } elseif (!\is_array($array)) {
6715 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
6716
        }
6717
6718 1046
        $array[$key] = $value;
6719
6720 1046
        return true;
6721
    }
6722
6723
    /**
6724
     * Convert a object into an array.
6725
     *
6726
     * @param mixed|object $object
6727
     *
6728
     * @return array|mixed
6729
     *
6730
     * @psalm-mutation-free
6731
     */
6732 5
    protected static function objectToArray($object)
6733
    {
6734 5
        if (!\is_object($object)) {
6735 4
            return $object;
6736
        }
6737
6738 5
        $object = \get_object_vars($object);
6739
6740
        /**
6741
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6742
         */
6743 5
        return \array_map(['static', 'objectToArray'], $object);
6744
    }
6745
6746
    /**
6747
     * @param array $data
6748
     * @param bool  $checkPropertiesInConstructor
6749
     *
6750
     * @return void
6751
     *
6752
     * @psalm-param array<mixed,T> $data
6753
     */
6754 1196
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6755
    {
6756 1196
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6757
                                        &&
6758 1196
                                        $checkPropertiesInConstructor === true;
6759
6760 1196
        if ($this->properties !== []) {
6761 98
            foreach ($data as $key => &$valueInner) {
6762 98
                $this->internalSet(
6763 98
                    $key,
6764 98
                    $valueInner,
6765 98
                    $checkPropertiesInConstructor
6766
                );
6767
            }
6768
        } else {
6769
            if (
6770 1117
                $this->checkPropertyTypes === true
6771
                ||
6772 1117
                $checkPropertiesInConstructor === true
6773
            ) {
6774 21
                $this->properties = $this->getPropertiesFromPhpDoc();
6775
            }
6776
6777
            /** @var TypeCheckInterface[] $properties */
6778 1117
            $properties = $this->properties;
6779
6780
            if (
6781 1117
                $this->checkPropertiesMismatchInConstructor === true
6782
                &&
6783 1117
                \count($data) !== 0
6784
                &&
6785 1117
                \count(\array_diff_key($properties, $data)) > 0
6786
            ) {
6787 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...
6788
            }
6789
6790 1116
            foreach ($data as $key => &$valueInner) {
6791 945
                $this->internalSet(
6792 945
                    $key,
6793 945
                    $valueInner,
6794 945
                    $checkPropertiesInConstructor
6795
                );
6796
            }
6797
        }
6798 1189
    }
6799
6800
    /**
6801
     * sorting keys
6802
     *
6803
     * @param array      $elements
6804
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6805
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6806
     *                              <strong>SORT_NATURAL</strong></p>
6807
     *
6808
     * @return $this
6809
     *               <p>(Mutable) Return this Arrayy object.</p>
6810
     *
6811
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6812
     * @psalm-return static<TKey,T>
6813
     */
6814 18
    protected function sorterKeys(
6815
        array &$elements,
6816
        $direction = \SORT_ASC,
6817
        int $strategy = \SORT_REGULAR
6818
    ): self {
6819 18
        $direction = $this->getDirection($direction);
6820
6821
        switch ($direction) {
6822 18
            case 'desc':
6823 18
            case \SORT_DESC:
6824 6
                \krsort($elements, $strategy);
6825
6826 6
                break;
6827 13
            case 'asc':
6828 13
            case \SORT_ASC:
6829
            default:
6830 13
                \ksort($elements, $strategy);
6831
        }
6832
6833 18
        return $this;
6834
    }
6835
6836
    /**
6837
     * @param array      $elements  <p>Warning: used as reference</p>
6838
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6839
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6840
     *                              <strong>SORT_NATURAL</strong></p>
6841
     * @param bool       $keepKeys
6842
     *
6843
     * @return $this
6844
     *               <p>(Mutable) Return this Arrayy object.</p>
6845
     *
6846
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6847
     * @psalm-return static<TKey,T>
6848
     */
6849 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6850
    {
6851 24
        $direction = $this->getDirection($direction);
6852
6853 24
        if (!$strategy) {
6854 24
            $strategy = \SORT_REGULAR;
6855
        }
6856
6857
        switch ($direction) {
6858 24
            case 'desc':
6859 24
            case \SORT_DESC:
6860 13
                if ($keepKeys) {
6861 9
                    \arsort($elements, $strategy);
6862
                } else {
6863 4
                    \rsort($elements, $strategy);
6864
                }
6865
6866 13
                break;
6867 11
            case 'asc':
6868 11
            case \SORT_ASC:
6869
            default:
6870 11
                if ($keepKeys) {
6871 4
                    \asort($elements, $strategy);
6872
                } else {
6873 7
                    \sort($elements, $strategy);
6874
                }
6875
        }
6876
6877 24
        return $this;
6878
    }
6879
6880
    /**
6881
     * @param array $array
6882
     *
6883
     * @return array
6884
     *
6885
     * @psalm-mutation-free
6886
     */
6887 25
    private function getArrayRecursiveHelperArrayy(array $array)
6888
    {
6889 25
        if ($array === []) {
6890
            return [];
6891
        }
6892
6893 25
        \array_walk_recursive(
6894 25
            $array,
6895
            /**
6896
             * @param array|self $item
6897
             *
6898
             * @return void
6899
             */
6900 25
            static function (&$item) {
6901 25
                if ($item instanceof self) {
6902 1
                    $item = $item->getArray();
6903
                }
6904 25
            }
6905
        );
6906
6907 25
        return $array;
6908
    }
6909
6910
    /**
6911
     * @param int|string|null $key
6912
     * @param mixed           $value
6913
     *
6914
     * @return void
6915
     */
6916 109
    private function checkType($key, $value)
6917
    {
6918
        if (
6919 109
            $key !== null
6920
            &&
6921 109
            isset($this->properties[$key]) === false
6922
            &&
6923 109
            $this->checkPropertiesMismatch === true
6924
        ) {
6925
            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...
6926
        }
6927
6928 109
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6929 96
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6930 22
        } elseif ($key !== null && isset($this->properties[$key])) {
6931 22
            $this->properties[$key]->checkType($value);
6932
        }
6933 107
    }
6934
}
6935