Completed
Push — master ( 76d9e4...3aa7e1 )
by Lars
01:41
created

Arrayy::remove()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.8846

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 23
ccs 7
cts 13
cp 0.5385
crap 3.8846
rs 9.552
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>|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 1173
    public function __construct(
103
        $data = [],
104
        string $iteratorClass = ArrayyIterator::class,
105
        bool $checkPropertiesInConstructor = true
106
    ) {
107 1173
        $data = $this->fallbackForArray($data);
108
109
        // used only for serialize + unserialize, all other methods are overwritten
110
        /**
111
         * @psalm-suppress InvalidArgument - why?
112
         */
113 1171
        parent::__construct([], 0, $iteratorClass);
114
115 1171
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
116
117 1164
        $this->setIteratorClass($iteratorClass);
118 1164
    }
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 2
    public function __set($key, $value)
174
    {
175 2
        $this->internalSet($key, $value);
176 2
    }
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 4
    public function &__get($key)
207
    {
208 4
        $return = $this->get($key);
209
210 4
        if (\is_array($return) === true) {
211
            return static::create($return, $this->iteratorClass, false);
212
        }
213
214 4
        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 6
    public function add($value, $key = null)
230
    {
231 6
        if ($key !== null) {
232 1
            $get = $this[$key];
233 1
            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 1
            $this->internalSet($key, $value);
241
242 1
            return $this;
243
        }
244
245 5
        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 17
    public function append($value, $key = null): self
260
    {
261 17
        $this->generatorToArray();
262
263 17
        if ($this->properties !== []) {
264 4
            $this->checkType($key, $value);
265
        }
266
267 16
        if ($key !== null) {
268
            if (
269 2
                isset($this->array[$key])
270
                &&
271 2
                \is_array($this->array[$key]) === true
272
            ) {
273
                $this->array[$key][] = $value;
274
            } else {
275 2
                $this->array[$key] = $value;
276
            }
277
        } else {
278 14
            $this->array[] = $value;
279
        }
280
281 16
        return $this;
282
    }
283
284
    /**
285
     * Sort the entries by value.
286
     *
287
     * @param int $sort_flags [optional] <p>
288
     *                        You may modify the behavior of the sort using the optional
289
     *                        parameter sort_flags, for details
290
     *                        see sort.
291
     *                        </p>
292
     *
293
     * @return $this
294
     *               <p>(Mutable) Return this Arrayy object.</p>
295
     *
296
     * @psalm-return static<TKey,T>
297
     */
298 4
    public function asort(int $sort_flags = 0): self
299
    {
300 4
        $this->generatorToArray();
301
302 4
        \asort($this->array, $sort_flags);
303
304 4
        return $this;
305
    }
306
307
    /**
308
     * Sort the entries by value.
309
     *
310
     * @param int $sort_flags [optional] <p>
311
     *                        You may modify the behavior of the sort using the optional
312
     *                        parameter sort_flags, for details
313
     *                        see sort.
314
     *                        </p>
315
     *
316
     * @return $this
317
     *               <p>(Immutable) Return this Arrayy object.</p>
318
     *
319
     * @psalm-return static<TKey,T>
320
     * @psalm-mutation-free
321
     */
322 4
    public function asortImmutable(int $sort_flags = 0): self
323
    {
324 4
        $that = clone $this;
325
326
        /**
327
         * @psalm-suppress ImpureMethodCall - object is already cloned
328
         */
329 4
        $that->asort($sort_flags);
330
331 4
        return $that;
332
    }
333
334
    /**
335
     * Counts all elements in an array, or something in an object.
336
     *
337
     * <p>
338
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
339
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
340
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
341
     * implemented and used in PHP.
342
     * </p>
343
     *
344
     * @see http://php.net/manual/en/function.count.php
345
     *
346
     * @param int $mode [optional] If the optional mode parameter is set to
347
     *                  COUNT_RECURSIVE (or 1), count
348
     *                  will recursively count the array. This is particularly useful for
349
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
350
     *
351
     * @return int
352
     *             <p>
353
     *             The number of elements in var, which is
354
     *             typically an array, since anything else will have one
355
     *             element.
356
     *             </p>
357
     *             <p>
358
     *             If var is not an array or an object with
359
     *             implemented Countable interface,
360
     *             1 will be returned.
361
     *             There is one exception, if var is &null;,
362
     *             0 will be returned.
363
     *             </p>
364
     *             <p>
365
     *             Caution: count may return 0 for a variable that isn't set,
366
     *             but it may also return 0 for a variable that has been initialized with an
367
     *             empty array. Use isset to test if a variable is set.
368
     *             </p>
369
     * @psalm-mutation-free
370
     */
371 147
    public function count(int $mode = \COUNT_NORMAL): int
372
    {
373
        if (
374 147
            $this->generator
375
            &&
376 147
            $mode === \COUNT_NORMAL
377
        ) {
378 4
            return \iterator_count($this->generator);
379
        }
380
381 143
        return \count($this->toArray(), $mode);
382
    }
383
384
    /**
385
     * Exchange the array for another one.
386
     *
387
     * @param array|static $data
388
     *
389
     * @return array
390
     *
391
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
392
     * @psalm-return array<mixed,mixed>|array<TKey,T>
393
     */
394 1
    public function exchangeArray($data): array
395
    {
396 1
        $this->array = $this->fallbackForArray($data);
397
398 1
        return $this->array;
399
    }
400
401
    /**
402
     * Creates a copy of the ArrayyObject.
403
     *
404
     * @return array
405
     *
406
     * @psalm-return array<mixed,mixed>|array<TKey,T>
407
     */
408 6
    public function getArrayCopy(): array
409
    {
410 6
        $this->generatorToArray();
411
412 6
        return $this->array;
413
    }
414
415
    /**
416
     * Returns a new iterator, thus implementing the \Iterator interface.
417
     *
418
     * @return \Iterator<mixed, mixed>
0 ignored issues
show
Documentation introduced by
The doc-type \Iterator<mixed, could not be parsed: Expected "|" or "end of type", but got "<" at position 9. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
419
     *                          <p>An iterator for the values in the array.</p>
420
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
421
     */
422 26
    public function getIterator(): \Iterator
423
    {
424 26
        if ($this->generator instanceof ArrayyRewindableGenerator) {
425 1
            return $this->generator;
426
        }
427
428 25
        $iterator = $this->getIteratorClass();
429
430 25
        if ($iterator === ArrayyIterator::class) {
431 25
            return new $iterator($this->toArray(), 0, static::class);
432
        }
433
434
        $return = new $iterator($this->toArray());
435
        \assert($return instanceof \Iterator);
436
437
        return $return;
438
    }
439
440
    /**
441
     * Gets the iterator classname for the ArrayObject.
442
     *
443
     * @return string
444
     *
445
     * @psalm-return class-string
446
     */
447 25
    public function getIteratorClass(): string
448
    {
449 25
        return $this->iteratorClass;
450
    }
451
452
    /**
453
     * Sort the entries by key.
454
     *
455
     * @param int $sort_flags [optional] <p>
456
     *                        You may modify the behavior of the sort using the optional
457
     *                        parameter sort_flags, for details
458
     *                        see sort.
459
     *                        </p>
460
     *
461
     * @return $this
462
     *               <p>(Mutable) Return this Arrayy object.</p>
463
     *
464
     * @psalm-return static<TKey,T>
465
     */
466 4
    public function ksort(int $sort_flags = 0): self
467
    {
468 4
        $this->generatorToArray();
469
470 4
        \ksort($this->array, $sort_flags);
471
472 4
        return $this;
473
    }
474
475
    /**
476
     * Sort the entries by key.
477
     *
478
     * @param int $sort_flags [optional] <p>
479
     *                        You may modify the behavior of the sort using the optional
480
     *                        parameter sort_flags, for details
481
     *                        see sort.
482
     *                        </p>
483
     *
484
     * @return $this
485
     *               <p>(Immutable) Return this Arrayy object.</p>
486
     *
487
     * @psalm-return static<TKey,T>
488
     */
489 4
    public function ksortImmutable(int $sort_flags = 0): self
490
    {
491 4
        $that = clone $this;
492
493
        /**
494
         * @psalm-suppress ImpureMethodCall - object is already cloned
495
         */
496 4
        $that->ksort($sort_flags);
497
498 4
        return $that;
499
    }
500
501
    /**
502
     * Sort an array using a case insensitive "natural order" algorithm.
503
     *
504
     * @return $this
505
     *               <p>(Mutable) Return this Arrayy object.</p>
506
     *
507
     * @psalm-return static<TKey,T>
508
     */
509 8
    public function natcasesort(): self
510
    {
511 8
        $this->generatorToArray();
512
513 8
        \natcasesort($this->array);
514
515 8
        return $this;
516
    }
517
518
    /**
519
     * Sort an array using a case insensitive "natural order" algorithm.
520
     *
521
     * @return $this
522
     *               <p>(Immutable) Return this Arrayy object.</p>
523
     *
524
     * @psalm-return static<TKey,T>
525
     * @psalm-mutation-free
526
     */
527 4
    public function natcasesortImmutable(): self
528
    {
529 4
        $that = clone $this;
530
531
        /**
532
         * @psalm-suppress ImpureMethodCall - object is already cloned
533
         */
534 4
        $that->natcasesort();
535
536 4
        return $that;
537
    }
538
539
    /**
540
     * Sort entries using a "natural order" algorithm.
541
     *
542
     * @return $this
543
     *               <p>(Mutable) Return this Arrayy object.</p>
544
     *
545
     * @psalm-return static<TKey,T>
546
     */
547 9
    public function natsort(): self
548
    {
549 9
        $this->generatorToArray();
550
551 9
        \natsort($this->array);
552
553 9
        return $this;
554
    }
555
556
    /**
557
     * Sort entries using a "natural order" algorithm.
558
     *
559
     * @return $this
560
     *               <p>(Immutable) Return this Arrayy object.</p>
561
     *
562
     * @psalm-return static<TKey,T>
563
     * @psalm-mutation-free
564
     */
565 4
    public function natsortImmutable(): self
566
    {
567 4
        $that = clone $this;
568
569
        /**
570
         * @psalm-suppress ImpureMethodCall - object is already cloned
571
         */
572 4
        $that->natsort();
573
574 4
        return $that;
575
    }
576
577
    /**
578
     * Whether or not an offset exists.
579
     *
580
     * @param bool|int|string $offset
581
     *
582
     * @return bool
583
     *
584
     * @noinspection PhpSillyAssignmentInspection
585
     *
586
     * @psalm-mutation-free
587
     */
588 137
    public function offsetExists($offset): bool
589
    {
590 137
        $this->generatorToArray();
591
592 137
        if ($this->array === []) {
593 7
            return false;
594
        }
595
596
        // php cast "bool"-index into "int"-index
597 131
        if ((bool) $offset === $offset) {
598 1
            $offset = (int) $offset;
599
        }
600
601
        /** @var int|string $offset - hint for phpstan */
602 131
        $offset = $offset;
0 ignored issues
show
Bug introduced by
Why assign $offset to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
603
604 131
        $tmpReturn = $this->keyExists($offset);
605
606
        if (
607 131
            $tmpReturn === true
608
            ||
609 131
            \strpos((string) $offset, $this->pathSeparator) === false
610
        ) {
611 127
            return $tmpReturn;
612
        }
613
614 5
        $offsetExists = false;
615
616
        /**
617
         * https://github.com/vimeo/psalm/issues/2536
618
         *
619
         * @psalm-suppress PossiblyInvalidArgument
620
         * @psalm-suppress InvalidScalarArgument
621
         */
622 View Code Duplication
        if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

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

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

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

Loading history...
958
    {
959
        // init
960 10
        $result = [];
961
962 10
        foreach ($this->getGenerator() as $key => $item) {
963 9
            if ($item instanceof self) {
964
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
965 9
            } elseif (\is_array($item) === true) {
966
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
967
                    ->appendToEachKey($prefix)
968
                    ->toArray();
969
            } else {
970 9
                $result[$prefix . $key] = $item;
971
            }
972
        }
973
974 10
        return self::create($result, $this->iteratorClass, false);
975
    }
976
977
    /**
978
     * Add a prefix to each value.
979
     *
980
     * @param mixed $prefix
981
     *
982
     * @return static
983
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
984
     *
985
     * @psalm-return static<TKey,T>
986
     * @psalm-mutation-free
987
     */
988 10 View Code Duplication
    public function appendToEachValue($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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