Completed
Push — master ( 5bf09e...d88aef )
by Lars
01:39
created

Arrayy::unshift()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 8
ccs 4
cts 4
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
    /**
32
     * @var array
33
     *
34
     * @psalm-var array<mixed,mixed>|array<TKey,T>
35
     */
36
    protected $array = [];
37
38
    /**
39
     * @var \Arrayy\ArrayyRewindableGenerator|null
40
     *
41
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
42
     */
43
    protected $generator;
44
45
    /**
46
     * @var string
47
     *
48
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
49
     */
50
    protected $iteratorClass = ArrayyIterator::class;
51
52
    /**
53
     * @var string
54
     */
55
    protected $pathSeparator = '.';
56
57
    /**
58
     * @var bool
59
     */
60
    protected $checkPropertyTypes = false;
61
62
    /**
63
     * @var bool
64
     */
65
    protected $checkForMissingPropertiesInConstructor = false;
66
67
    /**
68
     * @var bool
69
     */
70
    protected $checkPropertiesMismatchInConstructor = false;
71
72
    /**
73
     * @var bool
74
     */
75
    protected $checkPropertiesMismatch = true;
76
77
    /**
78
     * @var array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
79
     */
80
    protected $properties = [];
81
82
    /**
83
     * Initializes
84
     *
85
     * @param mixed  $data                         <p>
86
     *                                             Should be an array or a generator, otherwise it will try
87
     *                                             to convert it into an array.
88
     *                                             </p>
89
     * @param string $iteratorClass                optional <p>
90
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
91
     *                                             need this option.
92
     *                                             </p>
93
     * @param bool   $checkPropertiesInConstructor optional <p>
94
     *                                             You need to extend the "Arrayy"-class and you need to set
95
     *                                             the $checkPropertiesMismatchInConstructor class property
96
     *                                             to
97
     *                                             true, otherwise this option didn't not work anyway.
98
     *                                             </p>
99
     *
100
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
101
     */
102 1196
    public function __construct(
103
        $data = [],
104
        string $iteratorClass = ArrayyIterator::class,
105
        bool $checkPropertiesInConstructor = true
106
    ) {
107 1196
        $data = $this->fallbackForArray($data);
108
109
        // used only for serialize + unserialize, all other methods are overwritten
110
        /**
111
         * @psalm-suppress InvalidArgument - why?
112
         */
113 1194
        parent::__construct([], 0, $iteratorClass);
114
115 1194
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
116
117 1187
        $this->setIteratorClass($iteratorClass);
118 1187
    }
119
120
    /**
121
     * @return void
122
     */
123 50
    public function __clone()
124
    {
125 50
        if (!\is_array($this->properties)) {
126
            $this->properties = clone $this->properties;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->properties of type object is incompatible with the declared type array of property $properties.

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

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

Loading history...
127
        }
128
129 50
        if ($this->generator !== null) {
130
            $this->generator = clone $this->generator;
131
        }
132 50
    }
133
134
    /**
135
     * Call object as function.
136
     *
137
     * @param mixed $key
138
     *
139
     * @return mixed
140
     */
141 1
    public function __invoke($key = null)
142
    {
143 1
        if ($key !== null) {
144 1
            $this->generatorToArray();
145
146 1
            return $this->array[$key] ?? false;
147
        }
148
149
        return $this->toArray();
150
    }
151
152
    /**
153
     * Whether or not an element exists by key.
154
     *
155
     * @param mixed $key
156
     *
157
     * @return bool
158
     *              <p>True is the key/index exists, otherwise false.</p>
159
     */
160
    public function __isset($key): bool
161
    {
162
        return $this->offsetExists($key);
163
    }
164
165
    /**
166
     * Assigns a value to the specified element.
167
     *
168
     * @param mixed $key
169
     * @param mixed $value
170
     *
171
     * @return void
172
     */
173 3
    public function __set($key, $value)
174
    {
175 3
        $this->internalSet($key, $value);
176 3
    }
177
178
    /**
179
     * magic to string
180
     *
181
     * @return string
182
     */
183 15
    public function __toString(): string
184
    {
185 15
        return $this->toString();
186
    }
187
188
    /**
189
     * Unset element by key.
190
     *
191
     * @param mixed $key
192
     */
193
    public function __unset($key)
194
    {
195
        $this->internalRemove($key);
196
    }
197
198
    /**
199
     * Get a value by key.
200
     *
201
     * @param mixed $key
202
     *
203
     * @return mixed
204
     *               <p>Get a Value from the current array.</p>
205
     */
206 8
    public function &__get($key)
207
    {
208 8
        $return = $this->get($key);
209
210 8
        if (\is_array($return) === true) {
211
            return static::create($return, $this->iteratorClass, false);
212
        }
213
214 8
        return $return;
215
    }
216
217
    /**
218
     * Add new values (optional using dot-notation).
219
     *
220
     * @param mixed           $value
221
     * @param int|string|null $key
222
     *
223
     * @return static
224
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
225
     *
226
     * @psalm-param  T $value
227
     * @psalm-return static<TKey,T>
228
     */
229 12
    public function add($value, $key = null)
230
    {
231 12
        if ($key !== null) {
232 5
            $get = $this[$key];
233 5
            if ($get !== null) {
234 1
                $value = \array_merge_recursive(
235 1
                    !$get instanceof self ? [$get] : $get->getArray(),
236 1
                    !\is_array($value) ? [$value] : $value
237
                );
238
            }
239
240 5
            $this->internalSet($key, $value);
241
242 4
            return $this;
243
        }
244
245 7
        return $this->append($value);
246
    }
247
248
    /**
249
     * Append a (key) + value to the current array.
250
     *
251
     * @param mixed $value
252
     * @param mixed $key
253
     *
254
     * @return $this
255
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
256
     *
257
     * @psalm-return static<TKey,T>
258
     */
259 19
    public function append($value, $key = null): self
260
    {
261 19
        $this->generatorToArray();
262
263 19
        if ($this->properties !== []) {
264 6
            $this->checkType($key, $value);
265
        }
266
267 18
        if ($key !== null) {
268
            if (
269 2
                isset($this->array[$key])
270
                &&
271 2
                \is_array($this->array[$key]) === true
272
            ) {
273
                $this->array[$key][] = $value;
274
            } else {
275 2
                $this->array[$key] = $value;
276
            }
277
        } else {
278 16
            $this->array[] = $value;
279
        }
280
281 18
        return $this;
282
    }
283
284
    /**
285
     * Sort the entries by value.
286
     *
287
     * @param int $sort_flags [optional] <p>
288
     *                        You may modify the behavior of the sort using the optional
289
     *                        parameter sort_flags, for details
290
     *                        see sort.
291
     *                        </p>
292
     *
293
     * @return $this
294
     *               <p>(Mutable) Return this Arrayy object.</p>
295
     *
296
     * @psalm-return static<TKey,T>
297
     */
298 4
    public function asort(int $sort_flags = 0): self
299
    {
300 4
        $this->generatorToArray();
301
302 4
        \asort($this->array, $sort_flags);
303
304 4
        return $this;
305
    }
306
307
    /**
308
     * Sort the entries by value.
309
     *
310
     * @param int $sort_flags [optional] <p>
311
     *                        You may modify the behavior of the sort using the optional
312
     *                        parameter sort_flags, for details
313
     *                        see sort.
314
     *                        </p>
315
     *
316
     * @return $this
317
     *               <p>(Immutable) Return this Arrayy object.</p>
318
     *
319
     * @psalm-return static<TKey,T>
320
     * @psalm-mutation-free
321
     */
322 4
    public function asortImmutable(int $sort_flags = 0): self
323
    {
324 4
        $that = clone $this;
325
326
        /**
327
         * @psalm-suppress ImpureMethodCall - object is already cloned
328
         */
329 4
        $that->asort($sort_flags);
330
331 4
        return $that;
332
    }
333
334
    /**
335
     * Counts all elements in an array, or something in an object.
336
     *
337
     * <p>
338
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
339
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
340
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
341
     * implemented and used in PHP.
342
     * </p>
343
     *
344
     * @see http://php.net/manual/en/function.count.php
345
     *
346
     * @param int $mode [optional] If the optional mode parameter is set to
347
     *                  COUNT_RECURSIVE (or 1), count
348
     *                  will recursively count the array. This is particularly useful for
349
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
350
     *
351
     * @return int
352
     *             <p>
353
     *             The number of elements in var, which is
354
     *             typically an array, since anything else will have one
355
     *             element.
356
     *             </p>
357
     *             <p>
358
     *             If var is not an array or an object with
359
     *             implemented Countable interface,
360
     *             1 will be returned.
361
     *             There is one exception, if var is &null;,
362
     *             0 will be returned.
363
     *             </p>
364
     *             <p>
365
     *             Caution: count may return 0 for a variable that isn't set,
366
     *             but it may also return 0 for a variable that has been initialized with an
367
     *             empty array. Use isset to test if a variable is set.
368
     *             </p>
369
     * @psalm-mutation-free
370
     */
371 147
    public function count(int $mode = \COUNT_NORMAL): int
372
    {
373
        if (
374 147
            $this->generator
375
            &&
376 147
            $mode === \COUNT_NORMAL
377
        ) {
378 4
            return \iterator_count($this->generator);
379
        }
380
381 143
        return \count($this->toArray(), $mode);
382
    }
383
384
    /**
385
     * Exchange the array for another one.
386
     *
387
     * @param array|static $data
388
     *
389
     * @return array
390
     *
391
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
392
     * @psalm-return array<mixed,mixed>|array<TKey,T>
393
     */
394 1
    public function exchangeArray($data): array
395
    {
396 1
        $this->array = $this->fallbackForArray($data);
397
398 1
        return $this->array;
399
    }
400
401
    /**
402
     * Creates a copy of the ArrayyObject.
403
     *
404
     * @return array
405
     *
406
     * @psalm-return array<mixed,mixed>|array<TKey,T>
407
     */
408 6
    public function getArrayCopy(): array
409
    {
410 6
        $this->generatorToArray();
411
412 6
        return $this->array;
413
    }
414
415
    /**
416
     * Returns a new iterator, thus implementing the \Iterator interface.
417
     *
418
     * @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...
419
     *                          <p>An iterator for the values in the array.</p>
420
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
421
     */
422 27
    public function getIterator(): \Iterator
423
    {
424 27
        if ($this->generator instanceof ArrayyRewindableGenerator) {
425 1
            return $this->generator;
426
        }
427
428 26
        $iterator = $this->getIteratorClass();
429
430 26
        if ($iterator === ArrayyIterator::class) {
431 26
            return new $iterator($this->toArray(), 0, static::class);
432
        }
433
434
        $return = new $iterator($this->toArray());
435
        \assert($return instanceof \Iterator);
436
437
        return $return;
438
    }
439
440
    /**
441
     * Gets the iterator classname for the ArrayObject.
442
     *
443
     * @return string
444
     *
445
     * @psalm-return class-string
446
     */
447 26
    public function getIteratorClass(): string
448
    {
449 26
        return $this->iteratorClass;
450
    }
451
452
    /**
453
     * Sort the entries by key.
454
     *
455
     * @param int $sort_flags [optional] <p>
456
     *                        You may modify the behavior of the sort using the optional
457
     *                        parameter sort_flags, for details
458
     *                        see sort.
459
     *                        </p>
460
     *
461
     * @return $this
462
     *               <p>(Mutable) Return this Arrayy object.</p>
463
     *
464
     * @psalm-return static<TKey,T>
465
     */
466 4
    public function ksort(int $sort_flags = 0): self
467
    {
468 4
        $this->generatorToArray();
469
470 4
        \ksort($this->array, $sort_flags);
471
472 4
        return $this;
473
    }
474
475
    /**
476
     * Sort the entries by key.
477
     *
478
     * @param int $sort_flags [optional] <p>
479
     *                        You may modify the behavior of the sort using the optional
480
     *                        parameter sort_flags, for details
481
     *                        see sort.
482
     *                        </p>
483
     *
484
     * @return $this
485
     *               <p>(Immutable) Return this Arrayy object.</p>
486
     *
487
     * @psalm-return static<TKey,T>
488
     */
489 4
    public function ksortImmutable(int $sort_flags = 0): self
490
    {
491 4
        $that = clone $this;
492
493
        /**
494
         * @psalm-suppress ImpureMethodCall - object is already cloned
495
         */
496 4
        $that->ksort($sort_flags);
497
498 4
        return $that;
499
    }
500
501
    /**
502
     * Sort an array using a case insensitive "natural order" algorithm.
503
     *
504
     * @return $this
505
     *               <p>(Mutable) Return this Arrayy object.</p>
506
     *
507
     * @psalm-return static<TKey,T>
508
     */
509 8
    public function natcasesort(): self
510
    {
511 8
        $this->generatorToArray();
512
513 8
        \natcasesort($this->array);
514
515 8
        return $this;
516
    }
517
518
    /**
519
     * Sort an array using a case insensitive "natural order" algorithm.
520
     *
521
     * @return $this
522
     *               <p>(Immutable) Return this Arrayy object.</p>
523
     *
524
     * @psalm-return static<TKey,T>
525
     * @psalm-mutation-free
526
     */
527 4
    public function natcasesortImmutable(): self
528
    {
529 4
        $that = clone $this;
530
531
        /**
532
         * @psalm-suppress ImpureMethodCall - object is already cloned
533
         */
534 4
        $that->natcasesort();
535
536 4
        return $that;
537
    }
538
539
    /**
540
     * Sort entries using a "natural order" algorithm.
541
     *
542
     * @return $this
543
     *               <p>(Mutable) Return this Arrayy object.</p>
544
     *
545
     * @psalm-return static<TKey,T>
546
     */
547 10
    public function natsort(): self
548
    {
549 10
        $this->generatorToArray();
550
551 10
        \natsort($this->array);
552
553 10
        return $this;
554
    }
555
556
    /**
557
     * Sort entries using a "natural order" algorithm.
558
     *
559
     * @return $this
560
     *               <p>(Immutable) Return this Arrayy object.</p>
561
     *
562
     * @psalm-return static<TKey,T>
563
     * @psalm-mutation-free
564
     */
565 4
    public function natsortImmutable(): self
566
    {
567 4
        $that = clone $this;
568
569
        /**
570
         * @psalm-suppress ImpureMethodCall - object is already cloned
571
         */
572 4
        $that->natsort();
573
574 4
        return $that;
575
    }
576
577
    /**
578
     * Whether or not an offset exists.
579
     *
580
     * @param bool|int|string $offset
581
     *
582
     * @return bool
583
     *
584
     * @noinspection PhpSillyAssignmentInspection
585
     *
586
     * @psalm-mutation-free
587
     */
588 157
    public function offsetExists($offset): bool
589
    {
590 157
        $this->generatorToArray();
591
592 157
        if ($this->array === []) {
593 11
            return false;
594
        }
595
596
        // php cast "bool"-index into "int"-index
597 151
        if ((bool) $offset === $offset) {
598 1
            $offset = (int) $offset;
599
        }
600
601
        /** @var int|string $offset - hint for phpstan */
602 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...
603
604 151
        $tmpReturn = $this->keyExists($offset);
605
606
        if (
607 151
            $tmpReturn === true
608
            ||
609 151
            \strpos((string) $offset, $this->pathSeparator) === false
610
        ) {
611 147
            return $tmpReturn;
612
        }
613
614 5
        $offsetExists = false;
615
616
        /**
617
         * https://github.com/vimeo/psalm/issues/2536
618
         *
619
         * @psalm-suppress PossiblyInvalidArgument
620
         * @psalm-suppress InvalidScalarArgument
621
         */
622 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...
623 5
            $this->pathSeparator
624
            &&
625 5
            (string) $offset === $offset
626
            &&
627 5
            \strpos($offset, $this->pathSeparator) !== false
628
        ) {
629 5
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
630 5
            if ($explodedPath !== false) {
631
                /** @var string $lastOffset - helper for phpstan */
632 5
                $lastOffset = \array_pop($explodedPath);
633 5
                $containerPath = \implode($this->pathSeparator, $explodedPath);
634
635
                /**
636
                 * @psalm-suppress MissingClosureReturnType
637
                 * @psalm-suppress MissingClosureParamType
638
                 */
639 5
                $this->callAtPath(
640 5
                    $containerPath,
641
                    static function ($container) use ($lastOffset, &$offsetExists) {
642 5
                        $offsetExists = \array_key_exists($lastOffset, $container);
643 5
                    }
644
                );
645
            }
646
        }
647
648 5
        return $offsetExists;
649
    }
650
651
    /**
652
     * Returns the value at specified offset.
653
     *
654
     * @param int|string $offset
655
     *
656
     * @return mixed
657
     *               <p>Will return null if the offset did not exists.</p>
658
     */
659 126
    public function offsetGet($offset)
660
    {
661 126
        return $this->offsetExists($offset) ? $this->get($offset) : null;
662
    }
663
664
    /**
665
     * Assigns a value to the specified offset + check the type.
666
     *
667
     * @param int|string|null $offset
668
     * @param mixed           $value
669
     *
670
     * @return void
671
     */
672 26
    public function offsetSet($offset, $value)
673
    {
674 26
        $this->generatorToArray();
675
676 26
        if ($offset === null) {
677 6
            if ($this->properties !== []) {
678 1
                $this->checkType(null, $value);
679
            }
680
681 5
            $this->array[] = $value;
682
        } else {
683 20
            $this->internalSet(
684 20
                $offset,
685 20
                $value,
686 20
                true
687
            );
688
        }
689 25
    }
690
691
    /**
692
     * Unset an offset.
693
     *
694
     * @param int|string $offset
695
     *
696
     * @return void
697
     *              <p>(Mutable) Return nothing.</p>
698
     */
699 25
    public function offsetUnset($offset)
700
    {
701 25
        $this->generatorToArray();
702
703 25
        if ($this->array === []) {
704 6
            return;
705
        }
706
707 20
        if ($this->keyExists($offset)) {
708 13
            unset($this->array[$offset]);
709
710 13
            return;
711
        }
712
713
        /**
714
         * https://github.com/vimeo/psalm/issues/2536
715
         *
716
         * @psalm-suppress PossiblyInvalidArgument
717
         * @psalm-suppress InvalidScalarArgument
718
         */
719 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...
720 10
            $this->pathSeparator
721
            &&
722 10
            (string) $offset === $offset
723
            &&
724 10
            \strpos($offset, $this->pathSeparator) !== false
725
        ) {
726 7
            $path = \explode($this->pathSeparator, (string) $offset);
727
728 7
            if ($path !== false) {
729 7
                $pathToUnset = \array_pop($path);
730
731
                /**
732
                 * @psalm-suppress MissingClosureReturnType
733
                 * @psalm-suppress MissingClosureParamType
734
                 */
735 7
                $this->callAtPath(
736 7
                    \implode($this->pathSeparator, $path),
737
                    static function (&$offset) use ($pathToUnset) {
738 6
                        if (\is_array($offset)) {
739 5
                            unset($offset[$pathToUnset]);
740
                        } else {
741 1
                            $offset = null;
742
                        }
743 7
                    }
744
                );
745
            }
746
        }
747
748 10
        unset($this->array[$offset]);
749 10
    }
750
751
    /**
752
     * Serialize the current "Arrayy"-object.
753
     *
754
     * @return string
755
     */
756 2
    public function serialize(): string
757
    {
758 2
        $this->generatorToArray();
759
760 2
        if (\PHP_VERSION_ID < 70400) {
761 2
            return parent::serialize();
762
        }
763
764
        return \serialize($this);
765
    }
766
767
    /**
768
     * Sets the iterator classname for the current "Arrayy"-object.
769
     *
770
     * @param string $iteratorClass
771
     *
772
     * @throws \InvalidArgumentException
773
     *
774
     * @return void
775
     *
776
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
777
     */
778 1187
    public function setIteratorClass($iteratorClass)
779
    {
780 1187
        if (\class_exists($iteratorClass)) {
781 1187
            $this->iteratorClass = $iteratorClass;
782
783 1187
            return;
784
        }
785
786
        if (\strpos($iteratorClass, '\\') === 0) {
787
            $iteratorClass = '\\' . $iteratorClass;
788
            if (\class_exists($iteratorClass)) {
789
                /**
790
                 * @psalm-suppress PropertyTypeCoercion
791
                 */
792
                $this->iteratorClass = $iteratorClass;
793
794
                return;
795
            }
796
        }
797
798
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
799
    }
800
801
    /**
802
     * Sort the entries with a user-defined comparison function and maintain key association.
803
     *
804
     * @param callable $function
805
     *
806
     * @throws \InvalidArgumentException
807
     *
808
     * @return $this
809
     *               <p>(Mutable) Return this Arrayy object.</p>
810
     *
811
     * @psalm-return static<TKey,T>
812
     */
813 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...
814
    {
815 8
        if (!\is_callable($function)) {
816
            throw new \InvalidArgumentException('Passed function must be callable');
817
        }
818
819 8
        $this->generatorToArray();
820
821 8
        \uasort($this->array, $function);
822
823 8
        return $this;
824
    }
825
826
    /**
827
     * Sort the entries with a user-defined comparison function and maintain key association.
828
     *
829
     * @param callable $function
830
     *
831
     * @throws \InvalidArgumentException
832
     *
833
     * @return $this
834
     *               <p>(Immutable) Return this Arrayy object.</p>
835
     *
836
     * @psalm-return static<TKey,T>
837
     * @psalm-mutation-free
838
     */
839 4
    public function uasortImmutable($function): self
840
    {
841 4
        $that = clone $this;
842
843
        /**
844
         * @psalm-suppress ImpureMethodCall - object is already cloned
845
         */
846 4
        $that->uasort($function);
847
848 4
        return $that;
849
    }
850
851
    /**
852
     * Sort the entries by keys using a user-defined comparison function.
853
     *
854
     * @param callable $function
855
     *
856
     * @throws \InvalidArgumentException
857
     *
858
     * @return static
859
     *                <p>(Mutable) Return this Arrayy object.</p>
860
     *
861
     * @psalm-return static<TKey,T>
862
     */
863 5
    public function uksort($function): self
864
    {
865 5
        return $this->customSortKeys($function);
866
    }
867
868
    /**
869
     * Sort the entries by keys using a user-defined comparison function.
870
     *
871
     * @param callable $function
872
     *
873
     * @throws \InvalidArgumentException
874
     *
875
     * @return static
876
     *                <p>(Immutable) Return this Arrayy object.</p>
877
     *
878
     * @psalm-return static<TKey,T>
879
     * @psalm-mutation-free
880
     */
881 1
    public function uksortImmutable($function): self
882
    {
883 1
        return $this->customSortKeysImmutable($function);
884
    }
885
886
    /**
887
     * Unserialize an string and return the instance of the "Arrayy"-class.
888
     *
889
     * @param string $string
890
     *
891
     * @return $this
892
     *
893
     * @psalm-return static<TKey,T>
894
     */
895 2
    public function unserialize($string): self
896
    {
897 2
        if (\PHP_VERSION_ID < 70400) {
898 2
            parent::unserialize($string);
899
900 2
            return $this;
901
        }
902
903
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
904
    }
905
906
    /**
907
     * Append a (key) + values to the current array.
908
     *
909
     * @param array $values
910
     * @param mixed $key
911
     *
912
     * @return $this
913
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
914
     *
915
     * @psalm-param  array<mixed,T> $values
916
     * @psalm-param  TKey|null $key
917
     * @psalm-return static<TKey,T>
918
     */
919 1
    public function appendArrayValues(array $values, $key = null)
920
    {
921 1
        $this->generatorToArray();
922
923 1
        if ($key !== null) {
924
            if (
925 1
                isset($this->array[$key])
926
                &&
927 1
                \is_array($this->array[$key]) === true
928
            ) {
929 1
                foreach ($values as $value) {
930 1
                    $this->array[$key][] = $value;
931
                }
932
            } else {
933
                foreach ($values as $value) {
934 1
                    $this->array[$key] = $value;
935
                }
936
            }
937
        } else {
938
            foreach ($values as $value) {
939
                $this->array[] = $value;
940
            }
941
        }
942
943 1
        return $this;
944
    }
945
946
    /**
947
     * Add a suffix to each key.
948
     *
949
     * @param mixed $prefix
950
     *
951
     * @return static
952
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
953
     *
954
     * @psalm-return static<TKey,T>
955
     * @psalm-mutation-free
956
     */
957 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...
958
    {
959
        // init
960 10
        $result = [];
961
962 10
        foreach ($this->getGenerator() as $key => $item) {
963 9
            if ($item instanceof self) {
964
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
965 9
            } elseif (\is_array($item) === true) {
966
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
967
                    ->appendToEachKey($prefix)
968
                    ->toArray();
969
            } else {
970 9
                $result[$prefix . $key] = $item;
971
            }
972
        }
973
974 10
        return self::create($result, $this->iteratorClass, false);
975
    }
976
977
    /**
978
     * Add a prefix to each value.
979
     *
980
     * @param mixed $prefix
981
     *
982
     * @return static
983
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
984
     *
985
     * @psalm-return static<TKey,T>
986
     * @psalm-mutation-free
987
     */
988 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...
989
    {
990
        // init
991 10
        $result = [];
992
993 10
        foreach ($this->getGenerator() as $key => $item) {
994 9
            if ($item instanceof self) {
995
                $result[$key] = $item->appendToEachValue($prefix);
996 9
            } elseif (\is_array($item) === true) {
997
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
998 9
            } elseif (\is_object($item) === true) {
999 1
                $result[$key] = $item;
1000
            } else {
1001 9
                $result[$key] = $prefix . $item;
1002
            }
1003
        }
1004
1005 10
        return self::create($result, $this->iteratorClass, false);
1006
    }
1007
1008
    /**
1009
     * Sort an array in reverse order and maintain index association.
1010
     *
1011
     * @return $this
1012
     *               <p>(Mutable) Return this Arrayy object.</p>
1013
     *
1014
     * @psalm-return static<TKey,T>
1015
     */
1016 4
    public function arsort(): self
1017
    {
1018 4
        $this->generatorToArray();
1019
1020 4
        \arsort($this->array);
1021
1022 4
        return $this;
1023
    }
1024
1025
    /**
1026
     * Sort an array in reverse order and maintain index association.
1027
     *
1028
     * @return $this
1029
     *               <p>(Immutable) Return this Arrayy object.</p>
1030
     *
1031
     * @psalm-return static<TKey,T>
1032
     * @psalm-mutation-free
1033
     */
1034 10
    public function arsortImmutable(): self
1035
    {
1036 10
        $that = clone $this;
1037
1038 10
        $that->generatorToArray();
1039
1040 10
        \arsort($that->array);
1041
1042 10
        return $that;
1043
    }
1044
1045
    /**
1046
     * Iterate over the current array and execute a callback for each loop.
1047
     *
1048
     * @param \Closure $closure
1049
     *
1050
     * @return static
1051
     *                <p>(Immutable)</p>
1052
     *
1053
     * @psalm-return static<TKey,T>
1054
     * @psalm-mutation-free
1055
     */
1056 2
    public function at(\Closure $closure): self
1057
    {
1058 2
        $that = clone $this;
1059
1060 2
        foreach ($that->getGenerator() as $key => $value) {
1061 2
            $closure($value, $key);
1062
        }
1063
1064 2
        return static::create(
1065 2
            $that->toArray(),
1066 2
            $this->iteratorClass,
1067 2
            false
1068
        );
1069
    }
1070
1071
    /**
1072
     * Returns the average value of the current array.
1073
     *
1074
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1075
     *
1076
     * @return float|int
1077
     *                   <p>The average value.</p>
1078
     * @psalm-mutation-free
1079
     */
1080 10
    public function average($decimals = 0)
1081
    {
1082 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1083
1084 10
        if (!$count) {
1085 2
            return 0;
1086
        }
1087
1088 8
        if ((int) $decimals !== $decimals) {
1089 3
            $decimals = 0;
1090
        }
1091
1092 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1093
    }
1094
1095
    /**
1096
     * Changes all keys in an array.
1097
     *
1098
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1099
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1100
     *
1101
     * @return static
1102
     *                <p>(Immutable)</p>
1103
     *
1104
     * @psalm-return static<TKey,T>
1105
     * @psalm-mutation-free
1106
     */
1107 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1108
    {
1109
        if (
1110 1
            $case !== \CASE_LOWER
1111
            &&
1112 1
            $case !== \CASE_UPPER
1113
        ) {
1114
            $case = \CASE_LOWER;
1115
        }
1116
1117 1
        $return = [];
1118 1
        foreach ($this->getGenerator() as $key => $value) {
1119 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1120
1121 1
            if ($case === \CASE_LOWER) {
1122 1
                $key = \mb_strtolower((string) $key);
1123
            } else {
1124 1
                $key = \mb_strtoupper((string) $key);
1125
            }
1126
1127 1
            $return[$key] = $value;
1128
        }
1129
1130 1
        return static::create(
1131 1
            $return,
1132 1
            $this->iteratorClass,
1133 1
            false
1134
        );
1135
    }
1136
1137
    /**
1138
     * Change the path separator of the array wrapper.
1139
     *
1140
     * By default, the separator is: "."
1141
     *
1142
     * @param string $separator <p>Separator to set.</p>
1143
     *
1144
     * @return $this
1145
     *               <p>(Mutable) Return this Arrayy object.</p>
1146
     *
1147
     * @psalm-return static<TKey,T>
1148
     */
1149 11
    public function changeSeparator($separator): self
1150
    {
1151 11
        $this->pathSeparator = $separator;
1152
1153 11
        return $this;
1154
    }
1155
1156
    /**
1157
     * Create a chunked version of the current array.
1158
     *
1159
     * @param int  $size         <p>Size of each chunk.</p>
1160
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1161
     *
1162
     * @return static
1163
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1164
     *
1165
     * @psalm-return static<TKey,T>
1166
     * @psalm-mutation-free
1167
     */
1168 5
    public function chunk($size, $preserveKeys = false): self
1169
    {
1170 5
        return static::create(
1171 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1172 5
            $this->iteratorClass,
1173 5
            false
1174
        );
1175
    }
1176
1177
    /**
1178
     * Clean all falsy values from the current array.
1179
     *
1180
     * @return static
1181
     *                <p>(Immutable)</p>
1182
     *
1183
     * @psalm-return static<TKey,T>
1184
     * @psalm-mutation-free
1185
     */
1186 8
    public function clean(): self
1187
    {
1188 8
        return $this->filter(
1189
            static function ($value) {
1190 7
                return (bool) $value;
1191 8
            }
1192
        );
1193
    }
1194
1195
    /**
1196
     * WARNING!!! -> Clear the current full array or a $key of it.
1197
     *
1198
     * @param int|int[]|string|string[]|null $key
1199
     *
1200
     * @return $this
1201
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1202
     *
1203
     * @psalm-return static<TKey,T>
1204
     */
1205 10
    public function clear($key = null): self
1206
    {
1207 10
        if ($key !== null) {
1208 3
            if (\is_array($key)) {
1209 1
                foreach ($key as $keyTmp) {
1210 1
                    $this->offsetUnset($keyTmp);
1211
                }
1212
            } else {
1213 2
                $this->offsetUnset($key);
1214
            }
1215
1216 3
            return $this;
1217
        }
1218
1219 7
        $this->array = [];
1220 7
        $this->generator = null;
1221
1222 7
        return $this;
1223
    }
1224
1225
    /**
1226
     * Check if an item is in the current array.
1227
     *
1228
     * @param float|int|string $value
1229
     * @param bool             $recursive
1230
     * @param bool             $strict
1231
     *
1232
     * @return bool
1233
     * @psalm-mutation-free
1234
     */
1235 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1236
    {
1237 23
        if ($recursive === true) {
1238 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1239
        }
1240
1241 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1242 11
            if ($strict) {
1243 11
                if ($value === $valueFromArray) {
1244 11
                    return true;
1245
                }
1246
            } else {
1247
                /** @noinspection NestedPositiveIfStatementsInspection */
1248
                if ($value == $valueFromArray) {
1249 7
                    return true;
1250
                }
1251
            }
1252
        }
1253
1254 7
        return false;
1255
    }
1256
1257
    /**
1258
     * Check if an (case-insensitive) string is in the current array.
1259
     *
1260
     * @param mixed $value
1261
     * @param bool  $recursive
1262
     *
1263
     * @return bool
1264
     * @psalm-mutation-free
1265
     *
1266
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1267
     */
1268 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1269
    {
1270 26
        if ($value === null) {
1271 2
            return false;
1272
        }
1273
1274 24
        if ($recursive === true) {
1275 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1276 22
                if (\is_array($valueTmp) === true) {
1277 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1278 5
                    if ($return === true) {
1279 5
                        return $return;
1280
                    }
1281 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1282 22
                    return true;
1283
                }
1284
            }
1285
1286 8
            return false;
1287
        }
1288
1289 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1290 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1291 11
                return true;
1292
            }
1293
        }
1294
1295 4
        return false;
1296
    }
1297
1298
    /**
1299
     * Check if the given key/index exists in the array.
1300
     *
1301
     * @param int|string $key <p>key/index to search for</p>
1302
     *
1303
     * @return bool
1304
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1305
     *
1306
     * @psalm-mutation-free
1307
     */
1308 4
    public function containsKey($key): bool
1309
    {
1310 4
        return $this->offsetExists($key);
1311
    }
1312
1313
    /**
1314
     * Check if all given needles are present in the array as key/index.
1315
     *
1316
     * @param array $needles   <p>The keys you are searching for.</p>
1317
     * @param bool  $recursive
1318
     *
1319
     * @return bool
1320
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1321
     *
1322
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1323
     * @psalm-mutation-free
1324
     */
1325 2
    public function containsKeys(array $needles, $recursive = false): bool
1326
    {
1327 2
        if ($recursive === true) {
1328
            return
1329 2
                \count(
1330 2
                    \array_intersect(
1331 2
                        $needles,
1332 2
                        $this->keys(true)->toArray()
1333
                    ),
1334 2
                    \COUNT_RECURSIVE
1335
                )
1336
                ===
1337 2
                \count(
1338 2
                    $needles,
1339 2
                    \COUNT_RECURSIVE
1340
                );
1341
        }
1342
1343 1
        return \count(
1344 1
            \array_intersect($needles, $this->keys()->toArray()),
1345 1
            \COUNT_NORMAL
1346
        )
1347
               ===
1348 1
               \count(
1349 1
                   $needles,
1350 1
                   \COUNT_NORMAL
1351
               );
1352
    }
1353
1354
    /**
1355
     * Check if all given needles are present in the array as key/index.
1356
     *
1357
     * @param array $needles <p>The keys you are searching for.</p>
1358
     *
1359
     * @return bool
1360
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1361
     *
1362
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1363
     * @psalm-mutation-free
1364
     */
1365 1
    public function containsKeysRecursive(array $needles): bool
1366
    {
1367 1
        return $this->containsKeys($needles, true);
1368
    }
1369
1370
    /**
1371
     * alias: for "Arrayy->contains()"
1372
     *
1373
     * @param float|int|string $value
1374
     *
1375
     * @return bool
1376
     *
1377
     * @see Arrayy::contains()
1378
     * @psalm-mutation-free
1379
     */
1380 9
    public function containsValue($value): bool
1381
    {
1382 9
        return $this->contains($value);
1383
    }
1384
1385
    /**
1386
     * alias: for "Arrayy->contains($value, true)"
1387
     *
1388
     * @param float|int|string $value
1389
     *
1390
     * @return bool
1391
     *
1392
     * @see Arrayy::contains()
1393
     * @psalm-mutation-free
1394
     */
1395 18
    public function containsValueRecursive($value): bool
1396
    {
1397 18
        return $this->contains($value, true);
1398
    }
1399
1400
    /**
1401
     * Check if all given needles are present in the array.
1402
     *
1403
     * @param array $needles
1404
     *
1405
     * @return bool
1406
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1407
     *
1408
     * @psalm-param array<mixed>|array<T> $needles
1409
     * @psalm-mutation-free
1410
     */
1411 1
    public function containsValues(array $needles): bool
1412
    {
1413 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1414
               ===
1415 1
               \count($needles, \COUNT_NORMAL);
1416
    }
1417
1418
    /**
1419
     * Counts all the values of an array
1420
     *
1421
     * @see          http://php.net/manual/en/function.array-count-values.php
1422
     *
1423
     * @return static
1424
     *                <p>
1425
     *                (Immutable)
1426
     *                An associative Arrayy-object of values from input as
1427
     *                keys and their count as value.
1428
     *                </p>
1429
     *
1430
     * @psalm-return static<TKey,T>
1431
     * @psalm-mutation-free
1432
     */
1433 7
    public function countValues(): self
1434
    {
1435 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1436
    }
1437
1438
    /**
1439
     * Creates an Arrayy object.
1440
     *
1441
     * @param mixed  $data
1442
     * @param string $iteratorClass
1443
     * @param bool   $checkPropertiesInConstructor
1444
     *
1445
     * @return static
1446
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1447
     *
1448
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1449
     *
1450
     * @psalm-mutation-free
1451
     */
1452 715
    public static function create(
1453
        $data = [],
1454
        string $iteratorClass = ArrayyIterator::class,
1455
        bool $checkPropertiesInConstructor = true
1456
    ) {
1457 715
        return new static(
1458 715
            $data,
1459 715
            $iteratorClass,
1460 715
            $checkPropertiesInConstructor
1461
        );
1462
    }
1463
1464
    /**
1465
     * Flatten an array with the given character as a key delimiter
1466
     *
1467
     * @param string     $delimiter
1468
     * @param string     $prepend
1469
     * @param array|null $items
1470
     *
1471
     * @return array
1472
     */
1473 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1474
    {
1475
        // init
1476 2
        $flatten = [];
1477
1478 2
        if ($items === null) {
1479 2
            $items = $this->array;
1480
        }
1481
1482 2
        foreach ($items as $key => $value) {
1483 2
            if (\is_array($value) && !empty($value)) {
1484 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1485
            } else {
1486 2
                $flatten[] = [$prepend . $key => $value];
1487
            }
1488
        }
1489
1490 2
        if (\count($flatten) === 0) {
1491
            return [];
1492
        }
1493
1494 2
        return \array_merge_recursive([], ...$flatten);
1495
    }
1496
1497
    /**
1498
     * WARNING: Creates an Arrayy object by reference.
1499
     *
1500
     * @param array $array
1501
     *
1502
     * @return $this
1503
     *               <p>(Mutable) Return this Arrayy object.</p>
1504
     *
1505
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1506
     */
1507 2
    public function createByReference(array &$array = []): self
1508
    {
1509 2
        $array = $this->fallbackForArray($array);
1510
1511 2
        $this->array = &$array;
1512
1513 2
        return $this;
1514
    }
1515
1516
    /**
1517
     * Create an new instance from a callable function which will return an Generator.
1518
     *
1519
     * @param callable $generatorFunction
1520
     *
1521
     * @return static
1522
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1523
     *
1524
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1525
     *
1526
     * @psalm-mutation-free
1527
     */
1528 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1529
    {
1530 7
        return self::create($generatorFunction);
1531
    }
1532
1533
    /**
1534
     * Create an new instance filled with a copy of values from a "Generator"-object.
1535
     *
1536
     * @param \Generator $generator
1537
     *
1538
     * @return static
1539
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1540
     *
1541
     * @psalm-param \Generator<array-key,mixed> $generator
1542
     *
1543
     * @psalm-mutation-free
1544
     */
1545 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1546
    {
1547 4
        return self::create(\iterator_to_array($generator, true));
1548
    }
1549
1550
    /**
1551
     * Create an new Arrayy object via JSON.
1552
     *
1553
     * @param string $json
1554
     *
1555
     * @return static
1556
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1557
     *
1558
     * @psalm-mutation-free
1559
     */
1560 5
    public static function createFromJson(string $json): self
1561
    {
1562 5
        return static::create(\json_decode($json, true));
1563
    }
1564
1565
    /**
1566
     * Create an new Arrayy object via JSON.
1567
     *
1568
     * @param array $array
1569
     *
1570
     * @return static
1571
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1572
     *
1573
     * @psalm-mutation-free
1574
     */
1575 1
    public static function createFromArray(array $array): self
1576
    {
1577 1
        return static::create($array);
1578
    }
1579
1580
    /**
1581
     * Create an new instance filled with values from an object that is iterable.
1582
     *
1583
     * @param \Traversable $object <p>iterable object</p>
1584
     *
1585
     * @return static
1586
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1587
     *
1588
     * @psalm-param \Traversable<array-key,mixed> $object
1589
     *
1590
     * @psalm-mutation-free
1591
     */
1592 4
    public static function createFromObject(\Traversable $object): self
1593
    {
1594
        // init
1595 4
        $arrayy = new static();
1596
1597 4
        if ($object instanceof self) {
1598 4
            $objectArray = $object->getGenerator();
1599
        } else {
1600
            $objectArray = $object;
1601
        }
1602
1603 4
        foreach ($objectArray as $key => $value) {
1604
            /**
1605
             * @psalm-suppress ImpureMethodCall - object is already re-created
1606
             */
1607 3
            $arrayy->internalSet($key, $value);
1608
        }
1609
1610 4
        return $arrayy;
1611
    }
1612
1613
    /**
1614
     * Create an new instance filled with values from an object.
1615
     *
1616
     * @param object $object
1617
     *
1618
     * @return static
1619
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1620
     *
1621
     * @psalm-mutation-free
1622
     */
1623 5
    public static function createFromObjectVars($object): self
1624
    {
1625 5
        return self::create(self::objectToArray($object));
1626
    }
1627
1628
    /**
1629
     * Create an new Arrayy object via string.
1630
     *
1631
     * @param string      $str       <p>The input string.</p>
1632
     * @param string|null $delimiter <p>The boundary string.</p>
1633
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1634
     *                               used.</p>
1635
     *
1636
     * @return static
1637
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1638
     *
1639
     * @psalm-mutation-free
1640
     */
1641 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1642
    {
1643 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...
1644 1
            \preg_match_all($regEx, $str, $array);
1645
1646 1
            if (!empty($array)) {
1647 1
                $array = $array[0];
1648
            }
1649
        } else {
1650
            /** @noinspection NestedPositiveIfStatementsInspection */
1651 9
            if ($delimiter !== null) {
1652 7
                $array = \explode($delimiter, $str);
1653
            } else {
1654 2
                $array = [$str];
1655
            }
1656
        }
1657
1658
        // trim all string in the array
1659
        /**
1660
         * @psalm-suppress MissingClosureParamType
1661
         */
1662 10
        \array_walk(
1663 10
            $array,
1664
            static function (&$val) {
1665 10
                if ((string) $val === $val) {
1666 10
                    $val = \trim($val);
1667
                }
1668 10
            }
1669
        );
1670
1671 10
        return static::create($array);
1672
    }
1673
1674
    /**
1675
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1676
     *
1677
     * @param \Traversable $traversable
1678
     *
1679
     * @return static
1680
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1681
     *
1682
     * @psalm-param \Traversable<array-key,mixed> $traversable
1683
     *
1684
     * @psalm-mutation-free
1685
     */
1686 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1687
    {
1688 1
        return self::create(\iterator_to_array($traversable, true));
1689
    }
1690
1691
    /**
1692
     * Create an new instance containing a range of elements.
1693
     *
1694
     * @param float|int|string $low  <p>First value of the sequence.</p>
1695
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1696
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1697
     *
1698
     * @return static
1699
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1700
     *
1701
     * @psalm-mutation-free
1702
     */
1703 2
    public static function createWithRange($low, $high, $step = 1): self
1704
    {
1705 2
        return static::create(\range($low, $high, $step));
1706
    }
1707
1708
    /**
1709
     * Gets the element of the array at the current internal iterator position.
1710
     *
1711
     * @return false|mixed
1712
     */
1713
    public function current()
1714
    {
1715
        return \current($this->array);
1716
    }
1717
1718
    /**
1719
     * Custom sort by index via "uksort".
1720
     *
1721
     * @see          http://php.net/manual/en/function.uksort.php
1722
     *
1723
     * @param callable $function
1724
     *
1725
     * @throws \InvalidArgumentException
1726
     *
1727
     * @return $this
1728
     *               <p>(Mutable) Return this Arrayy object.</p>
1729
     *
1730
     * @psalm-return static<TKey,T>
1731
     */
1732 5
    public function customSortKeys(callable $function): self
1733
    {
1734 5
        $this->generatorToArray();
1735
1736 5
        \uksort($this->array, $function);
1737
1738 5
        return $this;
1739
    }
1740
1741
    /**
1742
     * Custom sort by index via "uksort".
1743
     *
1744
     * @see          http://php.net/manual/en/function.uksort.php
1745
     *
1746
     * @param callable $function
1747
     *
1748
     * @throws \InvalidArgumentException
1749
     *
1750
     * @return $this
1751
     *               <p>(Immutable) Return this Arrayy object.</p>
1752
     *
1753
     * @psalm-return static<TKey,T>
1754
     * @psalm-mutation-free
1755
     */
1756 1
    public function customSortKeysImmutable(callable $function): self
1757
    {
1758 1
        $that = clone $this;
1759
1760 1
        $that->generatorToArray();
1761
1762
        /**
1763
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1764
         */
1765 1
        \uksort($that->array, $function);
1766
1767 1
        return $that;
1768
    }
1769
1770
    /**
1771
     * Custom sort by value via "usort".
1772
     *
1773
     * @see          http://php.net/manual/en/function.usort.php
1774
     *
1775
     * @param callable $function
1776
     *
1777
     * @throws \InvalidArgumentException
1778
     *
1779
     * @return $this
1780
     *               <p>(Mutable) Return this Arrayy object.</p>
1781
     *
1782
     * @psalm-return static<TKey,T>
1783
     */
1784 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...
1785
    {
1786 10
        if (\is_callable($function) === false) {
1787
            throw new \InvalidArgumentException('Passed function must be callable');
1788
        }
1789
1790 10
        $this->generatorToArray();
1791
1792 10
        \usort($this->array, $function);
1793
1794 10
        return $this;
1795
    }
1796
1797
    /**
1798
     * Custom sort by value via "usort".
1799
     *
1800
     * @see          http://php.net/manual/en/function.usort.php
1801
     *
1802
     * @param callable $function
1803
     *
1804
     * @throws \InvalidArgumentException
1805
     *
1806
     * @return $this
1807
     *               <p>(Immutable) Return this Arrayy object.</p>
1808
     *
1809
     * @psalm-return static<TKey,T>
1810
     * @psalm-mutation-free
1811
     */
1812 4
    public function customSortValuesImmutable($function): self
1813
    {
1814 4
        $that = clone $this;
1815
1816
        /**
1817
         * @psalm-suppress ImpureMethodCall - object is already cloned
1818
         */
1819 4
        $that->customSortValues($function);
1820
1821 4
        return $that;
1822
    }
1823
1824
    /**
1825
     * Delete the given key or keys.
1826
     *
1827
     * @param int|int[]|string|string[] $keyOrKeys
1828
     *
1829
     * @return void
1830
     */
1831 9
    public function delete($keyOrKeys)
1832
    {
1833 9
        $keyOrKeys = (array) $keyOrKeys;
1834
1835 9
        foreach ($keyOrKeys as $key) {
1836 9
            $this->offsetUnset($key);
1837
        }
1838 9
    }
1839
1840
    /**
1841
     * Return values that are only in the current array.
1842
     *
1843
     * @param array ...$array
1844
     *
1845
     * @return static
1846
     *                <p>(Immutable)</p>
1847
     *
1848
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1849
     * @psalm-return static<TKey,T>
1850
     * @psalm-mutation-free
1851
     */
1852 13
    public function diff(...$array): self
1853
    {
1854 13
        return static::create(
1855 13
            \array_diff($this->toArray(), ...$array),
1856 13
            $this->iteratorClass,
1857 13
            false
1858
        );
1859
    }
1860
1861
    /**
1862
     * Return values that are only in the current array.
1863
     *
1864
     * @param array ...$array
1865
     *
1866
     * @return static
1867
     *                <p>(Immutable)</p>
1868
     *
1869
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1870
     * @psalm-return static<TKey,T>
1871
     * @psalm-mutation-free
1872
     */
1873 8
    public function diffKey(...$array): self
1874
    {
1875 8
        return static::create(
1876 8
            \array_diff_key($this->toArray(), ...$array),
1877 8
            $this->iteratorClass,
1878 8
            false
1879
        );
1880
    }
1881
1882
    /**
1883
     * Return values and Keys that are only in the current array.
1884
     *
1885
     * @param array $array
1886
     *
1887
     * @return static
1888
     *                <p>(Immutable)</p>
1889
     *
1890
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1891
     * @psalm-return static<TKey,T>
1892
     * @psalm-mutation-free
1893
     */
1894 8
    public function diffKeyAndValue(array $array = []): self
1895
    {
1896 8
        return static::create(
1897 8
            \array_diff_assoc($this->toArray(), $array),
1898 8
            $this->iteratorClass,
1899 8
            false
1900
        );
1901
    }
1902
1903
    /**
1904
     * Return values that are only in the current multi-dimensional array.
1905
     *
1906
     * @param array                 $array
1907
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
1908
     *
1909
     * @return static
1910
     *                <p>(Immutable)</p>
1911
     *
1912
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1913
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
1914
     * @psalm-return static<TKey,T>
1915
     * @psalm-mutation-free
1916
     */
1917 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1918
    {
1919
        // init
1920 1
        $result = [];
1921
1922
        if (
1923 1
            $helperVariableForRecursion !== null
1924
            &&
1925 1
            \is_array($helperVariableForRecursion) === true
1926
        ) {
1927
            $arrayForTheLoop = $helperVariableForRecursion;
1928
        } else {
1929 1
            $arrayForTheLoop = $this->getGenerator();
1930
        }
1931
1932 1
        foreach ($arrayForTheLoop as $key => $value) {
1933 1
            if ($value instanceof self) {
1934
                $value = $value->toArray();
1935
            }
1936
1937 1
            if (\array_key_exists($key, $array)) {
1938 1
                if ($value !== $array[$key]) {
1939 1
                    $result[$key] = $value;
1940
                }
1941
            } else {
1942 1
                $result[$key] = $value;
1943
            }
1944
        }
1945
1946 1
        return static::create(
1947 1
            $result,
1948 1
            $this->iteratorClass,
1949 1
            false
1950
        );
1951
    }
1952
1953
    /**
1954
     * Return values that are only in the new $array.
1955
     *
1956
     * @param array $array
1957
     *
1958
     * @return static
1959
     *                <p>(Immutable)</p>
1960
     *
1961
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1962
     * @psalm-return static<TKey,T>
1963
     * @psalm-mutation-free
1964
     */
1965 8
    public function diffReverse(array $array = []): self
1966
    {
1967 8
        return static::create(
1968 8
            \array_diff($array, $this->toArray()),
1969 8
            $this->iteratorClass,
1970 8
            false
1971
        );
1972
    }
1973
1974
    /**
1975
     * Divide an array into two arrays. One with keys and the other with values.
1976
     *
1977
     * @return static
1978
     *                <p>(Immutable)</p>
1979
     *
1980
     * @psalm-return static<TKey,T>
1981
     * @psalm-mutation-free
1982
     */
1983 1
    public function divide(): self
1984
    {
1985 1
        return static::create(
1986
            [
1987 1
                $this->keys(),
1988 1
                $this->values(),
1989
            ],
1990 1
            $this->iteratorClass,
1991 1
            false
1992
        );
1993
    }
1994
1995
    /**
1996
     * Iterate over the current array and modify the array's value.
1997
     *
1998
     * @param \Closure $closure
1999
     *
2000
     * @return static
2001
     *                <p>(Immutable)</p>
2002
     *
2003
     * @psalm-return static<TKey,T>
2004
     * @psalm-mutation-free
2005
     */
2006 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...
2007
    {
2008
        // init
2009 5
        $array = [];
2010
2011 5
        foreach ($this->getGenerator() as $key => $value) {
2012 5
            $array[$key] = $closure($value, $key);
2013
        }
2014
2015 5
        return static::create(
2016 5
            $array,
2017 5
            $this->iteratorClass,
2018 5
            false
2019
        );
2020
    }
2021
2022
    /**
2023
     * Sets the internal iterator to the last element in the array and returns this element.
2024
     *
2025
     * @return mixed
2026
     */
2027
    public function end()
2028
    {
2029
        return \end($this->array);
2030
    }
2031
2032
    /**
2033
     * Check if a value is in the current array using a closure.
2034
     *
2035
     * @param \Closure $closure
2036
     *
2037
     * @return bool
2038
     *              <p>Returns true if the given value is found, false otherwise.</p>
2039
     */
2040 4
    public function exists(\Closure $closure): bool
2041
    {
2042
        // init
2043 4
        $isExists = false;
2044
2045 4
        foreach ($this->getGenerator() as $key => $value) {
2046 3
            if ($closure($value, $key)) {
2047 1
                $isExists = true;
2048
2049 3
                break;
2050
            }
2051
        }
2052
2053 4
        return $isExists;
2054
    }
2055
2056
    /**
2057
     * Fill the array until "$num" with "$default" values.
2058
     *
2059
     * @param int   $num
2060
     * @param mixed $default
2061
     *
2062
     * @return static
2063
     *                <p>(Immutable)</p>
2064
     *
2065
     * @psalm-return static<TKey,T>
2066
     * @psalm-mutation-free
2067
     */
2068 8
    public function fillWithDefaults(int $num, $default = null): self
2069
    {
2070 8
        if ($num < 0) {
2071 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2072
        }
2073
2074 7
        $this->generatorToArray();
2075
2076 7
        $tmpArray = $this->array;
2077
2078 7
        $count = \count($tmpArray);
2079
2080 7
        while ($count < $num) {
2081 4
            $tmpArray[] = $default;
2082 4
            ++$count;
2083
        }
2084
2085 7
        return static::create(
2086 7
            $tmpArray,
2087 7
            $this->iteratorClass,
2088 7
            false
2089
        );
2090
    }
2091
2092
    /**
2093
     * Find all items in an array that pass the truth test.
2094
     *
2095
     * @param \Closure|null $closure [optional] <p>
2096
     *                               The callback function to use
2097
     *                               </p>
2098
     *                               <p>
2099
     *                               If no callback is supplied, all entries of
2100
     *                               input equal to false (see
2101
     *                               converting to
2102
     *                               boolean) will be removed.
2103
     *                               </p>
2104
     * @param int           $flag    [optional] <p>
2105
     *                               Flag determining what arguments are sent to <i>callback</i>:
2106
     *                               </p><ul>
2107
     *                               <li>
2108
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
2109
     *                               to <i>callback</i> instead of the value</span>
2110
     *                               </li>
2111
     *                               <li>
2112
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
2113
     *                               arguments to <i>callback</i> instead of the value</span>
2114
     *                               </li>
2115
     *                               </ul>
2116
     *
2117
     * @return static
2118
     *                <p>(Immutable)</p>
2119
     *
2120
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
2121
     * @psalm-return static<TKey,T>
2122
     * @psalm-mutation-free
2123
     */
2124 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2125
    {
2126 12
        if (!$closure) {
2127 1
            return $this->clean();
2128
        }
2129
2130 12
        return static::create(
2131 12
            \array_filter($this->toArray(), $closure, $flag),
2132 12
            $this->iteratorClass,
2133 12
            false
2134
        );
2135
    }
2136
2137
    /**
2138
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2139
     * property within that.
2140
     *
2141
     * @param string          $property
2142
     * @param string|string[] $value
2143
     * @param string          $comparisonOp
2144
     *                                      <p>
2145
     *                                      'eq' (equals),<br />
2146
     *                                      'gt' (greater),<br />
2147
     *                                      'gte' || 'ge' (greater or equals),<br />
2148
     *                                      'lt' (less),<br />
2149
     *                                      'lte' || 'le' (less or equals),<br />
2150
     *                                      'ne' (not equals),<br />
2151
     *                                      'contains',<br />
2152
     *                                      'notContains',<br />
2153
     *                                      'newer' (via strtotime),<br />
2154
     *                                      'older' (via strtotime),<br />
2155
     *                                      </p>
2156
     *
2157
     * @return static
2158
     *                <p>(Immutable)</p>
2159
     *
2160
     * @psalm-return static<TKey,T>
2161
     * @psalm-mutation-free
2162
     *
2163
     * @psalm-suppress MissingClosureReturnType
2164
     * @psalm-suppress MissingClosureParamType
2165
     */
2166 1
    public function filterBy(
2167
        string $property,
2168
        $value,
2169
        string $comparisonOp = null
2170
    ): self {
2171 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...
2172 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2173
        }
2174
2175
        $ops = [
2176
            'eq' => static function ($item, $prop, $value): bool {
2177 1
                return $item[$prop] === $value;
2178 1
            },
2179
            'gt' => static function ($item, $prop, $value): bool {
2180
                return $item[$prop] > $value;
2181 1
            },
2182
            'ge' => static function ($item, $prop, $value): bool {
2183
                return $item[$prop] >= $value;
2184 1
            },
2185
            'gte' => static function ($item, $prop, $value): bool {
2186
                return $item[$prop] >= $value;
2187 1
            },
2188
            'lt' => static function ($item, $prop, $value): bool {
2189 1
                return $item[$prop] < $value;
2190 1
            },
2191
            'le' => static function ($item, $prop, $value): bool {
2192
                return $item[$prop] <= $value;
2193 1
            },
2194
            'lte' => static function ($item, $prop, $value): bool {
2195
                return $item[$prop] <= $value;
2196 1
            },
2197
            'ne' => static function ($item, $prop, $value): bool {
2198
                return $item[$prop] !== $value;
2199 1
            },
2200
            'contains' => static function ($item, $prop, $value): bool {
2201 1
                return \in_array($item[$prop], (array) $value, true);
2202 1
            },
2203
            'notContains' => static function ($item, $prop, $value): bool {
2204
                return !\in_array($item[$prop], (array) $value, true);
2205 1
            },
2206
            'newer' => static function ($item, $prop, $value): bool {
2207
                return \strtotime($item[$prop]) > \strtotime($value);
2208 1
            },
2209
            'older' => static function ($item, $prop, $value): bool {
2210
                return \strtotime($item[$prop]) < \strtotime($value);
2211 1
            },
2212
        ];
2213
2214 1
        $result = \array_values(
2215 1
            \array_filter(
2216 1
                $this->toArray(false, true),
2217
                static function ($item) use (
2218 1
                    $property,
2219 1
                    $value,
2220 1
                    $ops,
2221 1
                    $comparisonOp
2222
                ) {
2223 1
                    $item = (array) $item;
2224 1
                    $itemArrayy = static::create($item);
2225 1
                    $item[$property] = $itemArrayy->get($property, []);
2226
2227 1
                    return $ops[$comparisonOp]($item, $property, $value);
2228 1
                }
2229
            )
2230
        );
2231
2232 1
        return static::create(
2233 1
            $result,
2234 1
            $this->iteratorClass,
2235 1
            false
2236
        );
2237
    }
2238
2239
    /**
2240
     * Find the first item in an array that passes the truth test,
2241
     *  otherwise return false
2242
     *
2243
     * @param \Closure $closure
2244
     *
2245
     * @return false|mixed
2246
     *                     <p>Return false if we did not find the value.</p>
2247
     */
2248 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...
2249
    {
2250 8
        foreach ($this->getGenerator() as $key => $value) {
2251 6
            if ($closure($value, $key)) {
2252 6
                return $value;
2253
            }
2254
        }
2255
2256 3
        return false;
2257
    }
2258
2259
    /**
2260
     * find by ...
2261
     *
2262
     * @param string          $property
2263
     * @param string|string[] $value
2264
     * @param string          $comparisonOp
2265
     *
2266
     * @return static
2267
     *                <p>(Immutable)</p>
2268
     *
2269
     * @psalm-return static<TKey,T>
2270
     * @psalm-mutation-free
2271
     */
2272 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2273
    {
2274 1
        return $this->filterBy($property, $value, $comparisonOp);
2275
    }
2276
2277
    /**
2278
     * Get the first value from the current array.
2279
     *
2280
     * @return mixed
2281
     *               <p>Return null if there wasn't a element.</p>
2282
     */
2283 22
    public function first()
2284
    {
2285 22
        $key_first = $this->firstKey();
2286 22
        if ($key_first === null) {
2287 3
            return null;
2288
        }
2289
2290 19
        return $this->get($key_first);
2291
    }
2292
2293
    /**
2294
     * Get the first key from the current array.
2295
     *
2296
     * @return mixed
2297
     *               <p>Return null if there wasn't a element.</p>
2298
     * @psalm-mutation-free
2299
     */
2300 29
    public function firstKey()
2301
    {
2302 29
        $this->generatorToArray();
2303
2304 29
        return \array_key_first($this->array);
2305
    }
2306
2307
    /**
2308
     * Get the first value(s) from the current array.
2309
     * And will return an empty array if there was no first entry.
2310
     *
2311
     * @param int|null $number <p>How many values you will take?</p>
2312
     *
2313
     * @return static
2314
     *                <p>(Immutable)</p>
2315
     *
2316
     * @psalm-return static<TKey,T>
2317
     * @psalm-mutation-free
2318
     */
2319 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...
2320
    {
2321 37
        $arrayTmp = $this->toArray();
2322
2323 37
        if ($number === null) {
2324 14
            $array = (array) \array_shift($arrayTmp);
2325
        } else {
2326 23
            $number = (int) $number;
2327 23
            $array = \array_splice($arrayTmp, 0, $number);
2328
        }
2329
2330 37
        return static::create(
2331 37
            $array,
2332 37
            $this->iteratorClass,
2333 37
            false
2334
        );
2335
    }
2336
2337
    /**
2338
     * Get the first value(s) from the current array.
2339
     * And will return an empty array if there was no first entry.
2340
     *
2341
     * @param int|null $number <p>How many values you will take?</p>
2342
     *
2343
     * @return static
2344
     *                <p>(Immutable)</p>
2345
     *
2346
     * @psalm-return static<TKey,T>
2347
     * @psalm-mutation-free
2348
     */
2349 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...
2350
    {
2351 3
        $arrayTmp = $this->keys()->toArray();
2352
2353 3
        if ($number === null) {
2354
            $array = (array) \array_shift($arrayTmp);
2355
        } else {
2356 3
            $number = (int) $number;
2357 3
            $array = \array_splice($arrayTmp, 0, $number);
2358
        }
2359
2360 3
        return static::create(
2361 3
            $array,
2362 3
            $this->iteratorClass,
2363 3
            false
2364
        );
2365
    }
2366
2367
    /**
2368
     * Get and rmove the first value(s) from the current array.
2369
     * And will return an empty array if there was no first entry.
2370
     *
2371
     * @param int|null $number <p>How many values you will take?</p>
2372
     *
2373
     * @return $this
2374
     *               <p>(Mutable)</p>
2375
     *
2376
     * @psalm-return static<TKey,T>
2377
     */
2378 34
    public function firstsMutable(int $number = null): self
2379
    {
2380 34
        $this->generatorToArray();
2381
2382 34
        if ($number === null) {
2383 19
            $this->array = (array) \array_shift($this->array);
2384
        } else {
2385 15
            $number = (int) $number;
2386 15
            $this->array = \array_splice($this->array, 0, $number);
2387
        }
2388
2389 34
        return $this;
2390
    }
2391
2392
    /**
2393
     * Exchanges all keys with their associated values in an array.
2394
     *
2395
     * @return static
2396
     *                <p>(Immutable)</p>
2397
     *
2398
     * @psalm-return static<TKey,T>
2399
     * @psalm-mutation-free
2400
     */
2401 1
    public function flip(): self
2402
    {
2403 1
        return static::create(
2404 1
            \array_flip($this->toArray()),
2405 1
            $this->iteratorClass,
2406 1
            false
2407
        );
2408
    }
2409
2410
    /**
2411
     * Get a value from an array (optional using dot-notation).
2412
     *
2413
     * @param mixed $key      <p>The key to look for.</p>
2414
     * @param mixed $fallback <p>Value to fallback to.</p>
2415
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
2416
     *                        class.</p>
2417
     *
2418
     * @return mixed|static
2419
     *
2420
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2421
     * @psalm-mutation-free
2422
     */
2423 240
    public function get($key, $fallback = null, array $array = null)
2424
    {
2425 240
        if ($array !== null) {
2426 4
            $usedArray = $array;
2427
        } else {
2428 237
            $this->generatorToArray();
2429
2430 237
            $usedArray = $this->array;
2431
        }
2432
2433 240
        if ($key === null) {
2434 1
            return static::create(
2435 1
                $usedArray,
2436 1
                $this->iteratorClass,
2437 1
                false
2438
            );
2439
        }
2440
2441
        // php cast "bool"-index into "int"-index
2442 240
        if ((bool) $key === $key) {
2443 3
            $key = (int) $key;
2444
        }
2445
2446 240
        if (\array_key_exists($key, $usedArray) === true) {
2447 203
            if (\is_array($usedArray[$key]) === true) {
2448 18
                return static::create(
2449 18
                    $usedArray[$key],
2450 18
                    $this->iteratorClass,
2451 18
                    false
2452
                );
2453
            }
2454
2455 189
            return $usedArray[$key];
2456
        }
2457
2458
        // crawl through array, get key according to object or not
2459 56
        $usePath = false;
2460
        if (
2461 56
            $this->pathSeparator
2462
            &&
2463 56
            (string) $key === $key
2464
            &&
2465 56
            \strpos($key, $this->pathSeparator) !== false
2466
        ) {
2467 30
            $segments = \explode($this->pathSeparator, (string) $key);
2468 30
            if ($segments !== false) {
2469 30
                $usePath = true;
2470
2471 30
                foreach ($segments as $segment) {
2472
                    if (
2473
                        (
2474 30
                            \is_array($usedArray) === true
2475
                            ||
2476 30
                            $usedArray instanceof \ArrayAccess
2477
                        )
2478
                        &&
2479 30
                        isset($usedArray[$segment])
2480
                    ) {
2481 30
                        $usedArray = $usedArray[$segment];
2482
2483 30
                        continue;
2484
                    }
2485
2486
                    if (
2487 12
                        \is_object($usedArray) === true
2488
                        &&
2489 12
                        \property_exists($usedArray, $segment)
2490
                    ) {
2491 1
                        $usedArray = $usedArray->{$segment};
2492
2493 1
                        continue;
2494
                    }
2495
2496 11
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2497
                }
2498
            }
2499
        }
2500
2501 54
        if (!$usePath && !isset($usedArray[$key])) {
2502 26
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2503
        }
2504
2505 28
        if (\is_array($usedArray) === true) {
2506 6
            return static::create(
2507 6
                $usedArray,
2508 6
                $this->iteratorClass,
2509 6
                false
2510
            );
2511
        }
2512
2513 28
        return $usedArray;
2514
    }
2515
2516
    /**
2517
     * alias: for "Arrayy->toArray()"
2518
     *
2519
     * @return array
2520
     *
2521
     * @see          Arrayy::getArray()
2522
     *
2523
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2524
     */
2525 14
    public function getAll(): array
2526
    {
2527 14
        return $this->toArray();
2528
    }
2529
2530
    /**
2531
     * Get the current array from the "Arrayy"-object.
2532
     *
2533
     * alias for "toArray()"
2534
     *
2535
     * @param bool $convertAllArrayyElements <p>
2536
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2537
     *                                       </p>
2538
     * @param bool $preserveKeys             <p>
2539
     *                                       e.g.: A generator maybe return the same key more then once,
2540
     *                                       so maybe you will ignore the keys.
2541
     *                                       </p>
2542
     *
2543
     * @return array
2544
     *
2545
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2546
     * @psalm-mutation-free
2547
     *
2548
     * @see Arrayy::toArray()
2549
     */
2550 498
    public function getArray(
2551
        bool $convertAllArrayyElements = false,
2552
        bool $preserveKeys = true
2553
    ): array {
2554 498
        return $this->toArray(
2555 498
            $convertAllArrayyElements,
2556 498
            $preserveKeys
2557
        );
2558
    }
2559
2560
    /**
2561
     * @param string $json
2562
     *
2563
     * @return $this
2564
     */
2565 3
    public static function createFromJsonMapper(string $json)
2566
    {
2567
        // init
2568 3
        $class = static::create();
2569
2570 3
        $jsonObject = \json_decode($json, false);
2571
2572 3
        $mapper = new \Arrayy\Mapper\Json();
2573 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...
2574
            if ($class->checkPropertiesMismatchInConstructor) {
2575
                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...
2576
            }
2577
        };
2578
2579 3
        return $mapper->map($jsonObject, $class);
2580
    }
2581
2582
    /**
2583
     * @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...
2584
     *
2585
     * @internal
2586
     */
2587 6
    public function getPhpDocPropertiesFromClass()
2588
    {
2589 6
        if ($this->properties === []) {
2590 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2591
        }
2592
2593 6
        return $this->properties;
2594
    }
2595
2596
    /**
2597
     * Get the current array from the "Arrayy"-object as list.
2598
     *
2599
     * alias for "toList()"
2600
     *
2601
     * @param bool $convertAllArrayyElements <p>
2602
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2603
     *                                       </p>
2604
     *
2605
     * @return array
2606
     *
2607
     * @psalm-return array<int,mixed>|array<int,T>
2608
     * @psalm-mutation-free
2609
     *
2610
     * @see Arrayy::toList()
2611
     */
2612 1
    public function getList(bool $convertAllArrayyElements = false): array
2613
    {
2614 1
        return $this->toList($convertAllArrayyElements);
2615
    }
2616
2617
    /**
2618
     * Returns the values from a single column of the input array, identified by
2619
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2620
     *
2621
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2622
     * array by the values from the $indexKey column in the input array.
2623
     *
2624
     * @param mixed $columnKey
2625
     * @param mixed $indexKey
2626
     *
2627
     * @return static
2628
     *                <p>(Immutable)</p>
2629
     *
2630
     * @psalm-return static<TKey,T>
2631
     * @psalm-mutation-free
2632
     */
2633 1
    public function getColumn($columnKey = null, $indexKey = null): self
2634
    {
2635 1
        return static::create(
2636 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2637 1
            $this->iteratorClass,
2638 1
            false
2639
        );
2640
    }
2641
2642
    /**
2643
     * Get the current array from the "Arrayy"-object as generator by reference.
2644
     *
2645
     * @return \Generator
2646
     *
2647
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2648
     */
2649 75
    public function &getGeneratorByReference(): \Generator
2650
    {
2651 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2652
            // -> false-positive -> see "&" from method
2653
            /** @noinspection YieldFromCanBeUsedInspection */
2654 17
            foreach ($this->generator as $key => $value) {
2655 17
                yield $key => $value;
2656
            }
2657
2658 5
            return;
2659
        }
2660
2661
        // -> false-positive -> see "&$value"
2662
        /** @noinspection YieldFromCanBeUsedInspection */
2663 59
        foreach ($this->array as $key => &$value) {
2664 54
            yield $key => $value;
2665
        }
2666 35
    }
2667
2668
    /**
2669
     * Get the current array from the "Arrayy"-object as generator.
2670
     *
2671
     * @return \Generator
2672
     *
2673
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2674
     * @psalm-mutation-free
2675
     */
2676 994
    public function getGenerator(): \Generator
2677
    {
2678 994
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2679 25
            yield from $this->generator;
2680
2681 25
            return;
2682
        }
2683
2684 992
        yield from $this->array;
2685 966
    }
2686
2687
    /**
2688
     * alias: for "Arrayy->keys()"
2689
     *
2690
     * @return static
2691
     *                <p>(Immutable)</p>
2692
     *
2693
     * @see          Arrayy::keys()
2694
     *
2695
     * @psalm-return static<array-key,TKey>
2696
     * @psalm-mutation-free
2697
     */
2698 2
    public function getKeys()
2699
    {
2700 2
        return $this->keys();
2701
    }
2702
2703
    /**
2704
     * Get the current array from the "Arrayy"-object as object.
2705
     *
2706
     * @return \stdClass
2707
     */
2708 4
    public function getObject(): \stdClass
2709
    {
2710 4
        return self::arrayToObject($this->toArray());
2711
    }
2712
2713
    /**
2714
     * alias: for "Arrayy->randomImmutable()"
2715
     *
2716
     * @return static
2717
     *                <p>(Immutable)</p>
2718
     *
2719
     * @see          Arrayy::randomImmutable()
2720
     *
2721
     * @psalm-return static<int|array-key,T>
2722
     */
2723 4
    public function getRandom(): self
2724
    {
2725 4
        return $this->randomImmutable();
2726
    }
2727
2728
    /**
2729
     * alias: for "Arrayy->randomKey()"
2730
     *
2731
     * @return mixed
2732
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2733
     *
2734
     * @see Arrayy::randomKey()
2735
     */
2736 3
    public function getRandomKey()
2737
    {
2738 3
        return $this->randomKey();
2739
    }
2740
2741
    /**
2742
     * alias: for "Arrayy->randomKeys()"
2743
     *
2744
     * @param int $number
2745
     *
2746
     * @return static
2747
     *                <p>(Immutable)</p>
2748
     *
2749
     * @see          Arrayy::randomKeys()
2750
     *
2751
     * @psalm-return static<TKey,T>
2752
     */
2753 8
    public function getRandomKeys(int $number): self
2754
    {
2755 8
        return $this->randomKeys($number);
2756
    }
2757
2758
    /**
2759
     * alias: for "Arrayy->randomValue()"
2760
     *
2761
     * @return mixed
2762
     *               <p>Get a random value or null if there wasn't a value.</p>
2763
     *
2764
     * @see Arrayy::randomValue()
2765
     */
2766 3
    public function getRandomValue()
2767
    {
2768 3
        return $this->randomValue();
2769
    }
2770
2771
    /**
2772
     * alias: for "Arrayy->randomValues()"
2773
     *
2774
     * @param int $number
2775
     *
2776
     * @return static
2777
     *                <p>(Immutable)</p>
2778
     *
2779
     * @see          Arrayy::randomValues()
2780
     *
2781
     * @psalm-return static<TKey,T>
2782
     */
2783 6
    public function getRandomValues(int $number): self
2784
    {
2785 6
        return $this->randomValues($number);
2786
    }
2787
2788
    /**
2789
     * Gets all values.
2790
     *
2791
     * @return static
2792
     *                <p>The values of all elements in this array, in the order they
2793
     *                appear in the array.</p>
2794
     *
2795
     * @psalm-return static<TKey,T>
2796
     */
2797 4
    public function getValues()
2798
    {
2799 4
        $this->generatorToArray(false);
2800
2801 4
        return static::create(
2802 4
            \array_values($this->array),
2803 4
            $this->iteratorClass,
2804 4
            false
2805
        );
2806
    }
2807
2808
    /**
2809
     * Gets all values via Generator.
2810
     *
2811
     * @return \Generator
2812
     *                    <p>The values of all elements in this array, in the order they
2813
     *                    appear in the array as Generator.</p>
2814
     *
2815
     * @psalm-return \Generator<TKey,T>
2816
     */
2817 4
    public function getValuesYield(): \Generator
2818
    {
2819 4
        yield from $this->getGenerator();
2820 4
    }
2821
2822
    /**
2823
     * Group values from a array according to the results of a closure.
2824
     *
2825
     * @param callable|string $grouper  <p>A callable function name.</p>
2826
     * @param bool            $saveKeys
2827
     *
2828
     * @return static
2829
     *                <p>(Immutable)</p>
2830
     *
2831
     * @psalm-return static<TKey,T>
2832
     * @psalm-mutation-free
2833
     */
2834 4
    public function group($grouper, bool $saveKeys = false): self
2835
    {
2836
        // init
2837 4
        $result = [];
2838
2839
        // Iterate over values, group by property/results from closure.
2840 4
        foreach ($this->getGenerator() as $key => $value) {
2841 4
            if (\is_callable($grouper) === true) {
2842 3
                $groupKey = $grouper($value, $key);
2843
            } else {
2844 1
                $groupKey = $this->get($grouper);
2845
            }
2846
2847 4
            $newValue = $this->get($groupKey, null, $result);
2848
2849 4
            if ($groupKey instanceof self) {
2850
                $groupKey = $groupKey->toArray();
2851
            }
2852
2853 4
            if ($newValue instanceof self) {
2854 4
                $newValue = $newValue->toArray();
2855
            }
2856
2857
            // Add to results.
2858 4
            if ($groupKey !== null) {
2859 3
                if ($saveKeys) {
2860 2
                    $result[$groupKey] = $newValue;
2861 2
                    $result[$groupKey][$key] = $value;
2862
                } else {
2863 1
                    $result[$groupKey] = $newValue;
2864 4
                    $result[$groupKey][] = $value;
2865
                }
2866
            }
2867
        }
2868
2869 4
        return static::create(
2870 4
            $result,
2871 4
            $this->iteratorClass,
2872 4
            false
2873
        );
2874
    }
2875
2876
    /**
2877
     * Check if an array has a given key.
2878
     *
2879
     * @param mixed $key
2880
     *
2881
     * @return bool
2882
     */
2883 30
    public function has($key): bool
2884
    {
2885 30
        static $UN_FOUND = null;
2886
2887 30
        if ($UN_FOUND === null) {
2888
            // Generate unique string to use as marker.
2889 1
            $UN_FOUND = \uniqid('arrayy', true);
2890
        }
2891
2892 30
        if (\is_array($key)) {
2893 1
            if ($key === []) {
2894
                return false;
2895
            }
2896
2897 1
            foreach ($key as $keyTmp) {
2898 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
2899 1
                if ($found === false) {
2900 1
                    return false;
2901
                }
2902
            }
2903
2904 1
            return true;
2905
        }
2906
2907 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2908
    }
2909
2910
    /**
2911
     * Check if an array has a given value.
2912
     *
2913
     * INFO: if you need to search recursive please use ```contains()```
2914
     *
2915
     * @param mixed $value
2916
     *
2917
     * @return bool
2918
     */
2919 1
    public function hasValue($value): bool
2920
    {
2921 1
        return $this->contains($value);
2922
    }
2923
2924
    /**
2925
     * Implodes the values of this array.
2926
     *
2927
     * @param string $glue
2928
     *
2929
     * @return string
2930
     * @psalm-mutation-free
2931
     */
2932 28
    public function implode(string $glue = ''): string
2933
    {
2934 28
        return $this->implode_recursive($glue, $this->toArray(), false);
2935
    }
2936
2937
    /**
2938
     * Implodes the keys of this array.
2939
     *
2940
     * @param string $glue
2941
     *
2942
     * @return string
2943
     * @psalm-mutation-free
2944
     */
2945 8
    public function implodeKeys(string $glue = ''): string
2946
    {
2947 8
        return $this->implode_recursive($glue, $this->toArray(), true);
2948
    }
2949
2950
    /**
2951
     * Given a list and an iterate-function that returns
2952
     * a key for each element in the list (or a property name),
2953
     * returns an object with an index of each item.
2954
     *
2955
     * @param mixed $key
2956
     *
2957
     * @return static
2958
     *                <p>(Immutable)</p>
2959
     *
2960
     * @psalm-return static<TKey,T>
2961
     * @psalm-mutation-free
2962
     */
2963 4
    public function indexBy($key): self
2964
    {
2965
        // init
2966 4
        $results = [];
2967
2968 4
        foreach ($this->getGenerator() as $a) {
2969 4
            if (\array_key_exists($key, $a) === true) {
2970 4
                $results[$a[$key]] = $a;
2971
            }
2972
        }
2973
2974 4
        return static::create(
2975 4
            $results,
2976 4
            $this->iteratorClass,
2977 4
            false
2978
        );
2979
    }
2980
2981
    /**
2982
     * alias: for "Arrayy->searchIndex()"
2983
     *
2984
     * @param mixed $value <p>The value to search for.</p>
2985
     *
2986
     * @return false|mixed
2987
     *
2988
     * @see Arrayy::searchIndex()
2989
     */
2990 4
    public function indexOf($value)
2991
    {
2992 4
        return $this->searchIndex($value);
2993
    }
2994
2995
    /**
2996
     * Get everything but the last..$to items.
2997
     *
2998
     * @param int $to
2999
     *
3000
     * @return static
3001
     *                <p>(Immutable)</p>
3002
     *
3003
     * @psalm-return static<TKey,T>
3004
     * @psalm-mutation-free
3005
     */
3006 12
    public function initial(int $to = 1): self
3007
    {
3008 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3009
    }
3010
3011
    /**
3012
     * Return an array with all elements found in input array.
3013
     *
3014
     * @param array $search
3015
     * @param bool  $keepKeys
3016
     *
3017
     * @return static
3018
     *                <p>(Immutable)</p>
3019
     *
3020
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3021
     * @psalm-return static<TKey,T>
3022
     * @psalm-mutation-free
3023
     */
3024 4
    public function intersection(array $search, bool $keepKeys = false): self
3025
    {
3026 4
        if ($keepKeys) {
3027
            /**
3028
             * @psalm-suppress MissingClosureReturnType
3029
             * @psalm-suppress MissingClosureParamType
3030
             */
3031 1
            return static::create(
3032 1
                \array_uintersect(
3033 1
                    $this->toArray(),
3034 1
                    $search,
3035
                    static function ($a, $b) {
3036 1
                        return $a === $b ? 0 : -1;
3037 1
                    }
3038
                ),
3039 1
                $this->iteratorClass,
3040 1
                false
3041
            );
3042
        }
3043
3044 3
        return static::create(
3045 3
            \array_values(\array_intersect($this->toArray(), $search)),
3046 3
            $this->iteratorClass,
3047 3
            false
3048
        );
3049
    }
3050
3051
    /**
3052
     * Return an array with all elements found in input array.
3053
     *
3054
     * @param array ...$array
3055
     *
3056
     * @return static
3057
     *                <p>(Immutable)</p>
3058
     *
3059
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3060
     * @psalm-return static<TKey,T>
3061
     * @psalm-mutation-free
3062
     */
3063 1
    public function intersectionMulti(...$array): self
3064
    {
3065 1
        return static::create(
3066 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3067 1
            $this->iteratorClass,
3068 1
            false
3069
        );
3070
    }
3071
3072
    /**
3073
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3074
     *
3075
     * @param array $search
3076
     *
3077
     * @return bool
3078
     *
3079
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3080
     */
3081 1
    public function intersects(array $search): bool
3082
    {
3083 1
        return $this->intersection($search)->count() > 0;
3084
    }
3085
3086
    /**
3087
     * Invoke a function on all of an array's values.
3088
     *
3089
     * @param callable $callable
3090
     * @param mixed    $arguments
3091
     *
3092
     * @return static
3093
     *                <p>(Immutable)</p>
3094
     *
3095
     * @psalm-param  callable(T=,mixed):mixed $callable
3096
     * @psalm-return static<TKey,T>
3097
     * @psalm-mutation-free
3098
     */
3099 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...
3100
    {
3101
        // If one argument given for each iteration, create an array for it.
3102 1
        if (\is_array($arguments) === false) {
3103 1
            $arguments = \array_fill(
3104 1
                0,
3105 1
                $this->count(),
3106 1
                $arguments
3107
            );
3108
        }
3109
3110
        // If the callable has arguments, pass them.
3111 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...
3112 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3113
        } else {
3114 1
            $array = $this->map($callable);
3115
        }
3116
3117 1
        return static::create(
3118 1
            $array,
3119 1
            $this->iteratorClass,
3120 1
            false
3121
        );
3122
    }
3123
3124
    /**
3125
     * Check whether array is associative or not.
3126
     *
3127
     * @param bool $recursive
3128
     *
3129
     * @return bool
3130
     *              <p>Returns true if associative, false otherwise.</p>
3131
     */
3132 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...
3133
    {
3134 15
        if ($this->isEmpty()) {
3135 3
            return false;
3136
        }
3137
3138 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3139 13
            if ((string) $key !== $key) {
3140 13
                return false;
3141
            }
3142
        }
3143
3144 3
        return true;
3145
    }
3146
3147
    /**
3148
     * Check if a given key or keys are empty.
3149
     *
3150
     * @param int|int[]|string|string[]|null $keys
3151
     *
3152
     * @return bool
3153
     *              <p>Returns true if empty, false otherwise.</p>
3154
     * @psalm-mutation-free
3155
     */
3156 45
    public function isEmpty($keys = null): bool
3157
    {
3158 45
        if ($this->generator) {
3159
            return $this->toArray() === [];
3160
        }
3161
3162 45
        if ($keys === null) {
3163 43
            return $this->array === [];
3164
        }
3165
3166 2
        foreach ((array) $keys as $key) {
3167 2
            if (!empty($this->get($key))) {
3168 2
                return false;
3169
            }
3170
        }
3171
3172 2
        return true;
3173
    }
3174
3175
    /**
3176
     * Check if the current array is equal to the given "$array" or not.
3177
     *
3178
     * @param array $array
3179
     *
3180
     * @return bool
3181
     *
3182
     * @psalm-param array<mixed,mixed> $array
3183
     */
3184 1
    public function isEqual(array $array): bool
3185
    {
3186 1
        return $this->toArray() === $array;
3187
    }
3188
3189
    /**
3190
     * Check if the current array is a multi-array.
3191
     *
3192
     * @return bool
3193
     */
3194 22
    public function isMultiArray(): bool
3195
    {
3196
        return !(
3197 22
            \count($this->toArray(), \COUNT_NORMAL)
3198
            ===
3199 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3200
        );
3201
    }
3202
3203
    /**
3204
     * Check whether array is numeric or not.
3205
     *
3206
     * @return bool
3207
     *              <p>Returns true if numeric, false otherwise.</p>
3208
     */
3209 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...
3210
    {
3211 5
        if ($this->isEmpty()) {
3212 2
            return false;
3213
        }
3214
3215 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3216 4
            if ((int) $key !== $key) {
3217 4
                return false;
3218
            }
3219
        }
3220
3221 2
        return true;
3222
    }
3223
3224
    /**
3225
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3226
     *
3227
     * @param bool $recursive
3228
     *
3229
     * @return bool
3230
     * @psalm-mutation-free
3231
     */
3232 9
    public function isSequential(bool $recursive = false): bool
3233
    {
3234
3235
        // recursive
3236
3237 9
        if ($recursive === true) {
3238
            return $this->array_keys_recursive($this->toArray())
3239
                   ===
3240
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3241
        }
3242
3243
        // non recursive
3244
3245 9
        return \array_keys($this->toArray())
3246
               ===
3247 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3248
    }
3249
3250
    /**
3251
     * @return array
3252
     *
3253
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3254
     */
3255 2
    public function jsonSerialize(): array
3256
    {
3257 2
        return $this->toArray();
3258
    }
3259
3260
    /**
3261
     * Gets the key/index of the element at the current internal iterator position.
3262
     *
3263
     * @return int|string|null
3264
     */
3265
    public function key()
3266
    {
3267
        return \key($this->array);
3268
    }
3269
3270
    /**
3271
     * Checks if the given key exists in the provided array.
3272
     *
3273
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3274
     *       then you need to use "Arrayy->offsetExists()".
3275
     *
3276
     * @param int|string $key the key to look for
3277
     *
3278
     * @return bool
3279
     * @psalm-mutation-free
3280
     */
3281 162
    public function keyExists($key): bool
3282
    {
3283 162
        return \array_key_exists($key, $this->array);
3284
    }
3285
3286
    /**
3287
     * Get all keys from the current array.
3288
     *
3289
     * @param bool       $recursive     [optional] <p>
3290
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3291
     *                                  </p>
3292
     * @param mixed|null $search_values [optional] <p>
3293
     *                                  If specified, then only keys containing these values are returned.
3294
     *                                  </p>
3295
     * @param bool       $strict        [optional] <p>
3296
     *                                  Determines if strict comparison (===) should be used during the search.
3297
     *                                  </p>
3298
     *
3299
     * @return static
3300
     *                <p>(Immutable) An array of all the keys in input.</p>
3301
     *
3302
     * @psalm-return static<array-key,TKey>
3303
     * @psalm-mutation-free
3304
     */
3305 29
    public function keys(
3306
        bool $recursive = false,
3307
        $search_values = null,
3308
        bool $strict = true
3309
    ): self {
3310
3311
        // recursive
3312
3313 29
        if ($recursive === true) {
3314 4
            $array = $this->array_keys_recursive(
3315 4
                null,
3316 4
                $search_values,
3317 4
                $strict
3318
            );
3319
3320 4
            return static::create(
3321 4
                $array,
3322 4
                $this->iteratorClass,
3323 4
                false
3324
            );
3325
        }
3326
3327
        // non recursive
3328
3329 28
        if ($search_values === null) {
3330
            $arrayFunction = function (): \Generator {
3331 28
                foreach ($this->getGenerator() as $key => $value) {
3332 26
                    yield $key;
3333
                }
3334 28
            };
3335
        } else {
3336
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3337 1
                $is_array_tmp = \is_array($search_values);
3338
3339 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3340 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...
3341
                        (
3342 1
                            $is_array_tmp === false
3343
                            &&
3344 1
                            $strict === true
3345
                            &&
3346 1
                            $search_values === $value
3347
                        )
3348
                        ||
3349
                        (
3350 1
                            $is_array_tmp === false
3351
                            &&
3352 1
                            $strict === false
3353
                            &&
3354 1
                            $search_values == $value
3355
                        )
3356
                        ||
3357
                        (
3358 1
                            $is_array_tmp === true
3359
                            &&
3360 1
                            \in_array($value, $search_values, $strict)
3361
                        )
3362
                    ) {
3363 1
                        yield $key;
3364
                    }
3365
                }
3366 1
            };
3367
        }
3368
3369 28
        return static::create(
3370 28
            $arrayFunction,
3371 28
            $this->iteratorClass,
3372 28
            false
3373
        );
3374
    }
3375
3376
    /**
3377
     * Sort an array by key in reverse order.
3378
     *
3379
     * @param int $sort_flags [optional] <p>
3380
     *                        You may modify the behavior of the sort using the optional
3381
     *                        parameter sort_flags, for details
3382
     *                        see sort.
3383
     *                        </p>
3384
     *
3385
     * @return $this
3386
     *               <p>(Mutable) Return this Arrayy object.</p>
3387
     *
3388
     * @psalm-return static<TKey,T>
3389
     */
3390 4
    public function krsort(int $sort_flags = 0): self
3391
    {
3392 4
        $this->generatorToArray();
3393
3394 4
        \krsort($this->array, $sort_flags);
3395
3396 4
        return $this;
3397
    }
3398
3399
    /**
3400
     * Sort an array by key in reverse order.
3401
     *
3402
     * @param int $sort_flags [optional] <p>
3403
     *                        You may modify the behavior of the sort using the optional
3404
     *                        parameter sort_flags, for details
3405
     *                        see sort.
3406
     *                        </p>
3407
     *
3408
     * @return $this
3409
     *               <p>(Immutable) Return this Arrayy object.</p>
3410
     *
3411
     * @psalm-return static<TKey,T>
3412
     * @psalm-mutation-free
3413
     */
3414 4
    public function krsortImmutable(int $sort_flags = 0): self
3415
    {
3416 4
        $that = clone $this;
3417
3418
        /**
3419
         * @psalm-suppress ImpureMethodCall - object is already cloned
3420
         */
3421 4
        $that->krsort($sort_flags);
3422
3423 4
        return $that;
3424
    }
3425
3426
    /**
3427
     * Get the last value from the current array.
3428
     *
3429
     * @return mixed|null
3430
     *                    <p>Return null if there wasn't a element.</p>
3431
     * @psalm-mutation-free
3432
     */
3433 17
    public function last()
3434
    {
3435 17
        $key_last = $this->lastKey();
3436 17
        if ($key_last === null) {
3437 2
            return null;
3438
        }
3439
3440 15
        return $this->get($key_last);
3441
    }
3442
3443
    /**
3444
     * Get the last key from the current array.
3445
     *
3446
     * @return mixed|null
3447
     *                    <p>Return null if there wasn't a element.</p>
3448
     * @psalm-mutation-free
3449
     */
3450 21
    public function lastKey()
3451
    {
3452 21
        $this->generatorToArray();
3453
3454 21
        return \array_key_last($this->array);
3455
    }
3456
3457
    /**
3458
     * Get the last value(s) from the current array.
3459
     *
3460
     * @param int|null $number
3461
     *
3462
     * @return static
3463
     *                <p>(Immutable)</p>
3464
     *
3465
     * @psalm-return static<TKey,T>
3466
     * @psalm-mutation-free
3467
     */
3468 13
    public function lastsImmutable(int $number = null): self
3469
    {
3470 13
        if ($this->isEmpty()) {
3471 1
            return static::create(
3472 1
                [],
3473 1
                $this->iteratorClass,
3474 1
                false
3475
            );
3476
        }
3477
3478 12
        if ($number === null) {
3479 8
            $poppedValue = $this->last();
3480
3481 8
            if ($poppedValue === null) {
3482 1
                $poppedValue = [$poppedValue];
3483
            } else {
3484 7
                $poppedValue = (array) $poppedValue;
3485
            }
3486
3487 8
            $arrayy = static::create(
3488 8
                $poppedValue,
3489 8
                $this->iteratorClass,
3490 8
                false
3491
            );
3492
        } else {
3493 4
            $number = (int) $number;
3494 4
            $arrayy = $this->rest(-$number);
3495
        }
3496
3497 12
        return $arrayy;
3498
    }
3499
3500
    /**
3501
     * Get the last value(s) from the current array.
3502
     *
3503
     * @param int|null $number
3504
     *
3505
     * @return $this
3506
     *               <p>(Mutable)</p>
3507
     *
3508
     * @psalm-return static<TKey,T>
3509
     */
3510 13
    public function lastsMutable(int $number = null): self
3511
    {
3512 13
        if ($this->isEmpty()) {
3513 1
            return $this;
3514
        }
3515
3516 12
        if ($number === null) {
3517 8
            $poppedValue = $this->last();
3518
3519 8
            if ($poppedValue === null) {
3520 1
                $poppedValue = [$poppedValue];
3521
            } else {
3522 7
                $poppedValue = (array) $poppedValue;
3523
            }
3524
3525 8
            $this->array = static::create(
3526 8
                $poppedValue,
3527 8
                $this->iteratorClass,
3528 8
                false
3529 8
            )->toArray();
3530
        } else {
3531 4
            $number = (int) $number;
3532 4
            $this->array = $this->rest(-$number)->toArray();
3533
        }
3534
3535 12
        $this->generator = null;
3536
3537 12
        return $this;
3538
    }
3539
3540
    /**
3541
     * Count the values from the current array.
3542
     *
3543
     * alias: for "Arrayy->count()"
3544
     *
3545
     * @param int $mode
3546
     *
3547
     * @return int
3548
     *
3549
     * @see Arrayy::count()
3550
     */
3551 20
    public function length(int $mode = \COUNT_NORMAL): int
3552
    {
3553 20
        return $this->count($mode);
3554
    }
3555
3556
    /**
3557
     * Apply the given function to the every element of the array,
3558
     * collecting the results.
3559
     *
3560
     * @param callable $callable
3561
     * @param bool     $useKeyAsSecondParameter
3562
     * @param mixed    ...$arguments
3563
     *
3564
     * @return static
3565
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3566
     *
3567
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3568
     * @psalm-return static<TKey,T>
3569
     * @psalm-mutation-free
3570
     */
3571 5
    public function map(
3572
        callable $callable,
3573
        bool $useKeyAsSecondParameter = false,
3574
        ...$arguments
3575
    ) {
3576
        /**
3577
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3578
         */
3579 5
        $useArguments = \func_num_args() > 2;
3580
3581 5
        return static::create(
3582
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3583 5
                foreach ($this->getGenerator() as $key => $value) {
3584 4
                    if ($useArguments) {
3585 3
                        if ($useKeyAsSecondParameter) {
3586
                            yield $key => $callable($value, $key, ...$arguments);
3587
                        } else {
3588 3
                            yield $key => $callable($value, ...$arguments);
3589
                        }
3590
                    } else {
3591
                        /** @noinspection NestedPositiveIfStatementsInspection */
3592 4
                        if ($useKeyAsSecondParameter) {
3593
                            yield $key => $callable($value, $key);
3594
                        } else {
3595 4
                            yield $key => $callable($value);
3596
                        }
3597
                    }
3598
                }
3599 5
            },
3600 5
            $this->iteratorClass,
3601 5
            false
3602
        );
3603
    }
3604
3605
    /**
3606
     * Check if all items in current array match a truth test.
3607
     *
3608
     * @param \Closure $closure
3609
     *
3610
     * @return bool
3611
     */
3612 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...
3613
    {
3614 15
        if ($this->count() === 0) {
3615 2
            return false;
3616
        }
3617
3618 13
        foreach ($this->getGenerator() as $key => $value) {
3619 13
            $value = $closure($value, $key);
3620
3621 13
            if ($value === false) {
3622 13
                return false;
3623
            }
3624
        }
3625
3626 7
        return true;
3627
    }
3628
3629
    /**
3630
     * Check if any item in the current array matches a truth test.
3631
     *
3632
     * @param \Closure $closure
3633
     *
3634
     * @return bool
3635
     */
3636 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...
3637
    {
3638 14
        if ($this->count() === 0) {
3639 2
            return false;
3640
        }
3641
3642 12
        foreach ($this->getGenerator() as $key => $value) {
3643 12
            $value = $closure($value, $key);
3644
3645 12
            if ($value === true) {
3646 12
                return true;
3647
            }
3648
        }
3649
3650 4
        return false;
3651
    }
3652
3653
    /**
3654
     * Get the max value from an array.
3655
     *
3656
     * @return false|mixed
3657
     *                     <p>Will return false if there are no values.</p>
3658
     */
3659 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...
3660
    {
3661 10
        if ($this->count() === 0) {
3662 1
            return false;
3663
        }
3664
3665 9
        $max = false;
3666 9
        foreach ($this->getGeneratorByReference() as &$value) {
3667
            if (
3668 9
                $max === false
3669
                ||
3670 9
                $value > $max
3671
            ) {
3672 9
                $max = $value;
3673
            }
3674
        }
3675
3676 9
        return $max;
3677
    }
3678
3679
    /**
3680
     * Merge the new $array into the current array.
3681
     *
3682
     * - keep key,value from the current array, also if the index is in the new $array
3683
     *
3684
     * @param array $array
3685
     * @param bool  $recursive
3686
     *
3687
     * @return static
3688
     *                <p>(Immutable)</p>
3689
     *
3690
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3691
     * @psalm-return static<int|TKey,T>
3692
     * @psalm-mutation-free
3693
     */
3694 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...
3695
    {
3696 32
        if ($recursive === true) {
3697 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
3698 9
            $result = \array_replace_recursive($this->toArray(), $array);
3699
        } else {
3700 23
            $result = \array_replace($this->toArray(), $array);
3701
        }
3702
3703 32
        return static::create(
3704 32
            $result,
3705 32
            $this->iteratorClass,
3706 32
            false
3707
        );
3708
    }
3709
3710
    /**
3711
     * Merge the new $array into the current array.
3712
     *
3713
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3714
     * - create new indexes
3715
     *
3716
     * @param array $array
3717
     * @param bool  $recursive
3718
     *
3719
     * @return static
3720
     *                <p>(Immutable)</p>
3721
     *
3722
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3723
     * @psalm-return static<TKey,T>
3724
     * @psalm-mutation-free
3725
     */
3726 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...
3727
    {
3728 19
        if ($recursive === true) {
3729 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
3730 5
            $result = \array_merge_recursive($this->toArray(), $array);
3731
        } else {
3732 14
            $result = \array_merge($this->toArray(), $array);
3733
        }
3734
3735 19
        return static::create(
3736 19
            $result,
3737 19
            $this->iteratorClass,
3738 19
            false
3739
        );
3740
    }
3741
3742
    /**
3743
     * Merge the the current array into the $array.
3744
     *
3745
     * - use key,value from the new $array, also if the index is in the current array
3746
     *
3747
     * @param array $array
3748
     * @param bool  $recursive
3749
     *
3750
     * @return static
3751
     *                <p>(Immutable)</p>
3752
     *
3753
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3754
     * @psalm-return static<TKey,T>
3755
     * @psalm-mutation-free
3756
     */
3757 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...
3758
    {
3759 16
        if ($recursive === true) {
3760 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
3761 4
            $result = \array_replace_recursive($array, $this->toArray());
3762
        } else {
3763 12
            $result = \array_replace($array, $this->toArray());
3764
        }
3765
3766 16
        return static::create(
3767 16
            $result,
3768 16
            $this->iteratorClass,
3769 16
            false
3770
        );
3771
    }
3772
3773
    /**
3774
     * Merge the current array into the new $array.
3775
     *
3776
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3777
     * - create new indexes
3778
     *
3779
     * @param array $array
3780
     * @param bool  $recursive
3781
     *
3782
     * @return static
3783
     *                <p>(Immutable)</p>
3784
     *
3785
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3786
     * @psalm-return static<TKey,T>
3787
     * @psalm-mutation-free
3788
     */
3789 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...
3790
    {
3791 20
        if ($recursive === true) {
3792 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
3793 7
            $result = \array_merge_recursive($array, $this->toArray());
3794
        } else {
3795 13
            $result = \array_merge($array, $this->toArray());
3796
        }
3797
3798 20
        return static::create(
3799 20
            $result,
3800 20
            $this->iteratorClass,
3801 20
            false
3802
        );
3803
    }
3804
3805
    /**
3806
     * @return ArrayyMeta|static
3807
     */
3808 16
    public static function meta()
3809
    {
3810 16
        return (new ArrayyMeta())->getMetaObject(static::class);
3811
    }
3812
3813
    /**
3814
     * Get the min value from an array.
3815
     *
3816
     * @return false|mixed
3817
     *                     <p>Will return false if there are no values.</p>
3818
     */
3819 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...
3820
    {
3821 10
        if ($this->count() === 0) {
3822 1
            return false;
3823
        }
3824
3825 9
        $min = false;
3826 9
        foreach ($this->getGeneratorByReference() as &$value) {
3827
            if (
3828 9
                $min === false
3829
                ||
3830 9
                $value < $min
3831
            ) {
3832 9
                $min = $value;
3833
            }
3834
        }
3835
3836 9
        return $min;
3837
    }
3838
3839
    /**
3840
     * Get the most used value from the array.
3841
     *
3842
     * @return mixed|null
3843
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
3844
     * @psalm-mutation-free
3845
     */
3846 3
    public function mostUsedValue()
3847
    {
3848 3
        return $this->countValues()->arsortImmutable()->firstKey();
3849
    }
3850
3851
    /**
3852
     * Get the most used value from the array.
3853
     *
3854
     * @param int|null $number <p>How many values you will take?</p>
3855
     *
3856
     * @return static
3857
     *                <p>(Immutable)</p>
3858
     *
3859
     * @psalm-return static<TKey,T>
3860
     * @psalm-mutation-free
3861
     */
3862 3
    public function mostUsedValues(int $number = null): self
3863
    {
3864 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
3865
    }
3866
3867
    /**
3868
     * Move an array element to a new index.
3869
     *
3870
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3871
     *
3872
     * @param int|string $from
3873
     * @param int        $to
3874
     *
3875
     * @return static
3876
     *                <p>(Immutable)</p>
3877
     *
3878
     * @psalm-return static<TKey,T>
3879
     * @psalm-mutation-free
3880
     */
3881 1
    public function moveElement($from, $to): self
3882
    {
3883 1
        $array = $this->toArray();
3884
3885 1
        if ((int) $from === $from) {
3886 1
            $tmp = \array_splice($array, $from, 1);
3887 1
            \array_splice($array, (int) $to, 0, $tmp);
3888 1
            $output = $array;
3889 1
        } elseif ((string) $from === $from) {
3890 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3891 1
            $itemToMove = $array[$from];
3892 1
            if ($indexToMove !== false) {
3893 1
                \array_splice($array, $indexToMove, 1);
3894
            }
3895 1
            $i = 0;
3896 1
            $output = [];
3897 1
            foreach ($array as $key => $item) {
3898 1
                if ($i === $to) {
3899 1
                    $output[$from] = $itemToMove;
3900
                }
3901 1
                $output[$key] = $item;
3902 1
                ++$i;
3903
            }
3904
        } else {
3905
            $output = [];
3906
        }
3907
3908 1
        return static::create(
3909 1
            $output,
3910 1
            $this->iteratorClass,
3911 1
            false
3912
        );
3913
    }
3914
3915
    /**
3916
     * Move an array element to the first place.
3917
     *
3918
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3919
     *       loss the keys of an indexed array.
3920
     *
3921
     * @param int|string $key
3922
     *
3923
     * @return static
3924
     *                <p>(Immutable)</p>
3925
     *
3926
     * @psalm-return static<TKey,T>
3927
     * @psalm-mutation-free
3928
     */
3929 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...
3930
    {
3931 1
        $array = $this->toArray();
3932
3933 1
        if ($this->offsetExists($key)) {
3934 1
            $tmpValue = $this->get($key);
3935 1
            unset($array[$key]);
3936 1
            $array = [$key => $tmpValue] + $array;
3937
        }
3938
3939 1
        return static::create(
3940 1
            $array,
3941 1
            $this->iteratorClass,
3942 1
            false
3943
        );
3944
    }
3945
3946
    /**
3947
     * Move an array element to the last place.
3948
     *
3949
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3950
     *       loss the keys of an indexed array.
3951
     *
3952
     * @param int|string $key
3953
     *
3954
     * @return static
3955
     *                <p>(Immutable)</p>
3956
     *
3957
     * @psalm-return static<TKey,T>
3958
     * @psalm-mutation-free
3959
     */
3960 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...
3961
    {
3962 1
        $array = $this->toArray();
3963
3964 1
        if ($this->offsetExists($key)) {
3965 1
            $tmpValue = $this->get($key);
3966 1
            unset($array[$key]);
3967 1
            $array += [$key => $tmpValue];
3968
        }
3969
3970 1
        return static::create(
3971 1
            $array,
3972 1
            $this->iteratorClass,
3973 1
            false
3974
        );
3975
    }
3976
3977
    /**
3978
     * Moves the internal iterator position to the next element and returns this element.
3979
     *
3980
     * @return false|mixed
3981
     *                     <p>(Mutable) Will return false if there are no values.</p>
3982
     */
3983
    public function next()
3984
    {
3985
        return \next($this->array);
3986
    }
3987
3988
    /**
3989
     * Get the next nth keys and values from the array.
3990
     *
3991
     * @param int $step
3992
     * @param int $offset
3993
     *
3994
     * @return static
3995
     *                <p>(Immutable)</p>
3996
     *
3997
     * @psalm-return static<TKey,T>
3998
     * @psalm-mutation-free
3999
     */
4000 1
    public function nth(int $step, int $offset = 0): self
4001
    {
4002
        $arrayFunction = function () use ($step, $offset): \Generator {
4003 1
            $position = 0;
4004 1
            foreach ($this->getGenerator() as $key => $value) {
4005 1
                if ($position++ % $step !== $offset) {
4006 1
                    continue;
4007
                }
4008
4009 1
                yield $key => $value;
4010
            }
4011 1
        };
4012
4013 1
        return static::create(
4014 1
            $arrayFunction,
4015 1
            $this->iteratorClass,
4016 1
            false
4017
        );
4018
    }
4019
4020
    /**
4021
     * Get a subset of the items from the given array.
4022
     *
4023
     * @param mixed[] $keys
4024
     *
4025
     * @return static
4026
     *                <p>(Immutable)</p>
4027
     *
4028
     * @psalm-return static<TKey,T>
4029
     * @psalm-mutation-free
4030
     */
4031 1
    public function only(array $keys): self
4032
    {
4033 1
        $array = $this->toArray();
4034
4035 1
        return static::create(
4036 1
            \array_intersect_key($array, \array_flip($keys)),
4037 1
            $this->iteratorClass,
4038 1
            false
4039
        );
4040
    }
4041
4042
    /**
4043
     * Pad array to the specified size with a given value.
4044
     *
4045
     * @param int   $size  <p>Size of the result array.</p>
4046
     * @param mixed $value <p>Empty value by default.</p>
4047
     *
4048
     * @return static
4049
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4050
     *
4051
     * @psalm-return static<TKey,T>
4052
     * @psalm-mutation-free
4053
     */
4054 5
    public function pad(int $size, $value): self
4055
    {
4056 5
        return static::create(
4057 5
            \array_pad($this->toArray(), $size, $value),
4058 5
            $this->iteratorClass,
4059 5
            false
4060
        );
4061
    }
4062
4063
    /**
4064
     * Partitions this array in two array according to a predicate.
4065
     * Keys are preserved in the resulting array.
4066
     *
4067
     * @param \Closure $closure
4068
     *                          <p>The predicate on which to partition.</p>
4069
     *
4070
     * @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...
4071
     *                    <p>An array with two elements. The first element contains the array
4072
     *                    of elements where the predicate returned TRUE, the second element
4073
     *                    contains the array of elements where the predicate returned FALSE.</p>
4074
     *
4075
     * @psalm-return array<int, static<TKey,T>>
4076
     */
4077 1
    public function partition(\Closure $closure): array
4078
    {
4079
        // init
4080 1
        $matches = [];
4081 1
        $noMatches = [];
4082
4083 1
        foreach ($this->array as $key => $value) {
4084 1
            if ($closure($value, $key)) {
4085 1
                $matches[$key] = $value;
4086
            } else {
4087 1
                $noMatches[$key] = $value;
4088
            }
4089
        }
4090
4091 1
        return [self::create($matches), self::create($noMatches)];
4092
    }
4093
4094
    /**
4095
     * Pop a specified value off the end of the current array.
4096
     *
4097
     * @return mixed|null
4098
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4099
     */
4100 5
    public function pop()
4101
    {
4102 5
        $this->generatorToArray();
4103
4104 5
        return \array_pop($this->array);
4105
    }
4106
4107
    /**
4108
     * Prepend a (key) + value to the current array.
4109
     *
4110
     * @param mixed $value
4111
     * @param mixed $key
4112
     *
4113
     * @return $this
4114
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4115
     *
4116
     * @psalm-return static<TKey,T>
4117
     */
4118 11
    public function prepend($value, $key = null)
4119
    {
4120 11
        $this->generatorToArray();
4121
4122 11
        if ($this->properties !== []) {
4123 3
            $this->checkType($key, $value);
4124
        }
4125
4126 9
        if ($key === null) {
4127 8
            \array_unshift($this->array, $value);
4128
        } else {
4129 2
            $this->array = [$key => $value] + $this->array;
4130
        }
4131
4132 9
        return $this;
4133
    }
4134
4135
    /**
4136
     * Add a suffix to each key.
4137
     *
4138
     * @param mixed $suffix
4139
     *
4140
     * @return static
4141
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4142
     *
4143
     * @psalm-return static<TKey,T>
4144
     * @psalm-mutation-free
4145
     */
4146 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...
4147
    {
4148
        // init
4149 10
        $result = [];
4150
4151 10
        foreach ($this->getGenerator() as $key => $item) {
4152 9
            if ($item instanceof self) {
4153
                $result[$key] = $item->prependToEachKey($suffix);
4154 9
            } elseif (\is_array($item) === true) {
4155
                $result[$key] = self::create(
4156
                    $item,
4157
                    $this->iteratorClass,
4158
                    false
4159
                )->prependToEachKey($suffix)
4160
                    ->toArray();
4161
            } else {
4162 9
                $result[$key . $suffix] = $item;
4163
            }
4164
        }
4165
4166 10
        return self::create(
4167 10
            $result,
4168 10
            $this->iteratorClass,
4169 10
            false
4170
        );
4171
    }
4172
4173
    /**
4174
     * Add a suffix to each value.
4175
     *
4176
     * @param mixed $suffix
4177
     *
4178
     * @return static
4179
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4180
     *
4181
     * @psalm-return static<TKey,T>
4182
     * @psalm-mutation-free
4183
     */
4184 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...
4185
    {
4186
        // init
4187 10
        $result = [];
4188
4189 10
        foreach ($this->getGenerator() as $key => $item) {
4190 9
            if ($item instanceof self) {
4191
                $result[$key] = $item->prependToEachValue($suffix);
4192 9
            } elseif (\is_array($item) === true) {
4193
                $result[$key] = self::create(
4194
                    $item,
4195
                    $this->iteratorClass,
4196
                    false
4197
                )->prependToEachValue($suffix)
4198
                    ->toArray();
4199 9
            } elseif (\is_object($item) === true) {
4200 1
                $result[$key] = $item;
4201
            } else {
4202 9
                $result[$key] = $item . $suffix;
4203
            }
4204
        }
4205
4206 10
        return self::create(
4207 10
            $result,
4208 10
            $this->iteratorClass,
4209 10
            false
4210
        );
4211
    }
4212
4213
    /**
4214
     * Return the value of a given key and
4215
     * delete the key.
4216
     *
4217
     * @param int|int[]|string|string[]|null $keyOrKeys
4218
     * @param mixed                          $fallback
4219
     *
4220
     * @return mixed
4221
     */
4222 5
    public function pull($keyOrKeys = null, $fallback = null)
4223
    {
4224 5
        if ($keyOrKeys === null) {
4225 1
            $array = $this->toArray();
4226 1
            $this->clear();
4227
4228 1
            return $array;
4229
        }
4230
4231 4
        if (\is_array($keyOrKeys) === true) {
4232 1
            $valueOrValues = [];
4233 1
            foreach ($keyOrKeys as $key) {
4234 1
                $valueOrValues[] = $this->get($key, $fallback);
4235 1
                $this->offsetUnset($key);
4236
            }
4237
        } else {
4238 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4239 4
            $this->offsetUnset($keyOrKeys);
4240
        }
4241
4242 4
        return $valueOrValues;
4243
    }
4244
4245
    /**
4246
     * Push one or more values onto the end of array at once.
4247
     *
4248
     * @param array ...$args
4249
     *
4250
     * @return $this
4251
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4252
     *
4253
     * @noinspection ReturnTypeCanBeDeclaredInspection
4254
     *
4255
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4256
     * @psalm-return static<TKey,T>
4257
     */
4258 7
    public function push(...$args)
4259
    {
4260 7
        $this->generatorToArray();
4261
4262
        if (
4263 7
            $this->checkPropertyTypes
4264
            &&
4265 7
            $this->properties !== []
4266
        ) {
4267 1
            foreach ($args as $key => $value) {
4268 1
                $this->checkType($key, $value);
4269
            }
4270
        }
4271
4272 7
        \array_push($this->array, ...$args);
4273
4274 7
        return $this;
4275
    }
4276
4277
    /**
4278
     * Get a random value from the current array.
4279
     *
4280
     * @param int|null $number <p>How many values you will take?</p>
4281
     *
4282
     * @return static
4283
     *                <p>(Immutable)</p>
4284
     *
4285
     * @psalm-return static<int|array-key,T>
4286
     */
4287 19
    public function randomImmutable(int $number = null): self
4288
    {
4289 19
        $this->generatorToArray();
4290
4291 19
        if ($this->count() === 0) {
4292 1
            return static::create(
4293 1
                [],
4294 1
                $this->iteratorClass,
4295 1
                false
4296
            );
4297
        }
4298
4299 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...
4300
            /** @noinspection NonSecureArrayRandUsageInspection */
4301 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4302
4303 13
            return static::create(
4304 13
                $arrayRandValue,
4305 13
                $this->iteratorClass,
4306 13
                false
4307
            );
4308
        }
4309
4310 6
        $arrayTmp = $this->array;
4311
        /** @noinspection NonSecureShuffleUsageInspection */
4312 6
        \shuffle($arrayTmp);
4313
4314 6
        return static::create(
4315 6
            $arrayTmp,
4316 6
            $this->iteratorClass,
4317 6
            false
4318 6
        )->firstsImmutable($number);
4319
    }
4320
4321
    /**
4322
     * Pick a random key/index from the keys of this array.
4323
     *
4324
     * @throws \RangeException If array is empty
4325
     *
4326
     * @return mixed
4327
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4328
     */
4329 4
    public function randomKey()
4330
    {
4331 4
        $result = $this->randomKeys(1);
4332
4333 4
        if (!isset($result[0])) {
4334
            $result[0] = null;
4335
        }
4336
4337 4
        return $result[0];
4338
    }
4339
4340
    /**
4341
     * Pick a given number of random keys/indexes out of this array.
4342
     *
4343
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4344
     *
4345
     * @throws \RangeException If array is empty
4346
     *
4347
     * @return static
4348
     *                <p>(Immutable)</p>
4349
     *
4350
     * @psalm-return static<TKey,T>
4351
     */
4352 13
    public function randomKeys(int $number): self
4353
    {
4354 13
        $this->generatorToArray();
4355
4356 13
        $count = $this->count();
4357
4358
        if (
4359 13
            $number === 0
4360
            ||
4361 13
            $number > $count
4362
        ) {
4363 2
            throw new \RangeException(
4364 2
                \sprintf(
4365 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4366 2
                    $number,
4367 2
                    $count
4368
                )
4369
            );
4370
        }
4371
4372 11
        $result = (array) \array_rand($this->array, $number);
4373
4374 11
        return static::create(
4375 11
            $result,
4376 11
            $this->iteratorClass,
4377 11
            false
4378
        );
4379
    }
4380
4381
    /**
4382
     * Get a random value from the current array.
4383
     *
4384
     * @param int|null $number <p>How many values you will take?</p>
4385
     *
4386
     * @return $this
4387
     *               <p>(Mutable) Return this Arrayy object.</p>
4388
     *
4389
     * @psalm-return static<TKey,T>
4390
     */
4391 17
    public function randomMutable(int $number = null): self
4392
    {
4393 17
        $this->generatorToArray();
4394
4395 17
        if ($this->count() === 0) {
4396
            return static::create(
4397
                [],
4398
                $this->iteratorClass,
4399
                false
4400
            );
4401
        }
4402
4403 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...
4404
            /** @noinspection NonSecureArrayRandUsageInspection */
4405 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4406 7
            $this->array = $arrayRandValue;
4407
4408 7
            return $this;
4409
        }
4410
4411
        /** @noinspection NonSecureShuffleUsageInspection */
4412 11
        \shuffle($this->array);
4413
4414 11
        return $this->firstsMutable($number);
4415
    }
4416
4417
    /**
4418
     * Pick a random value from the values of this array.
4419
     *
4420
     * @return mixed
4421
     *               <p>Get a random value or null if there wasn't a value.</p>
4422
     */
4423 4
    public function randomValue()
4424
    {
4425 4
        $result = $this->randomImmutable();
4426
4427 4
        if (!isset($result[0])) {
4428
            $result[0] = null;
4429
        }
4430
4431 4
        return $result[0];
4432
    }
4433
4434
    /**
4435
     * Pick a given number of random values out of this array.
4436
     *
4437
     * @param int $number
4438
     *
4439
     * @return static
4440
     *                <p>(Mutable)</p>
4441
     *
4442
     * @psalm-return static<TKey,T>
4443
     */
4444 7
    public function randomValues(int $number): self
4445
    {
4446 7
        return $this->randomMutable($number);
4447
    }
4448
4449
    /**
4450
     * Get a random value from an array, with the ability to skew the results.
4451
     *
4452
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4453
     *
4454
     * @param array    $array
4455
     * @param int|null $number <p>How many values you will take?</p>
4456
     *
4457
     * @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...
4458
     *                           <p>(Immutable)</p>
4459
     *
4460
     * @psalm-param  array<mixed,mixed> $array
4461
     * @psalm-return static<int|array-key,T>
4462
     */
4463 9
    public function randomWeighted(array $array, int $number = null): self
4464
    {
4465
        // init
4466 9
        $options = [];
4467
4468 9
        foreach ($array as $option => $weight) {
4469 9
            if ($this->searchIndex($option) !== false) {
4470 9
                for ($i = 0; $i < $weight; ++$i) {
4471 1
                    $options[] = $option;
4472
                }
4473
            }
4474
        }
4475
4476 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4477
    }
4478
4479
    /**
4480
     * Reduce the current array via callable e.g. anonymous-function.
4481
     *
4482
     * @param callable $callable
4483
     * @param mixed    $init
4484
     *
4485
     * @return static
4486
     *                <p>(Immutable)</p>
4487
     *
4488
     * @psalm-return static<TKey,T>
4489
     * @psalm-mutation-free
4490
     */
4491 18
    public function reduce($callable, $init = []): self
4492
    {
4493 18
        if ($this->generator) {
4494 1
            $result = $init;
4495
4496 1
            foreach ($this->getGenerator() as $value) {
4497 1
                $result = $callable($result, $value);
4498
            }
4499
4500 1
            return static::create(
4501 1
                $result,
4502 1
                $this->iteratorClass,
4503 1
                false
4504
            );
4505
        }
4506
4507 18
        $result = \array_reduce($this->array, $callable, $init);
4508
4509 18
        if ($result === null) {
4510
            $this->array = [];
4511
        } else {
4512 18
            $this->array = (array) $result;
4513
        }
4514
4515 18
        return static::create(
4516 18
            $this->array,
4517 18
            $this->iteratorClass,
4518 18
            false
4519
        );
4520
    }
4521
4522
    /**
4523
     * @param bool $unique
4524
     *
4525
     * @return static
4526
     *                <p>(Immutable)</p>
4527
     *
4528
     * @psalm-return static<TKey,T>
4529
     * @psalm-mutation-free
4530
     */
4531 14
    public function reduce_dimension(bool $unique = true): self
4532
    {
4533
        // init
4534 14
        $result = [];
4535
4536 14
        foreach ($this->getGenerator() as $val) {
4537 12
            if (\is_array($val) === true) {
4538 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4539
            } else {
4540 12
                $result[] = [$val];
4541
            }
4542
        }
4543
4544 14
        $result = $result === [] ? [] : \array_merge(...$result);
4545
4546 14
        $resultArrayy = new static($result);
4547
4548
        /**
4549
         * @psalm-suppress ImpureMethodCall - object is already re-created
4550
         * @psalm-suppress InvalidReturnStatement - why?
4551
         */
4552 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4553
    }
4554
4555
    /**
4556
     * Create a numerically re-indexed Arrayy object.
4557
     *
4558
     * @return $this
4559
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4560
     *
4561
     * @psalm-return static<TKey,T>
4562
     */
4563 9
    public function reindex(): self
4564
    {
4565 9
        $this->generatorToArray(false);
4566
4567 9
        $this->array = \array_values($this->array);
4568
4569 9
        return $this;
4570
    }
4571
4572
    /**
4573
     * Return all items that fail the truth test.
4574
     *
4575
     * @param \Closure $closure
4576
     *
4577
     * @return static
4578
     *                <p>(Immutable)</p>
4579
     *
4580
     * @psalm-return static<TKey,T>
4581
     * @psalm-mutation-free
4582
     */
4583 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...
4584
    {
4585
        // init
4586 1
        $filtered = [];
4587
4588 1
        foreach ($this->getGenerator() as $key => $value) {
4589 1
            if (!$closure($value, $key)) {
4590 1
                $filtered[$key] = $value;
4591
            }
4592
        }
4593
4594 1
        return static::create(
4595 1
            $filtered,
4596 1
            $this->iteratorClass,
4597 1
            false
4598
        );
4599
    }
4600
4601
    /**
4602
     * Remove a value from the current array (optional using dot-notation).
4603
     *
4604
     * @param mixed $key
4605
     *
4606
     * @return static
4607
     *                <p>(Mutable)</p>
4608
     *
4609
     * @psalm-param  TKey $key
4610
     * @psalm-return static<TKey,T>
4611
     */
4612 21
    public function remove($key)
4613
    {
4614
        // recursive call
4615 21
        if (\is_array($key) === true) {
4616
            foreach ($key as $k) {
4617
                $this->internalRemove($k);
4618
            }
4619
4620
            return static::create(
4621
                $this->toArray(),
4622
                $this->iteratorClass,
4623
                false
4624
            );
4625
        }
4626
4627 21
        $this->internalRemove($key);
4628
4629 21
        return static::create(
4630 21
            $this->toArray(),
4631 21
            $this->iteratorClass,
4632 21
            false
4633
        );
4634
    }
4635
4636
    /**
4637
     * alias: for "Arrayy->removeValue()"
4638
     *
4639
     * @param mixed $element
4640
     *
4641
     * @return static
4642
     *                <p>(Immutable)</p>
4643
     *
4644
     * @psalm-param  T $element
4645
     * @psalm-return static<TKey,T>
4646
     * @psalm-mutation-free
4647
     */
4648 8
    public function removeElement($element)
4649
    {
4650 8
        return $this->removeValue($element);
4651
    }
4652
4653
    /**
4654
     * Remove the first value from the current array.
4655
     *
4656
     * @return static
4657
     *                <p>(Immutable)</p>
4658
     *
4659
     * @psalm-return static<TKey,T>
4660
     * @psalm-mutation-free
4661
     */
4662 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...
4663
    {
4664 7
        $tmpArray = $this->toArray();
4665
4666 7
        \array_shift($tmpArray);
4667
4668 7
        return static::create(
4669 7
            $tmpArray,
4670 7
            $this->iteratorClass,
4671 7
            false
4672
        );
4673
    }
4674
4675
    /**
4676
     * Remove the last value from the current array.
4677
     *
4678
     * @return static
4679
     *                <p>(Immutable)</p>
4680
     *
4681
     * @psalm-return static<TKey,T>
4682
     * @psalm-mutation-free
4683
     */
4684 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...
4685
    {
4686 7
        $tmpArray = $this->toArray();
4687
4688 7
        \array_pop($tmpArray);
4689
4690 7
        return static::create(
4691 7
            $tmpArray,
4692 7
            $this->iteratorClass,
4693 7
            false
4694
        );
4695
    }
4696
4697
    /**
4698
     * Removes a particular value from an array (numeric or associative).
4699
     *
4700
     * @param mixed $value
4701
     *
4702
     * @return static
4703
     *                <p>(Immutable)</p>
4704
     *
4705
     * @psalm-param  T $value
4706
     * @psalm-return static<TKey,T>
4707
     * @psalm-mutation-free
4708
     */
4709 8
    public function removeValue($value): self
4710
    {
4711 8
        $this->generatorToArray();
4712
4713
        // init
4714 8
        $isSequentialArray = $this->isSequential();
4715
4716 8
        foreach ($this->array as $key => $item) {
4717 7
            if ($item === $value) {
4718 7
                unset($this->array[$key]);
4719
            }
4720
        }
4721
4722 8
        if ($isSequentialArray) {
4723 6
            $this->array = \array_values($this->array);
4724
        }
4725
4726 8
        return static::create(
4727 8
            $this->array,
4728 8
            $this->iteratorClass,
4729 8
            false
4730
        );
4731
    }
4732
4733
    /**
4734
     * Generate array of repeated arrays.
4735
     *
4736
     * @param int $times <p>How many times has to be repeated.</p>
4737
     *
4738
     * @return static
4739
     *                <p>(Immutable)</p>
4740
     *
4741
     * @psalm-return static<TKey,T>
4742
     * @psalm-mutation-free
4743
     */
4744 1
    public function repeat($times): self
4745
    {
4746 1
        if ($times === 0) {
4747 1
            return static::create([], $this->iteratorClass);
4748
        }
4749
4750 1
        return static::create(
4751 1
            \array_fill(0, (int) $times, $this->toArray()),
4752 1
            $this->iteratorClass,
4753 1
            false
4754
        );
4755
    }
4756
4757
    /**
4758
     * Replace a key with a new key/value pair.
4759
     *
4760
     * @param mixed $oldKey
4761
     * @param mixed $newKey
4762
     * @param mixed $newValue
4763
     *
4764
     * @return static
4765
     *                <p>(Immutable)</p>
4766
     *
4767
     * @psalm-return static<TKey,T>
4768
     * @psalm-mutation-free
4769
     */
4770 5
    public function replace($oldKey, $newKey, $newValue): self
4771
    {
4772 5
        $that = clone $this;
4773
4774
        /**
4775
         * @psalm-suppress ImpureMethodCall - object is already cloned
4776
         */
4777 5
        return $that->remove($oldKey)
4778 5
            ->set($newKey, $newValue);
4779
    }
4780
4781
    /**
4782
     * Create an array using the current array as values and the other array as keys.
4783
     *
4784
     * @param array $keys <p>An array of keys.</p>
4785
     *
4786
     * @return static
4787
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4788
     *
4789
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4790
     * @psalm-return static<TKey,T>
4791
     * @psalm-mutation-free
4792
     */
4793 2
    public function replaceAllKeys(array $keys): self
4794
    {
4795 2
        return static::create(
4796 2
            \array_combine($keys, $this->toArray()),
4797 2
            $this->iteratorClass,
4798 2
            false
4799
        );
4800
    }
4801
4802
    /**
4803
     * Create an array using the current array as keys and the other array as values.
4804
     *
4805
     * @param array $array <p>An array o values.</p>
4806
     *
4807
     * @return static
4808
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4809
     *
4810
     * @psalm-param  array<mixed,T> $array
4811
     * @psalm-return static<TKey,T>
4812
     * @psalm-mutation-free
4813
     */
4814 2
    public function replaceAllValues(array $array): self
4815
    {
4816 2
        return static::create(
4817 2
            \array_combine($this->array, $array),
4818 2
            $this->iteratorClass,
4819 2
            false
4820
        );
4821
    }
4822
4823
    /**
4824
     * Replace the keys in an array with another set.
4825
     *
4826
     * @param array $keys <p>An array of keys matching the array's size</p>
4827
     *
4828
     * @return static
4829
     *                <p>(Immutable)</p>
4830
     *
4831
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4832
     * @psalm-return static<TKey,T>
4833
     * @psalm-mutation-free
4834
     */
4835 1
    public function replaceKeys(array $keys): self
4836
    {
4837 1
        $values = \array_values($this->toArray());
4838 1
        $result = \array_combine($keys, $values);
4839
4840 1
        return static::create(
4841 1
            $result,
4842 1
            $this->iteratorClass,
4843 1
            false
4844
        );
4845
    }
4846
4847
    /**
4848
     * Replace the first matched value in an array.
4849
     *
4850
     * @param mixed $search      <p>The value to replace.</p>
4851
     * @param mixed $replacement <p>The value to replace.</p>
4852
     *
4853
     * @return static
4854
     *                <p>(Immutable)</p>
4855
     *
4856
     * @psalm-return static<TKey,T>
4857
     * @psalm-mutation-free
4858
     */
4859 3
    public function replaceOneValue($search, $replacement = ''): self
4860
    {
4861 3
        $array = $this->toArray();
4862 3
        $key = \array_search($search, $array, true);
4863
4864 3
        if ($key !== false) {
4865 3
            $array[$key] = $replacement;
4866
        }
4867
4868 3
        return static::create(
4869 3
            $array,
4870 3
            $this->iteratorClass,
4871 3
            false
4872
        );
4873
    }
4874
4875
    /**
4876
     * Replace values in the current array.
4877
     *
4878
     * @param mixed $search      <p>The value to replace.</p>
4879
     * @param mixed $replacement <p>What to replace it with.</p>
4880
     *
4881
     * @return static
4882
     *                <p>(Immutable)</p>
4883
     *
4884
     * @psalm-return static<TKey,T>
4885
     * @psalm-mutation-free
4886
     */
4887 1
    public function replaceValues($search, $replacement = ''): self
4888
    {
4889
        /**
4890
         * @psalm-suppress MissingClosureReturnType
4891
         * @psalm-suppress MissingClosureParamType
4892
         */
4893 1
        return $this->each(
4894
            static function ($value) use ($search, $replacement) {
4895 1
                return \str_replace($search, $replacement, $value);
4896 1
            }
4897
        );
4898
    }
4899
4900
    /**
4901
     * Get the last elements from index $from until the end of this array.
4902
     *
4903
     * @param int $from
4904
     *
4905
     * @return static
4906
     *                <p>(Immutable)</p>
4907
     *
4908
     * @psalm-return static<TKey,T>
4909
     * @psalm-mutation-free
4910
     */
4911 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...
4912
    {
4913 15
        $tmpArray = $this->toArray();
4914
4915 15
        return static::create(
4916 15
            \array_splice($tmpArray, $from),
4917 15
            $this->iteratorClass,
4918 15
            false
4919
        );
4920
    }
4921
4922
    /**
4923
     * Return the array in the reverse order.
4924
     *
4925
     * @return $this
4926
     *               <p>(Mutable) Return this Arrayy object.</p>
4927
     *
4928
     * @psalm-return static<TKey,T>
4929
     */
4930 9
    public function reverse(): self
4931
    {
4932 9
        $this->generatorToArray();
4933
4934 9
        $this->array = \array_reverse($this->array);
4935
4936 9
        return $this;
4937
    }
4938
4939
    /**
4940
     * Sort an array in reverse order.
4941
     *
4942
     * @param int $sort_flags [optional] <p>
4943
     *                        You may modify the behavior of the sort using the optional
4944
     *                        parameter sort_flags, for details
4945
     *                        see sort.
4946
     *                        </p>
4947
     *
4948
     * @return $this
4949
     *               <p>(Mutable) Return this Arrayy object.</p>
4950
     *
4951
     * @psalm-return static<TKey,T>
4952
     */
4953 4
    public function rsort(int $sort_flags = 0): self
4954
    {
4955 4
        $this->generatorToArray();
4956
4957 4
        \rsort($this->array, $sort_flags);
4958
4959 4
        return $this;
4960
    }
4961
4962
    /**
4963
     * Sort an array in reverse order.
4964
     *
4965
     * @param int $sort_flags [optional] <p>
4966
     *                        You may modify the behavior of the sort using the optional
4967
     *                        parameter sort_flags, for details
4968
     *                        see sort.
4969
     *                        </p>
4970
     *
4971
     * @return $this
4972
     *               <p>(Immutable) Return this Arrayy object.</p>
4973
     *
4974
     * @psalm-return static<TKey,T>
4975
     * @psalm-mutation-free
4976
     */
4977 4
    public function rsortImmutable(int $sort_flags = 0): self
4978
    {
4979 4
        $that = clone $this;
4980
4981
        /**
4982
         * @psalm-suppress ImpureMethodCall - object is already cloned
4983
         */
4984 4
        $that->rsort($sort_flags);
4985
4986 4
        return $that;
4987
    }
4988
4989
    /**
4990
     * Search for the first index of the current array via $value.
4991
     *
4992
     * @param mixed $value
4993
     *
4994
     * @return false|float|int|string
4995
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4996
     * @psalm-mutation-free
4997
     */
4998 21
    public function searchIndex($value)
4999
    {
5000 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5001 20
            if ($value === $valueFromArray) {
5002 20
                return $keyFromArray;
5003
            }
5004
        }
5005
5006 11
        return false;
5007
    }
5008
5009
    /**
5010
     * Search for the value of the current array via $index.
5011
     *
5012
     * @param mixed $index
5013
     *
5014
     * @return static
5015
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5016
     *
5017
     * @psalm-return static<TKey,T>
5018
     * @psalm-mutation-free
5019
     */
5020 9
    public function searchValue($index): self
5021
    {
5022 9
        $this->generatorToArray();
5023
5024
        // init
5025 9
        $return = [];
5026
5027 9
        if ($this->array === []) {
5028
            return static::create(
5029
                [],
5030
                $this->iteratorClass,
5031
                false
5032
            );
5033
        }
5034
5035
        // php cast "bool"-index into "int"-index
5036 9
        if ((bool) $index === $index) {
5037 1
            $index = (int) $index;
5038
        }
5039
5040 9
        if ($this->offsetExists($index)) {
5041 7
            $return = [$this->array[$index]];
5042
        }
5043
5044 9
        return static::create(
5045 9
            $return,
5046 9
            $this->iteratorClass,
5047 9
            false
5048
        );
5049
    }
5050
5051
    /**
5052
     * Set a value for the current array (optional using dot-notation).
5053
     *
5054
     * @param string $key   <p>The key to set.</p>
5055
     * @param mixed  $value <p>Its value.</p>
5056
     *
5057
     * @return $this
5058
     *               <p>(Mutable) Return this Arrayy object.</p>
5059
     *
5060
     * @psalm-param  TKey $key
5061
     * @psalm-param  T $value
5062
     * @psalm-return static<TKey,T>
5063
     */
5064 28
    public function set($key, $value): self
5065
    {
5066 28
        $this->internalSet($key, $value);
5067
5068 27
        return $this;
5069
    }
5070
5071
    /**
5072
     * Get a value from a array and set it if it was not.
5073
     *
5074
     * WARNING: this method only set the value, if the $key is not already set
5075
     *
5076
     * @param mixed $key      <p>The key</p>
5077
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5078
     *
5079
     * @return mixed
5080
     *               <p>(Mutable)</p>
5081
     */
5082 11
    public function setAndGet($key, $fallback = null)
5083
    {
5084 11
        $this->generatorToArray();
5085
5086
        // If the key doesn't exist, set it.
5087 11
        if (!$this->has($key)) {
5088 4
            $this->array = $this->set($key, $fallback)->toArray();
5089
        }
5090
5091 11
        return $this->get($key);
5092
    }
5093
5094
    /**
5095
     * Shifts a specified value off the beginning of array.
5096
     *
5097
     * @return mixed
5098
     *               <p>(Mutable) A shifted element from the current array.</p>
5099
     */
5100 5
    public function shift()
5101
    {
5102 5
        $this->generatorToArray();
5103
5104 5
        return \array_shift($this->array);
5105
    }
5106
5107
    /**
5108
     * Shuffle the current array.
5109
     *
5110
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5111
     * @param array $array  [optional]
5112
     *
5113
     * @return static
5114
     *                <p>(Immutable)</p>
5115
     *
5116
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5117
     * @psalm-return static<TKey,T>
5118
     *
5119
     * @noinspection BadExceptionsProcessingInspection
5120
     * @noinspection RandomApiMigrationInspection
5121
     * @noinspection NonSecureShuffleUsageInspection
5122
     */
5123 2
    public function shuffle(bool $secure = false, array $array = null): self
5124
    {
5125 2
        if ($array === null) {
5126 2
            $array = $this->toArray(false);
5127
        }
5128
5129 2
        if ($secure !== true) {
5130 2
            \shuffle($array);
5131
        } else {
5132 1
            $size = \count($array, \COUNT_NORMAL);
5133 1
            $keys = \array_keys($array);
5134 1
            for ($i = $size - 1; $i > 0; --$i) {
5135
                try {
5136 1
                    $r = \random_int(0, $i);
5137
                } catch (\Exception $e) {
5138
                    $r = \mt_rand(0, $i);
5139
                }
5140 1
                if ($r !== $i) {
5141 1
                    $temp = $array[$keys[$r]];
5142 1
                    $array[$keys[$r]] = $array[$keys[$i]];
5143 1
                    $array[$keys[$i]] = $temp;
5144
                }
5145
            }
5146
        }
5147
5148 2
        foreach ($array as $key => $value) {
5149
            // check if recursive is needed
5150 2
            if (\is_array($value) === true) {
5151 2
                $array[$key] = $this->shuffle($secure, $value);
5152
            }
5153
        }
5154
5155 2
        return static::create(
5156 2
            $array,
5157 2
            $this->iteratorClass,
5158 2
            false
5159
        );
5160
    }
5161
5162
    /**
5163
     * Count the values from the current array.
5164
     *
5165
     * alias: for "Arrayy->count()"
5166
     *
5167
     * @param int $mode
5168
     *
5169
     * @return int
5170
     */
5171 20
    public function size(int $mode = \COUNT_NORMAL): int
5172
    {
5173 20
        return $this->count($mode);
5174
    }
5175
5176
    /**
5177
     * Checks whether array has exactly $size items.
5178
     *
5179
     * @param int $size
5180
     *
5181
     * @return bool
5182
     */
5183 1
    public function sizeIs(int $size): bool
5184
    {
5185
        // init
5186 1
        $itemsTempCount = 0;
5187
5188
        /** @noinspection PhpUnusedLocalVariableInspection */
5189 1
        foreach ($this->getGeneratorByReference() as &$value) {
5190 1
            ++$itemsTempCount;
5191 1
            if ($itemsTempCount > $size) {
5192 1
                return false;
5193
            }
5194
        }
5195
5196 1
        return $itemsTempCount === $size;
5197
    }
5198
5199
    /**
5200
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5201
     * smaller than $fromSize.
5202
     *
5203
     * @param int $fromSize
5204
     * @param int $toSize
5205
     *
5206
     * @return bool
5207
     */
5208 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5209
    {
5210 1
        if ($fromSize > $toSize) {
5211 1
            $tmp = $toSize;
5212 1
            $toSize = $fromSize;
5213 1
            $fromSize = $tmp;
5214
        }
5215
5216
        // init
5217 1
        $itemsTempCount = 0;
5218
5219 1
        foreach ($this->getGenerator() as $key => $value) {
5220 1
            ++$itemsTempCount;
5221 1
            if ($itemsTempCount > $toSize) {
5222 1
                return false;
5223
            }
5224
        }
5225
5226 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5227
    }
5228
5229
    /**
5230
     * Checks whether array has more than $size items.
5231
     *
5232
     * @param int $size
5233
     *
5234
     * @return bool
5235
     */
5236 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...
5237
    {
5238
        // init
5239 1
        $itemsTempCount = 0;
5240
5241 1
        foreach ($this->getGenerator() as $key => $value) {
5242 1
            ++$itemsTempCount;
5243 1
            if ($itemsTempCount > $size) {
5244 1
                return true;
5245
            }
5246
        }
5247
5248 1
        return $itemsTempCount > $size;
5249
    }
5250
5251
    /**
5252
     * Checks whether array has less than $size items.
5253
     *
5254
     * @param int $size
5255
     *
5256
     * @return bool
5257
     */
5258 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...
5259
    {
5260
        // init
5261 1
        $itemsTempCount = 0;
5262
5263 1
        foreach ($this->getGenerator() as $key => $value) {
5264 1
            ++$itemsTempCount;
5265 1
            if ($itemsTempCount > $size) {
5266 1
                return false;
5267
            }
5268
        }
5269
5270 1
        return $itemsTempCount < $size;
5271
    }
5272
5273
    /**
5274
     * Counts all elements in an array, or something in an object.
5275
     *
5276
     * <p>
5277
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5278
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5279
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5280
     * implemented and used in PHP.
5281
     * </p>
5282
     *
5283
     * @return int
5284
     *             <p>
5285
     *             The number of elements in var, which is
5286
     *             typically an array, since anything else will have one
5287
     *             element.
5288
     *             </p>
5289
     *             <p>
5290
     *             If var is not an array or an object with
5291
     *             implemented Countable interface,
5292
     *             1 will be returned.
5293
     *             There is one exception, if var is &null;,
5294
     *             0 will be returned.
5295
     *             </p>
5296
     *             <p>
5297
     *             Caution: count may return 0 for a variable that isn't set,
5298
     *             but it may also return 0 for a variable that has been initialized with an
5299
     *             empty array. Use isset to test if a variable is set.
5300
     *             </p>
5301
     */
5302 10
    public function sizeRecursive(): int
5303
    {
5304 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5305
    }
5306
5307
    /**
5308
     * Extract a slice of the array.
5309
     *
5310
     * @param int      $offset       <p>Slice begin index.</p>
5311
     * @param int|null $length       <p>Length of the slice.</p>
5312
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5313
     *
5314
     * @return static
5315
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5316
     *
5317
     * @psalm-return static<TKey,T>
5318
     * @psalm-mutation-free
5319
     */
5320 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5321
    {
5322 5
        return static::create(
5323 5
            \array_slice(
5324 5
                $this->toArray(),
5325 5
                $offset,
5326 5
                $length,
5327 5
                $preserveKeys
5328
            ),
5329 5
            $this->iteratorClass,
5330 5
            false
5331
        );
5332
    }
5333
5334
    /**
5335
     * Sort the current array and optional you can keep the keys.
5336
     *
5337
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5338
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5339
     *                              <strong>SORT_NATURAL</strong></p>
5340
     * @param bool       $keepKeys
5341
     *
5342
     * @return static
5343
     *                <p>(Mutable) Return this Arrayy object.</p>
5344
     *
5345
     * @psalm-return static<TKey,T>
5346
     */
5347 20
    public function sort(
5348
        $direction = \SORT_ASC,
5349
        int $strategy = \SORT_REGULAR,
5350
        bool $keepKeys = false
5351
    ): self {
5352 20
        $this->generatorToArray();
5353
5354 20
        return $this->sorting(
5355 20
            $this->array,
5356 20
            $direction,
5357 20
            $strategy,
5358 20
            $keepKeys
5359
        );
5360
    }
5361
5362
    /**
5363
     * Sort the current array and optional you can keep the keys.
5364
     *
5365
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5366
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5367
     *                              <strong>SORT_NATURAL</strong></p>
5368
     * @param bool       $keepKeys
5369
     *
5370
     * @return static
5371
     *                <p>(Immutable) Return this Arrayy object.</p>
5372
     *
5373
     * @psalm-return static<TKey,T>
5374
     */
5375 12
    public function sortImmutable(
5376
        $direction = \SORT_ASC,
5377
        int $strategy = \SORT_REGULAR,
5378
        bool $keepKeys = false
5379
    ): self {
5380 12
        $that = clone $this;
5381
5382 12
        $that->generatorToArray();
5383
5384 12
        return $that->sorting(
5385 12
            $that->array,
5386 12
            $direction,
5387 12
            $strategy,
5388 12
            $keepKeys
5389
        );
5390
    }
5391
5392
    /**
5393
     * Sort the current array by key.
5394
     *
5395
     * @see          http://php.net/manual/en/function.ksort.php
5396
     * @see          http://php.net/manual/en/function.krsort.php
5397
     *
5398
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5399
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5400
     *                              <strong>SORT_NATURAL</strong></p>
5401
     *
5402
     * @return $this
5403
     *               <p>(Mutable) Return this Arrayy object.</p>
5404
     *
5405
     * @psalm-return static<TKey,T>
5406
     */
5407 18
    public function sortKeys(
5408
        $direction = \SORT_ASC,
5409
        int $strategy = \SORT_REGULAR
5410
    ): self {
5411 18
        $this->generatorToArray();
5412
5413 18
        $this->sorterKeys($this->array, $direction, $strategy);
5414
5415 18
        return $this;
5416
    }
5417
5418
    /**
5419
     * Sort the current array by key.
5420
     *
5421
     * @see          http://php.net/manual/en/function.ksort.php
5422
     * @see          http://php.net/manual/en/function.krsort.php
5423
     *
5424
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5425
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5426
     *                              <strong>SORT_NATURAL</strong></p>
5427
     *
5428
     * @return $this
5429
     *               <p>(Immutable) Return this Arrayy object.</p>
5430
     *
5431
     * @psalm-return static<TKey,T>
5432
     * @psalm-mutation-free
5433
     */
5434 8
    public function sortKeysImmutable(
5435
        $direction = \SORT_ASC,
5436
        int $strategy = \SORT_REGULAR
5437
    ): self {
5438 8
        $that = clone $this;
5439
5440
        /**
5441
         * @psalm-suppress ImpureMethodCall - object is already cloned
5442
         */
5443 8
        $that->sortKeys($direction, $strategy);
5444
5445 8
        return $that;
5446
    }
5447
5448
    /**
5449
     * Sort the current array by value.
5450
     *
5451
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5452
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5453
     *                              <strong>SORT_NATURAL</strong></p>
5454
     *
5455
     * @return static
5456
     *                <p>(Mutable)</p>
5457
     *
5458
     * @psalm-return static<TKey,T>
5459
     */
5460 1
    public function sortValueKeepIndex(
5461
        $direction = \SORT_ASC,
5462
        int $strategy = \SORT_REGULAR
5463
    ): self {
5464 1
        return $this->sort($direction, $strategy, true);
5465
    }
5466
5467
    /**
5468
     * Sort the current array by value.
5469
     *
5470
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5471
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5472
     *                              <strong>SORT_NATURAL</strong></p>
5473
     *
5474
     * @return static
5475
     *                <p>(Mutable)</p>
5476
     *
5477
     * @psalm-return static<TKey,T>
5478
     */
5479 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5480
    {
5481 1
        return $this->sort($direction, $strategy, false);
5482
    }
5483
5484
    /**
5485
     * Sort a array by value, by a closure or by a property.
5486
     *
5487
     * - If the sorter is null, the array is sorted naturally.
5488
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5489
     *
5490
     * @param callable|string|null $sorter
5491
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5492
     *                                        <strong>SORT_DESC</strong></p>
5493
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5494
     *                                        <strong>SORT_NATURAL</strong></p>
5495
     *
5496
     * @return static
5497
     *                <p>(Immutable)</p>
5498
     *
5499
     * @psalm-return static<TKey,T>
5500
     * @psalm-mutation-free
5501
     */
5502 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5503
    {
5504 1
        $array = $this->toArray();
5505 1
        $direction = $this->getDirection($direction);
5506
5507
        // Transform all values into their results.
5508 1
        if ($sorter) {
5509 1
            $arrayy = static::create(
5510 1
                $array,
5511 1
                $this->iteratorClass,
5512 1
                false
5513
            );
5514
5515
            /**
5516
             * @psalm-suppress MissingClosureReturnType
5517
             * @psalm-suppress MissingClosureParamType
5518
             */
5519 1
            $results = $arrayy->each(
5520
                function ($value) use ($sorter) {
5521 1
                    if (\is_callable($sorter) === true) {
5522 1
                        return $sorter($value);
5523
                    }
5524
5525 1
                    return $this->get($sorter);
5526 1
                }
5527
            );
5528
5529 1
            $results = $results->toArray();
5530
        } else {
5531 1
            $results = $array;
5532
        }
5533
5534
        // Sort by the results and replace by original values
5535 1
        \array_multisort($results, $direction, $strategy, $array);
5536
5537 1
        return static::create(
5538 1
            $array,
5539 1
            $this->iteratorClass,
5540 1
            false
5541
        );
5542
    }
5543
5544
    /**
5545
     * @param int      $offset
5546
     * @param int|null $length
5547
     * @param array    $replacement
5548
     *
5549
     * @return static
5550
     *                <p>(Immutable)</p>
5551
     *
5552
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5553
     * @psalm-return static<TKey,T>
5554
     * @psalm-mutation-free
5555
     */
5556 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5557
    {
5558 1
        $tmpArray = $this->toArray();
5559
5560 1
        \array_splice(
5561 1
            $tmpArray,
5562 1
            $offset,
5563 1
            $length ?? $this->count(),
5564 1
            $replacement
5565
        );
5566
5567 1
        return static::create(
5568 1
            $tmpArray,
5569 1
            $this->iteratorClass,
5570 1
            false
5571
        );
5572
    }
5573
5574
    /**
5575
     * Split an array in the given amount of pieces.
5576
     *
5577
     * @param int  $numberOfPieces
5578
     * @param bool $keepKeys
5579
     *
5580
     * @return static
5581
     *                <p>(Immutable)</p>
5582
     *
5583
     * @psalm-return static<TKey,T>
5584
     * @psalm-mutation-free
5585
     */
5586 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5587
    {
5588 1
        $this->generatorToArray();
5589
5590 1
        $count = $this->count();
5591
5592 1
        if ($count === 0) {
5593 1
            $result = [];
5594
        } else {
5595 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5596 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5597
        }
5598
5599 1
        return static::create(
5600 1
            $result,
5601 1
            $this->iteratorClass,
5602 1
            false
5603
        );
5604
    }
5605
5606
    /**
5607
     * Stripe all empty items.
5608
     *
5609
     * @return static
5610
     *                <p>(Immutable)</p>
5611
     *
5612
     * @psalm-return static<TKey,T>
5613
     * @psalm-mutation-free
5614
     */
5615 1
    public function stripEmpty(): self
5616
    {
5617 1
        return $this->filter(
5618
            static function ($item) {
5619 1
                if ($item === null) {
5620 1
                    return false;
5621
                }
5622
5623 1
                return (bool) \trim((string) $item);
5624 1
            }
5625
        );
5626
    }
5627
5628
    /**
5629
     * Swap two values between positions by key.
5630
     *
5631
     * @param int|string $swapA <p>a key in the array</p>
5632
     * @param int|string $swapB <p>a key in the array</p>
5633
     *
5634
     * @return static
5635
     *                <p>(Immutable)</p>
5636
     *
5637
     * @psalm-return static<TKey,T>
5638
     * @psalm-mutation-free
5639
     */
5640 1
    public function swap($swapA, $swapB): self
5641
    {
5642 1
        $array = $this->toArray();
5643
5644 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5645
5646 1
        return static::create(
5647 1
            $array,
5648 1
            $this->iteratorClass,
5649 1
            false
5650
        );
5651
    }
5652
5653
    /**
5654
     * Get the current array from the "Arrayy"-object.
5655
     * alias for "getArray()"
5656
     *
5657
     * @param bool $convertAllArrayyElements <p>
5658
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5659
     *                                       </p>
5660
     * @param bool $preserveKeys             <p>
5661
     *                                       e.g.: A generator maybe return the same key more then once,
5662
     *                                       so maybe you will ignore the keys.
5663
     *                                       </p>
5664
     *
5665
     * @return array
5666
     *
5667
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5668
     * @psalm-mutation-free
5669
     */
5670 943
    public function toArray(
5671
        bool $convertAllArrayyElements = false,
5672
        bool $preserveKeys = true
5673
    ): array {
5674
        // init
5675 943
        $array = [];
5676
5677 943
        if ($convertAllArrayyElements) {
5678 2
            foreach ($this->getGenerator() as $key => $value) {
5679 2
                if ($value instanceof self) {
5680 1
                    $value = $value->toArray(true);
5681
                }
5682
5683 2
                if ($preserveKeys) {
5684 1
                    $array[$key] = $value;
5685
                } else {
5686 2
                    $array[] = $value;
5687
                }
5688
            }
5689
        } else {
5690 943
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5691
        }
5692
5693 943
        return $array;
5694
    }
5695
5696
    /**
5697
     * Get the current array from the "Arrayy"-object as list.
5698
     *
5699
     * @param bool $convertAllArrayyElements <p>
5700
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5701
     *                                       </p>
5702
     *
5703
     * @return array
5704
     *
5705
     * @psalm-return list<array<TKey,T>>
5706
     * @psalm-mutation-free
5707
     */
5708 1
    public function toList(bool $convertAllArrayyElements = false): array
5709
    {
5710 1
        return $this->toArray(
5711 1
            $convertAllArrayyElements,
5712 1
            false
5713
        );
5714
    }
5715
5716
    /**
5717
     * Convert the current array to JSON.
5718
     *
5719
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
5720
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
5721
     *
5722
     * @return string
5723
     */
5724 12
    public function toJson(int $options = 0, int $depth = 512): string
5725
    {
5726 12
        $return = \json_encode($this->toArray(), $options, $depth);
5727 12
        if ($return === false) {
5728
            return '';
5729
        }
5730
5731 12
        return $return;
5732
    }
5733
5734
    /**
5735
     * @param string[]|null $items  [optional]
5736
     * @param string[]      $helper [optional]
5737
     *
5738
     * @return static|static[]
5739
     *
5740
     * @psalm-return static<int, static<TKey,T>>
5741
     */
5742 1
    public function toPermutation(array $items = null, array $helper = []): self
5743
    {
5744
        // init
5745 1
        $return = [];
5746
5747 1
        if ($items === null) {
5748 1
            $items = $this->toArray();
5749
        }
5750
5751 1
        if (empty($items)) {
5752 1
            $return[] = $helper;
5753
        } else {
5754 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5755 1
                $new_items = $items;
5756 1
                $new_helper = $helper;
5757 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5758
                /** @noinspection PhpSillyAssignmentInspection */
5759
                /** @var string[] $new_items */
5760 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...
5761 1
                \array_unshift($new_helper, $tmp_helper);
5762
                /** @noinspection SlowArrayOperationsInLoopInspection */
5763 1
                $return = \array_merge(
5764 1
                    $return,
5765 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5766
                );
5767
            }
5768
        }
5769
5770 1
        return static::create(
5771 1
            $return,
5772 1
            $this->iteratorClass,
5773 1
            false
5774
        );
5775
    }
5776
5777
    /**
5778
     * Implodes array to a string with specified separator.
5779
     *
5780
     * @param string $separator [optional] <p>The element's separator.</p>
5781
     *
5782
     * @return string
5783
     *                <p>The string representation of array, separated by ",".</p>
5784
     */
5785 19
    public function toString(string $separator = ','): string
5786
    {
5787 19
        return $this->implode($separator);
5788
    }
5789
5790
    /**
5791
     * Return a duplicate free copy of the current array.
5792
     *
5793
     * @return $this
5794
     *               <p>(Mutable)</p>
5795
     *
5796
     * @psalm-return static<TKey,T>
5797
     */
5798 13
    public function unique(): self
5799
    {
5800
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5801
5802
        /**
5803
         * @psalm-suppress MissingClosureReturnType
5804
         * @psalm-suppress MissingClosureParamType
5805
         */
5806 13
        $this->array = $this->reduce(
5807
            static function ($resultArray, $value) {
5808 12
                if (!\in_array($value, $resultArray, true)) {
5809 12
                    $resultArray[] = $value;
5810
                }
5811
5812 12
                return $resultArray;
5813 13
            },
5814 13
            []
5815 13
        )->toArray();
5816 13
        $this->generator = null;
5817
5818 13
        return $this;
5819
    }
5820
5821
    /**
5822
     * Return a duplicate free copy of the current array. (with the old keys)
5823
     *
5824
     * @return $this
5825
     *               <p>(Mutable)</p>
5826
     *
5827
     * @psalm-return static<TKey,T>
5828
     */
5829 11
    public function uniqueKeepIndex(): self
5830
    {
5831
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5832
5833
        // init
5834 11
        $array = $this->toArray();
5835
5836
        /**
5837
         * @psalm-suppress MissingClosureReturnType
5838
         * @psalm-suppress MissingClosureParamType
5839
         */
5840 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...
5841 11
            \array_keys($array),
5842
            static function ($resultArray, $key) use ($array) {
5843 10
                if (!\in_array($array[$key], $resultArray, true)) {
5844 10
                    $resultArray[$key] = $array[$key];
5845
                }
5846
5847 10
                return $resultArray;
5848 11
            },
5849 11
            []
5850
        );
5851 11
        $this->generator = null;
5852
5853 11
        return $this;
5854
    }
5855
5856
    /**
5857
     * alias: for "Arrayy->unique()"
5858
     *
5859
     * @return static
5860
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
5861
     *
5862
     * @see          Arrayy::unique()
5863
     *
5864
     * @psalm-return static<TKey,T>
5865
     */
5866 10
    public function uniqueNewIndex(): self
5867
    {
5868 10
        return $this->unique();
5869
    }
5870
5871
    /**
5872
     * Prepends one or more values to the beginning of array at once.
5873
     *
5874
     * @param array ...$args
5875
     *
5876
     * @return $this
5877
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
5878
     *
5879
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
5880
     * @psalm-return static<TKey,T>
5881
     */
5882 4
    public function unshift(...$args): self
5883
    {
5884 4
        $this->generatorToArray();
5885
5886 4
        \array_unshift($this->array, ...$args);
5887
5888 4
        return $this;
5889
    }
5890
5891
    /**
5892
     * Tests whether the given closure return something valid for all elements of this array.
5893
     *
5894
     * @param \Closure $closure the predicate
5895
     *
5896
     * @return bool
5897
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
5898
     */
5899 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...
5900
    {
5901 1
        foreach ($this->getGenerator() as $key => $value) {
5902 1
            if (!$closure($value, $key)) {
5903 1
                return false;
5904
            }
5905
        }
5906
5907 1
        return true;
5908
    }
5909
5910
    /**
5911
     * Get all values from a array.
5912
     *
5913
     * @return static
5914
     *                <p>(Immutable)</p>
5915
     *
5916
     * @psalm-return static<TKey,T>
5917
     * @psalm-mutation-free
5918
     */
5919 2
    public function values(): self
5920
    {
5921 2
        return static::create(
5922
            function () {
5923
                /** @noinspection YieldFromCanBeUsedInspection */
5924 2
                foreach ($this->getGenerator() as $value) {
5925 2
                    yield $value;
5926
                }
5927 2
            },
5928 2
            $this->iteratorClass,
5929 2
            false
5930
        );
5931
    }
5932
5933
    /**
5934
     * Apply the given function to every element in the array, discarding the results.
5935
     *
5936
     * @param callable $callable
5937
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
5938
     *
5939
     * @return $this
5940
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
5941
     *
5942
     * @psalm-return static<TKey,T>
5943
     */
5944 12
    public function walk($callable, bool $recursive = false): self
5945
    {
5946 12
        $this->generatorToArray();
5947
5948 12
        if ($this->array !== []) {
5949 10
            if ($recursive === true) {
5950 5
                \array_walk_recursive($this->array, $callable);
5951
            } else {
5952 5
                \array_walk($this->array, $callable);
5953
            }
5954
        }
5955
5956 12
        return $this;
5957
    }
5958
5959
    /**
5960
     * Returns a collection of matching items.
5961
     *
5962
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
5963
     * @param mixed  $value                 the value to match
5964
     *
5965
     * @throws \InvalidArgumentException if property or method is not defined
5966
     *
5967
     * @return static
5968
     *
5969
     * @psalm-param  T $value
5970
     * @psalm-return static<TKey,T>
5971
     */
5972 1
    public function where(string $keyOrPropertyOrMethod, $value): self
5973
    {
5974 1
        return $this->filter(
5975
            function ($item) use ($keyOrPropertyOrMethod, $value) {
5976 1
                $accessorValue = $this->extractValue(
5977 1
                    $item,
5978 1
                    $keyOrPropertyOrMethod
5979
                );
5980
5981 1
                return $accessorValue === $value;
5982 1
            }
5983
        );
5984
    }
5985
5986
    /**
5987
     * Convert an array into a object.
5988
     *
5989
     * @param array $array
5990
     *
5991
     * @return \stdClass
5992
     *
5993
     * @psalm-param array<mixed,mixed> $array
5994
     */
5995 4
    final protected static function arrayToObject(array $array = []): \stdClass
5996
    {
5997
        // init
5998 4
        $object = new \stdClass();
5999
6000 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6001 1
            return $object;
6002
        }
6003
6004 3
        foreach ($array as $name => $value) {
6005 3
            if (\is_array($value) === true) {
6006 1
                $object->{$name} = static::arrayToObject($value);
6007
            } else {
6008 3
                $object->{$name} = $value;
6009
            }
6010
        }
6011
6012 3
        return $object;
6013
    }
6014
6015
    /**
6016
     * @param array|\Generator|null $input         <p>
6017
     *                                             An array containing keys to return.
6018
     *                                             </p>
6019
     * @param mixed|null            $search_values [optional] <p>
6020
     *                                             If specified, then only keys containing these values are returned.
6021
     *                                             </p>
6022
     * @param bool                  $strict        [optional] <p>
6023
     *                                             Determines if strict comparison (===) should be used during the
6024
     *                                             search.
6025
     *                                             </p>
6026
     *
6027
     * @return array
6028
     *               <p>an array of all the keys in input</p>
6029
     *
6030
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6031
     * @psalm-return array<TKey|mixed>
6032
     * @psalm-mutation-free
6033
     */
6034 11
    protected function array_keys_recursive(
6035
        $input = null,
6036
        $search_values = null,
6037
        bool $strict = true
6038
    ): array {
6039
        // init
6040 11
        $keys = [];
6041 11
        $keysTmp = [];
6042
6043 11
        if ($input === null) {
6044 4
            $input = $this->getGenerator();
6045
        }
6046
6047 11
        if ($search_values === null) {
6048 11
            foreach ($input as $key => $value) {
6049 11
                $keys[] = $key;
6050
6051
                // check if recursive is needed
6052 11
                if (\is_array($value) === true) {
6053 11
                    $keysTmp[] = $this->array_keys_recursive($value);
6054
                }
6055
            }
6056
        } else {
6057 1
            $is_array_tmp = \is_array($search_values);
6058
6059 1
            foreach ($input as $key => $value) {
6060 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...
6061
                    (
6062 1
                        $is_array_tmp === false
6063
                        &&
6064 1
                        $strict === true
6065
                        &&
6066 1
                        $search_values === $value
6067
                    )
6068
                    ||
6069
                    (
6070 1
                        $is_array_tmp === false
6071
                        &&
6072 1
                        $strict === false
6073
                        &&
6074 1
                        $search_values == $value
6075
                    )
6076
                    ||
6077
                    (
6078 1
                        $is_array_tmp === true
6079
                        &&
6080 1
                        \in_array($value, $search_values, $strict)
6081
                    )
6082
                ) {
6083 1
                    $keys[] = $key;
6084
                }
6085
6086
                // check if recursive is needed
6087 1
                if (\is_array($value) === true) {
6088 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6089
                }
6090
            }
6091
        }
6092
6093 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6094
    }
6095
6096
    /**
6097
     * @param mixed      $path
6098
     * @param callable   $callable
6099
     * @param array|null $currentOffset
6100
     *
6101
     * @return void
6102
     *
6103
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6104
     * @psalm-mutation-free
6105
     */
6106 11
    protected function callAtPath($path, $callable, &$currentOffset = null)
6107
    {
6108 11
        $this->generatorToArray();
6109
6110 11
        if ($currentOffset === null) {
6111 11
            $currentOffset = &$this->array;
6112
        }
6113
6114 11
        $explodedPath = \explode($this->pathSeparator, $path);
6115 11
        if ($explodedPath === false) {
6116
            return;
6117
        }
6118
6119 11
        $nextPath = \array_shift($explodedPath);
6120
6121 11
        if (!isset($currentOffset[$nextPath])) {
6122 1
            return;
6123
        }
6124
6125 10
        if (!empty($explodedPath)) {
6126 1
            $this->callAtPath(
6127 1
                \implode($this->pathSeparator, $explodedPath),
6128 1
                $callable,
6129 1
                $currentOffset[$nextPath]
6130
            );
6131
        } else {
6132 10
            $callable($currentOffset[$nextPath]);
6133
        }
6134 10
    }
6135
6136
    /**
6137
     * Extracts the value of the given property or method from the object.
6138
     *
6139
     * @param static $object                <p>The object to extract the value from.</p>
6140
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
6141
     *                                      value should be extracted.</p>
6142
     *
6143
     * @throws \InvalidArgumentException if the method or property is not defined
6144
     *
6145
     * @return mixed
6146
     *               <p>The value extracted from the specified property or method.</p>
6147
     *
6148
     * @psalm-param self<TKey,T> $object
6149
     */
6150 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
6151
    {
6152 2
        if (isset($object[$keyOrPropertyOrMethod])) {
6153 2
            $return = $object->get($keyOrPropertyOrMethod);
6154
6155 2
            if ($return instanceof self) {
6156 1
                return $return->toArray();
6157
            }
6158
6159 1
            return $return;
6160
        }
6161
6162
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
6163
            return $object->{$keyOrPropertyOrMethod};
6164
        }
6165
6166
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
6167
            return $object->{$keyOrPropertyOrMethod}();
6168
        }
6169
6170
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6171
    }
6172
6173
    /**
6174
     * create a fallback for array
6175
     *
6176
     * 1. use the current array, if it's a array
6177
     * 2. fallback to empty array, if there is nothing
6178
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6179
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6180
     * 5. call "__toArray()" on object, if the method exists
6181
     * 6. cast a string or object with "__toString()" into an array
6182
     * 7. throw a "InvalidArgumentException"-Exception
6183
     *
6184
     * @param mixed $data
6185
     *
6186
     * @throws \InvalidArgumentException
6187
     *
6188
     * @return array
6189
     *
6190
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6191
     */
6192 1196
    protected function fallbackForArray(&$data): array
6193
    {
6194 1196
        $data = $this->internalGetArray($data);
6195
6196 1196
        if ($data === null) {
6197 2
            throw new \InvalidArgumentException('Passed value should be a array');
6198
        }
6199
6200 1194
        return $data;
6201
    }
6202
6203
    /**
6204
     * @param bool $preserveKeys <p>
6205
     *                           e.g.: A generator maybe return the same key more then once,
6206
     *                           so maybe you will ignore the keys.
6207
     *                           </p>
6208
     *
6209
     * @return bool
6210
     *
6211
     * @noinspection ReturnTypeCanBeDeclaredInspection
6212
     * @psalm-mutation-free :/
6213
     */
6214 1108
    protected function generatorToArray(bool $preserveKeys = true)
6215
    {
6216 1108
        if ($this->generator) {
6217 2
            $this->array = $this->toArray(false, $preserveKeys);
6218 2
            $this->generator = null;
6219
6220 2
            return true;
6221
        }
6222
6223 1108
        return false;
6224
    }
6225
6226
    /**
6227
     * Get correct PHP constant for direction.
6228
     *
6229
     * @param int|string $direction
6230
     *
6231
     * @return int
6232
     * @psalm-mutation-free
6233
     */
6234 43
    protected function getDirection($direction): int
6235
    {
6236 43
        if ((string) $direction === $direction) {
6237 10
            $direction = \strtolower($direction);
6238
6239 10
            if ($direction === 'desc') {
6240 2
                $direction = \SORT_DESC;
6241
            } else {
6242 8
                $direction = \SORT_ASC;
6243
            }
6244
        }
6245
6246
        if (
6247 43
            $direction !== \SORT_DESC
6248
            &&
6249 43
            $direction !== \SORT_ASC
6250
        ) {
6251
            $direction = \SORT_ASC;
6252
        }
6253
6254 43
        return $direction;
6255
    }
6256
6257
    /**
6258
     * @return TypeCheckInterface[]
6259
     *
6260
     * @noinspection ReturnTypeCanBeDeclaredInspection
6261
     */
6262 22
    protected function getPropertiesFromPhpDoc()
6263
    {
6264 22
        static $PROPERTY_CACHE = [];
6265 22
        $cacheKey = 'Class::' . static::class;
6266
6267 22
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6268 21
            return $PROPERTY_CACHE[$cacheKey];
6269
        }
6270
6271
        // init
6272 3
        $properties = [];
6273
6274 3
        $reflector = new \ReflectionClass($this);
6275 3
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6276 3
        $docComment = $reflector->getDocComment();
6277 3
        if ($docComment) {
6278 2
            $docblock = $factory->create($docComment);
6279
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6280 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6281 2
                $typeName = $tag->getVariableName();
6282 2
                if ($typeName !== null) {
6283 2
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
6284 2
                    if ($typeCheckPhpDoc !== null) {
6285 2
                        $properties[$typeName] = $typeCheckPhpDoc;
6286
                    }
6287
                }
6288
            }
6289
        }
6290
6291 3
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6292
    }
6293
6294
    /**
6295
     * @param mixed $glue
6296
     * @param mixed $pieces
6297
     * @param bool  $useKeys
6298
     *
6299
     * @return string
6300
     * @psalm-mutation-free
6301
     */
6302 36
    protected function implode_recursive(
6303
        $glue = '',
6304
        $pieces = [],
6305
        bool $useKeys = false
6306
    ): string {
6307 36
        if ($pieces instanceof self) {
6308 1
            $pieces = $pieces->toArray();
6309
        }
6310
6311 36
        if (\is_array($pieces) === true) {
6312 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6313 36
            $pieces_count_not_zero = $pieces_count > 0;
6314
6315 36
            return \implode(
6316 36
                $glue,
6317 36
                \array_map(
6318 36
                    [$this, 'implode_recursive'],
6319 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6320 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6321
                )
6322
            );
6323
        }
6324
6325
        if (
6326 36
            \is_scalar($pieces) === true
6327
            ||
6328 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6329
        ) {
6330 32
            return (string) $pieces;
6331
        }
6332
6333 8
        return '';
6334
    }
6335
6336
    /**
6337
     * @param mixed                 $needle   <p>
6338
     *                                        The searched value.
6339
     *                                        </p>
6340
     *                                        <p>
6341
     *                                        If needle is a string, the comparison is done
6342
     *                                        in a case-sensitive manner.
6343
     *                                        </p>
6344
     * @param array|\Generator|null $haystack <p>
6345
     *                                        The array.
6346
     *                                        </p>
6347
     * @param bool                  $strict   [optional] <p>
6348
     *                                        If the third parameter strict is set to true
6349
     *                                        then the in_array function will also check the
6350
     *                                        types of the
6351
     *                                        needle in the haystack.
6352
     *                                        </p>
6353
     *
6354
     * @return bool
6355
     *              <p>true if needle is found in the array, false otherwise</p>
6356
     *
6357
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6358
     * @psalm-mutation-free
6359
     */
6360 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6361
    {
6362 18
        if ($haystack === null) {
6363
            $haystack = $this->getGenerator();
6364
        }
6365
6366 18
        foreach ($haystack as $item) {
6367 14
            if (\is_array($item) === true) {
6368 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6369
            } else {
6370
                /** @noinspection NestedPositiveIfStatementsInspection */
6371 14
                if ($strict === true) {
6372 14
                    $returnTmp = $item === $needle;
6373
                } else {
6374
                    $returnTmp = $item == $needle;
6375
                }
6376
            }
6377
6378 14
            if ($returnTmp === true) {
6379 14
                return true;
6380
            }
6381
        }
6382
6383 8
        return false;
6384
    }
6385
6386
    /**
6387
     * @param mixed $data
6388
     *
6389
     * @return array|null
6390
     *
6391
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6392
     */
6393 1196
    protected function internalGetArray(&$data)
6394
    {
6395 1196
        if (\is_array($data) === true) {
6396 1190
            return $data;
6397
        }
6398
6399 56
        if (!$data) {
6400 6
            return [];
6401
        }
6402
6403 55
        if (\is_object($data) === true) {
6404 49
            if ($data instanceof \ArrayObject) {
6405 5
                return $data->getArrayCopy();
6406
            }
6407
6408 45
            if ($data instanceof \Generator) {
6409
                return static::createFromGeneratorImmutable($data)->toArray();
6410
            }
6411
6412 45
            if ($data instanceof \Traversable) {
6413
                return static::createFromObject($data)->toArray();
6414
            }
6415
6416 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...
6417
                return (array) $data->jsonSerialize();
6418
            }
6419
6420 45
            if (\method_exists($data, '__toArray')) {
6421
                return (array) $data->__toArray();
6422
            }
6423
6424 45
            if (\method_exists($data, '__toString')) {
6425
                return [(string) $data];
6426
            }
6427
        }
6428
6429 51
        if (\is_callable($data)) {
6430
            /**
6431
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6432
             */
6433 43
            $this->generator = new ArrayyRewindableGenerator($data);
6434
6435 43
            return [];
6436
        }
6437
6438 10
        if (\is_scalar($data)) {
6439 8
            return [$data];
6440
        }
6441
6442 2
        return null;
6443
    }
6444
6445
    /**
6446
     * Internal mechanics of remove method.
6447
     *
6448
     * @param mixed $key
6449
     *
6450
     * @return bool
6451
     */
6452 21
    protected function internalRemove($key): bool
6453
    {
6454 21
        $this->generatorToArray();
6455
6456
        if (
6457 21
            $this->pathSeparator
6458
            &&
6459 21
            (string) $key === $key
6460
            &&
6461 21
            \strpos($key, $this->pathSeparator) !== false
6462
        ) {
6463
            $path = \explode($this->pathSeparator, (string) $key);
6464
6465
            if ($path !== false) {
6466
                // crawl though the keys
6467
                while (\count($path, \COUNT_NORMAL) > 1) {
6468
                    $key = \array_shift($path);
6469
6470
                    if (!$this->has($key)) {
6471
                        return false;
6472
                    }
6473
6474
                    $this->array = &$this->array[$key];
6475
                }
6476
6477
                $key = \array_shift($path);
6478
            }
6479
        }
6480
6481 21
        unset($this->array[$key]);
6482
6483 21
        return true;
6484
    }
6485
6486
    /**
6487
     * Internal mechanic of set method.
6488
     *
6489
     * @param int|string|null $key
6490
     * @param mixed           $value
6491
     * @param bool            $checkProperties
6492
     *
6493
     * @return bool
6494
     */
6495 1047
    protected function internalSet(
6496
        $key,
6497
        &$value,
6498
        bool $checkProperties = true
6499
    ): bool {
6500
        if (
6501 1047
            $checkProperties === true
6502
            &&
6503 1047
            $this->properties !== []
6504
        ) {
6505 109
            $this->checkType($key, $value);
6506
        }
6507
6508 1045
        if ($key === null) {
6509
            return false;
6510
        }
6511
6512 1045
        $this->generatorToArray();
6513
6514
        /** @var array<int|string,mixed> $array */
6515 1045
        $array = &$this->array;
6516
6517
        /**
6518
         * https://github.com/vimeo/psalm/issues/2536
6519
         *
6520
         * @psalm-suppress PossiblyInvalidArgument
6521
         * @psalm-suppress InvalidScalarArgument
6522
         */
6523
        if (
6524 1045
            $this->pathSeparator
6525
            &&
6526 1045
            (string) $key === $key
6527
            &&
6528 1045
            \strpos($key, $this->pathSeparator) !== false
6529
        ) {
6530 9
            $path = \explode($this->pathSeparator, (string) $key);
6531
6532 9
            if ($path !== false) {
6533
                // crawl through the keys
6534 9
                while (\count($path, \COUNT_NORMAL) > 1) {
6535 9
                    $key = \array_shift($path);
6536
6537 9
                    $array = &$array[$key];
6538
                }
6539
6540 9
                $key = \array_shift($path);
6541
            }
6542
        }
6543
6544 1045
        if ($array === null) {
6545 4
            $array = [];
6546 1042
        } elseif (!\is_array($array)) {
6547 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
6548
        }
6549
6550 1045
        $array[$key] = $value;
6551
6552 1045
        return true;
6553
    }
6554
6555
    /**
6556
     * Convert a object into an array.
6557
     *
6558
     * @param mixed|object $object
6559
     *
6560
     * @return array|mixed
6561
     *
6562
     * @psalm-mutation-free
6563
     */
6564 5
    protected static function objectToArray($object)
6565
    {
6566 5
        if (!\is_object($object)) {
6567 4
            return $object;
6568
        }
6569
6570 5
        $object = \get_object_vars($object);
6571
6572
        /**
6573
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6574
         */
6575 5
        return \array_map(['static', 'objectToArray'], $object);
6576
    }
6577
6578
    /**
6579
     * @param array $data
6580
     * @param bool  $checkPropertiesInConstructor
6581
     *
6582
     * @return void
6583
     *
6584
     * @psalm-param array<mixed,T> $data
6585
     */
6586 1194
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6587
    {
6588 1194
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6589
                                        &&
6590 1194
                                        $checkPropertiesInConstructor === true;
6591
6592 1194
        if ($this->properties !== []) {
6593 98
            foreach ($data as $key => &$valueInner) {
6594 98
                $this->internalSet(
6595 98
                    $key,
6596 98
                    $valueInner,
6597 98
                    $checkPropertiesInConstructor
6598
                );
6599
            }
6600
        } else {
6601
            if (
6602 1115
                $this->checkPropertyTypes === true
6603
                ||
6604 1115
                $checkPropertiesInConstructor === true
6605
            ) {
6606 21
                $this->properties = $this->getPropertiesFromPhpDoc();
6607
            }
6608
6609
            /** @var TypeCheckInterface[] $properties */
6610 1115
            $properties = $this->properties;
6611
6612
            if (
6613 1115
                $this->checkPropertiesMismatchInConstructor === true
6614
                &&
6615 1115
                \count($data) !== 0
6616
                &&
6617 1115
                \count(\array_diff_key($properties, $data)) > 0
6618
            ) {
6619 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...
6620
            }
6621
6622 1114
            foreach ($data as $key => &$valueInner) {
6623 944
                $this->internalSet(
6624 944
                    $key,
6625 944
                    $valueInner,
6626 944
                    $checkPropertiesInConstructor
6627
                );
6628
            }
6629
        }
6630 1187
    }
6631
6632
    /**
6633
     * sorting keys
6634
     *
6635
     * @param array      $elements
6636
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6637
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6638
     *                              <strong>SORT_NATURAL</strong></p>
6639
     *
6640
     * @return $this
6641
     *               <p>(Mutable) Return this Arrayy object.</p>
6642
     *
6643
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6644
     * @psalm-return static<TKey,T>
6645
     */
6646 18
    protected function sorterKeys(
6647
        array &$elements,
6648
        $direction = \SORT_ASC,
6649
        int $strategy = \SORT_REGULAR
6650
    ): self {
6651 18
        $direction = $this->getDirection($direction);
6652
6653
        switch ($direction) {
6654 18
            case 'desc':
6655 18
            case \SORT_DESC:
6656 6
                \krsort($elements, $strategy);
6657
6658 6
                break;
6659 13
            case 'asc':
6660 13
            case \SORT_ASC:
6661
            default:
6662 13
                \ksort($elements, $strategy);
6663
        }
6664
6665 18
        return $this;
6666
    }
6667
6668
    /**
6669
     * @param array      $elements  <p>Warning: used as reference</p>
6670
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6671
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6672
     *                              <strong>SORT_NATURAL</strong></p>
6673
     * @param bool       $keepKeys
6674
     *
6675
     * @return $this
6676
     *               <p>(Mutable) Return this Arrayy object.</p>
6677
     *
6678
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6679
     * @psalm-return static<TKey,T>
6680
     */
6681 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6682
    {
6683 24
        $direction = $this->getDirection($direction);
6684
6685 24
        if (!$strategy) {
6686 24
            $strategy = \SORT_REGULAR;
6687
        }
6688
6689
        switch ($direction) {
6690 24
            case 'desc':
6691 24
            case \SORT_DESC:
6692 13
                if ($keepKeys) {
6693 9
                    \arsort($elements, $strategy);
6694
                } else {
6695 4
                    \rsort($elements, $strategy);
6696
                }
6697
6698 13
                break;
6699 11
            case 'asc':
6700 11
            case \SORT_ASC:
6701
            default:
6702 11
                if ($keepKeys) {
6703 4
                    \asort($elements, $strategy);
6704
                } else {
6705 7
                    \sort($elements, $strategy);
6706
                }
6707
        }
6708
6709 24
        return $this;
6710
    }
6711
6712
    /**
6713
     * @param array $array
6714
     *
6715
     * @return array
6716
     *
6717
     * @psalm-mutation-free
6718
     */
6719 25
    private function getArrayRecursiveHelperArrayy(array $array)
6720
    {
6721 25
        if ($array === []) {
6722
            return [];
6723
        }
6724
6725 25
        \array_walk_recursive(
6726 25
            $array,
6727
            /**
6728
             * @param array|self $item
6729
             *
6730
             * @return void
6731
             */
6732
            static function (&$item) {
6733 25
                if ($item instanceof self) {
6734 1
                    $item = $item->getArray();
6735
                }
6736 25
            }
6737
        );
6738
6739 25
        return $array;
6740
    }
6741
6742
    /**
6743
     * @param int|string|null $key
6744
     * @param mixed           $value
6745
     *
6746
     * @return void
6747
     */
6748 109
    private function checkType($key, $value)
6749
    {
6750
        if (
6751 109
            $key !== null
6752
            &&
6753 109
            isset($this->properties[$key]) === false
6754
            &&
6755 109
            $this->checkPropertiesMismatch === true
6756
        ) {
6757
            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...
6758
        }
6759
6760 109
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6761 96
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6762 22
        } elseif ($key !== null && isset($this->properties[$key])) {
6763 22
            $this->properties[$key]->checkType($value);
6764
        }
6765 107
    }
6766
}
6767