Completed
Push — master ( 7fb058...98e961 )
by Lars
02:00
created

Arrayy::get()   F

Complexity

Conditions 31
Paths 78

Size

Total Lines 139

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 64
CRAP Score 31.1953

Importance

Changes 0
Metric Value
cc 31
nc 78
nop 3
dl 0
loc 139
ccs 64
cts 68
cp 0.9412
crap 31.1953
rs 3.3333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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