Completed
Push — master ( 1466a5...68742c )
by Lars
02:12
created

Arrayy::getArrayRecursiveHelperArrayy()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.0123

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 1
dl 0
loc 22
ccs 8
cts 9
cp 0.8889
crap 3.0123
rs 9.568
c 0
b 0
f 0
1
<?php
2
3
/** @noinspection ReturnTypeCanBeDeclaredInspection */
4
/** @noinspection ClassReImplementsParentInterfaceInspection */
5
6
declare(strict_types=1);
7
8
namespace Arrayy;
9
10
use Arrayy\Type\TypeInterface;
11
use Arrayy\TypeCheck\TypeCheckArray;
12
use Arrayy\TypeCheck\TypeCheckInterface;
13
use Arrayy\TypeCheck\TypeCheckPhpDoc;
14
15
/**
16
 * Methods to manage arrays.
17
 *
18
 * For the full copyright and license information, please view the LICENSE
19
 * file that was distributed with this source code.
20
 *
21
 * @template TKey of array-key
22
 * @template T
23
 * @template-extends \ArrayObject<TKey,T>
24
 * @template-implements \IteratorAggregate<TKey,T>
25
 * @template-implements \ArrayAccess<TKey|null,T>
26
 */
27
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
28
{
29
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
30
31
    /**
32
     * @var array
33
     *
34
     * @psalm-var array<mixed,mixed>|array<TKey,T>
35
     */
36
    protected $array = [];
37
38
    /**
39
     * @var \Arrayy\ArrayyRewindableGenerator|null
40
     *
41
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
42
     */
43
    protected $generator;
44
45
    /**
46
     * @var string
47
     *
48
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
49
     */
50
    protected $iteratorClass = ArrayyIterator::class;
51
52
    /**
53
     * @var string
54
     */
55
    protected $pathSeparator = '.';
56
57
    /**
58
     * @var bool
59
     */
60
    protected $checkPropertyTypes = false;
61
62
    /**
63
     * @var bool
64
     */
65
    protected $checkForMissingPropertiesInConstructor = false;
66
67
    /**
68
     * @var bool
69
     */
70
    protected $checkPropertiesMismatchInConstructor = false;
71
72
    /**
73
     * @var bool
74
     */
75
    protected $checkPropertiesMismatch = true;
76
77
    /**
78
     * @var array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
79
     */
80
    protected $properties = [];
81
82
    /**
83
     * Initializes
84
     *
85
     * @param mixed  $data                         <p>
86
     *                                             Should be an array or a generator, otherwise it will try
87
     *                                             to convert it into an array.
88
     *                                             </p>
89
     * @param string $iteratorClass                optional <p>
90
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
91
     *                                             need this option.
92
     *                                             </p>
93
     * @param bool   $checkPropertiesInConstructor optional <p>
94
     *                                             You need to extend the "Arrayy"-class and you need to set
95
     *                                             the $checkPropertiesMismatchInConstructor class property
96
     *                                             to
97
     *                                             true, otherwise this option didn't not work anyway.
98
     *                                             </p>
99
     *
100
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
101
     */
102 1196
    public function __construct(
103
        $data = [],
104
        string $iteratorClass = ArrayyIterator::class,
105
        bool $checkPropertiesInConstructor = true
106
    ) {
107 1196
        $data = $this->fallbackForArray($data);
108
109
        // used only for serialize + unserialize, all other methods are overwritten
110
        /**
111
         * @psalm-suppress InvalidArgument - why?
112
         */
113 1194
        parent::__construct([], 0, $iteratorClass);
114
115 1194
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
116
117 1187
        $this->setIteratorClass($iteratorClass);
118 1187
    }
119
120
    /**
121
     * @return void
122
     */
123 50
    public function __clone()
124
    {
125 50
        if (!\is_array($this->properties)) {
126
            $this->properties = clone $this->properties;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->properties of type object is incompatible with the declared type array of property $properties.

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

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

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