Completed
Push — master ( 55c956...cdcbe6 )
by Lars
02:11 queued 15s
created

Arrayy::setAndGet()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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

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

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

Loading history...
129
        }
130
131 51
        if ($this->generator !== null) {
132
            $this->generator = clone $this->generator;
133
        }
134 51
    }
135
136
    /**
137
     * Call object as function.
138
     *
139
     * @param mixed $key
140
     *
141
     * @return mixed
142
     */
143 1
    public function __invoke($key = null)
144
    {
145 1
        if ($key !== null) {
146 1
            $this->generatorToArray();
147
148 1
            return $this->array[$key] ?? false;
149
        }
150
151
        return $this->toArray();
152
    }
153
154
    /**
155
     * Whether or not an element exists by key.
156
     *
157
     * @param mixed $key
158
     *
159
     * @return bool
160
     *              <p>True is the key/index exists, otherwise false.</p>
161
     */
162
    public function __isset($key): bool
163
    {
164
        return $this->offsetExists($key);
165
    }
166
167
    /**
168
     * Assigns a value to the specified element.
169
     *
170
     * @param mixed $key
171
     * @param mixed $value
172
     *
173
     * @return void
174
     */
175 3
    public function __set($key, $value)
176
    {
177 3
        $this->internalSet($key, $value);
178 3
    }
179
180
    /**
181
     * magic to string
182
     *
183
     * @return string
184
     */
185 15
    public function __toString(): string
186
    {
187 15
        return $this->toString();
188
    }
189
190
    /**
191
     * Unset element by key.
192
     *
193
     * @param mixed $key
194
     */
195
    public function __unset($key)
196
    {
197
        $this->internalRemove($key);
198
    }
199
200
    /**
201
     * Get a value by key.
202
     *
203
     * @param mixed $key
204
     *
205
     * @return mixed
206
     *               <p>Get a Value from the current array.</p>
207
     */
208 126
    public function &__get($key)
209
    {
210 126
        $return = $this->get($key, null, null, true);
211
212 126
        if (\is_array($return) === true) {
213
            $return = static::create($return, $this->iteratorClass, false);
214
        }
215
216 126
        return $return;
217
    }
218
219
    /**
220
     * Add new values (optional using dot-notation).
221
     *
222
     * @param mixed           $value
223
     * @param int|string|null $key
224
     *
225
     * @return static
226
     *                <p>(Immutable) Return this Arrayy object, with the appended values.</p>
227
     *
228
     * @psalm-param  T $value
229
     * @psalm-return static<TKey,T>
230
     *
231
     * @psalm-mutation-free
232
     */
233 13
    public function add($value, $key = null)
234
    {
235 13
        if ($key !== null) {
236 5
            $get = $this->get($key);
237 5
            if ($get !== null) {
238 1
                $value = \array_merge_recursive(
239 1
                    !$get instanceof self ? [$get] : $get->getArray(),
240 1
                    !\is_array($value) ? [$value] : $value
241
                );
242
            }
243
244 5
            $this->internalSet($key, $value);
245
246 4
            return $this;
247
        }
248
249 8
        return $this->append($value);
250
    }
251
252
    /**
253
     * Append a (key) + value to the current array.
254
     *
255
     * EXAMPLE: <code>
256
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
257
     * </code>
258
     *
259
     * @param mixed $value
260
     * @param mixed $key
261
     *
262
     * @return $this
263
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
264
     *
265
     * @psalm-return static<TKey,T>
266
     */
267 20
    public function append($value, $key = null): self
268
    {
269 20
        $this->generatorToArray();
270
271 20
        if ($this->properties !== []) {
272 6
            $this->checkType($key, $value);
273
        }
274
275 19
        if ($key !== null) {
276
            if (
277 2
                isset($this->array[$key])
278
                &&
279 2
                \is_array($this->array[$key]) === true
280
            ) {
281
                $this->array[$key][] = $value;
282
            } else {
283 2
                $this->array[$key] = $value;
284
            }
285
        } else {
286 17
            $this->array[] = $value;
287
        }
288
289 19
        return $this;
290
    }
291
292
    /**
293
     * Append a (key) + value to the current array.
294
     *
295
     * EXAMPLE: <code>
296
     * a(['fòô' => 'bàř'])->appendImmutable('foo')->getArray(); // ['fòô' => 'bàř', 0 => 'foo']
297
     * </code>
298
     *
299
     * @param mixed $value
300
     * @param mixed $key
301
     *
302
     * @return $this
303
     *               <p>(Immutable) Return this Arrayy object, with the appended values.</p>
304
     *
305
     * @psalm-return static<TKey,T>
306
     * @psalm-mutation-free
307
     */
308 1 View Code Duplication
    public function appendImmutable($value, $key = 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...
309
    {
310
        $generator = function () use ($key, $value): \Generator {
311 1
            if ($this->properties !== []) {
312
                $this->checkType($key, $value);
313
            }
314
315
            /** @noinspection YieldFromCanBeUsedInspection - FP */
316 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
317 1
                yield $keyOld => $itemOld;
318
            }
319
320 1
            if ($key !== null) {
321
                yield $key => $value;
322
            } else {
323 1
                yield $value;
324
            }
325 1
        };
326
327 1
        return static::create(
328 1
            $generator,
329 1
            $this->iteratorClass,
330 1
            false
331
        );
332
    }
333
334
    /**
335
     * Sort the entries by value.
336
     *
337
     * @param int $sort_flags [optional] <p>
338
     *                        You may modify the behavior of the sort using the optional
339
     *                        parameter sort_flags, for details
340
     *                        see sort.
341
     *                        </p>
342
     *
343
     * @return $this
344
     *               <p>(Mutable) Return this Arrayy object.</p>
345
     *
346
     * @psalm-return static<TKey,T>
347
     */
348 4
    public function asort(int $sort_flags = 0): self
349
    {
350 4
        $this->generatorToArray();
351
352 4
        \asort($this->array, $sort_flags);
353
354 4
        return $this;
355
    }
356
357
    /**
358
     * Sort the entries by value.
359
     *
360
     * @param int $sort_flags [optional] <p>
361
     *                        You may modify the behavior of the sort using the optional
362
     *                        parameter sort_flags, for details
363
     *                        see sort.
364
     *                        </p>
365
     *
366
     * @return $this
367
     *               <p>(Immutable) Return this Arrayy object.</p>
368
     *
369
     * @psalm-return static<TKey,T>
370
     * @psalm-mutation-free
371
     */
372 4
    public function asortImmutable(int $sort_flags = 0): self
373
    {
374 4
        $that = clone $this;
375
376
        /**
377
         * @psalm-suppress ImpureMethodCall - object is already cloned
378
         */
379 4
        $that->asort($sort_flags);
380
381 4
        return $that;
382
    }
383
384
    /**
385
     * Counts all elements in an array, or something in an object.
386
     *
387
     * <p>
388
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
389
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
390
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
391
     * implemented and used in PHP.
392
     * </p>
393
     *
394
     * @see http://php.net/manual/en/function.count.php
395
     *
396
     * @param int $mode [optional] If the optional mode parameter is set to
397
     *                  COUNT_RECURSIVE (or 1), count
398
     *                  will recursively count the array. This is particularly useful for
399
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
400
     *
401
     * @return int
402
     *             <p>
403
     *             The number of elements in var, which is
404
     *             typically an array, since anything else will have one
405
     *             element.
406
     *             </p>
407
     *             <p>
408
     *             If var is not an array or an object with
409
     *             implemented Countable interface,
410
     *             1 will be returned.
411
     *             There is one exception, if var is &null;,
412
     *             0 will be returned.
413
     *             </p>
414
     *             <p>
415
     *             Caution: count may return 0 for a variable that isn't set,
416
     *             but it may also return 0 for a variable that has been initialized with an
417
     *             empty array. Use isset to test if a variable is set.
418
     *             </p>
419
     * @psalm-mutation-free
420
     */
421 148
    public function count(int $mode = \COUNT_NORMAL): int
422
    {
423
        if (
424 148
            $this->generator
425
            &&
426 148
            $mode === \COUNT_NORMAL
427
        ) {
428 4
            return \iterator_count($this->generator);
429
        }
430
431 144
        return \count($this->toArray(), $mode);
432
    }
433
434
    /**
435
     * Exchange the array for another one.
436
     *
437
     * @param array|static $data
438
     *
439
     * @return array
440
     *
441
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
442
     * @psalm-return array<mixed,mixed>|array<TKey,T>
443
     */
444 1
    public function exchangeArray($data): array
445
    {
446 1
        $this->array = $this->fallbackForArray($data);
447
448 1
        return $this->array;
449
    }
450
451
    /**
452
     * Creates a copy of the ArrayyObject.
453
     *
454
     * @return array
455
     *
456
     * @psalm-return array<mixed,mixed>|array<TKey,T>
457
     */
458 6
    public function getArrayCopy(): array
459
    {
460 6
        $this->generatorToArray();
461
462 6
        return $this->array;
463
    }
464
465
    /**
466
     * Returns a new iterator, thus implementing the \Iterator interface.
467
     *
468
     * @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...
469
     *                          <p>An iterator for the values in the array.</p>
470
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
471
     */
472 27
    public function getIterator(): \Iterator
473
    {
474 27
        if ($this->generator instanceof ArrayyRewindableGenerator) {
475 1
            return $this->generator;
476
        }
477
478 26
        $iterator = $this->getIteratorClass();
479
480 26
        if ($iterator === ArrayyIterator::class) {
481 26
            return new $iterator($this->toArray(), 0, static::class);
482
        }
483
484
        $return = new $iterator($this->toArray());
485
        \assert($return instanceof \Iterator);
486
487
        return $return;
488
    }
489
490
    /**
491
     * Gets the iterator classname for the ArrayObject.
492
     *
493
     * @return string
494
     *
495
     * @psalm-return class-string
496
     */
497 26
    public function getIteratorClass(): string
498
    {
499 26
        return $this->iteratorClass;
500
    }
501
502
    /**
503
     * Sort the entries by key.
504
     *
505
     * @param int $sort_flags [optional] <p>
506
     *                        You may modify the behavior of the sort using the optional
507
     *                        parameter sort_flags, for details
508
     *                        see sort.
509
     *                        </p>
510
     *
511
     * @return $this
512
     *               <p>(Mutable) Return this Arrayy object.</p>
513
     *
514
     * @psalm-return static<TKey,T>
515
     */
516 4
    public function ksort(int $sort_flags = 0): self
517
    {
518 4
        $this->generatorToArray();
519
520 4
        \ksort($this->array, $sort_flags);
521
522 4
        return $this;
523
    }
524
525
    /**
526
     * Sort the entries by key.
527
     *
528
     * @param int $sort_flags [optional] <p>
529
     *                        You may modify the behavior of the sort using the optional
530
     *                        parameter sort_flags, for details
531
     *                        see sort.
532
     *                        </p>
533
     *
534
     * @return $this
535
     *               <p>(Immutable) Return this Arrayy object.</p>
536
     *
537
     * @psalm-return static<TKey,T>
538
     */
539 4
    public function ksortImmutable(int $sort_flags = 0): self
540
    {
541 4
        $that = clone $this;
542
543
        /**
544
         * @psalm-suppress ImpureMethodCall - object is already cloned
545
         */
546 4
        $that->ksort($sort_flags);
547
548 4
        return $that;
549
    }
550
551
    /**
552
     * Sort an array using a case insensitive "natural order" algorithm.
553
     *
554
     * @return $this
555
     *               <p>(Mutable) Return this Arrayy object.</p>
556
     *
557
     * @psalm-return static<TKey,T>
558
     */
559 8
    public function natcasesort(): self
560
    {
561 8
        $this->generatorToArray();
562
563 8
        \natcasesort($this->array);
564
565 8
        return $this;
566
    }
567
568
    /**
569
     * Sort an array using a case insensitive "natural order" algorithm.
570
     *
571
     * @return $this
572
     *               <p>(Immutable) Return this Arrayy object.</p>
573
     *
574
     * @psalm-return static<TKey,T>
575
     * @psalm-mutation-free
576
     */
577 4
    public function natcasesortImmutable(): self
578
    {
579 4
        $that = clone $this;
580
581
        /**
582
         * @psalm-suppress ImpureMethodCall - object is already cloned
583
         */
584 4
        $that->natcasesort();
585
586 4
        return $that;
587
    }
588
589
    /**
590
     * Sort entries using a "natural order" algorithm.
591
     *
592
     * @return $this
593
     *               <p>(Mutable) Return this Arrayy object.</p>
594
     *
595
     * @psalm-return static<TKey,T>
596
     */
597 10
    public function natsort(): self
598
    {
599 10
        $this->generatorToArray();
600
601 10
        \natsort($this->array);
602
603 10
        return $this;
604
    }
605
606
    /**
607
     * Sort entries using a "natural order" algorithm.
608
     *
609
     * @return $this
610
     *               <p>(Immutable) Return this Arrayy object.</p>
611
     *
612
     * @psalm-return static<TKey,T>
613
     * @psalm-mutation-free
614
     */
615 4
    public function natsortImmutable(): self
616
    {
617 4
        $that = clone $this;
618
619
        /**
620
         * @psalm-suppress ImpureMethodCall - object is already cloned
621
         */
622 4
        $that->natsort();
623
624 4
        return $that;
625
    }
626
627
    /**
628
     * Whether or not an offset exists.
629
     *
630
     * @param bool|int|string $offset
631
     *
632
     * @return bool
633
     *
634
     * @noinspection PhpSillyAssignmentInspection
635
     *
636
     * @psalm-mutation-free
637
     */
638 157
    public function offsetExists($offset): bool
639
    {
640 157
        $this->generatorToArray();
641
642 157
        if ($this->array === []) {
643 8
            return false;
644
        }
645
646
        // php cast "bool"-index into "int"-index
647 151
        if ((bool) $offset === $offset) {
648 1
            $offset = (int) $offset;
649
        }
650
651
        /** @var int|string $offset - hint for phpstan */
652 151
        $offset = $offset;
0 ignored issues
show
Bug introduced by
Why assign $offset to itself?

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

This assignement can be removed without consequences.

Loading history...
653
654 151
        $tmpReturn = $this->keyExists($offset);
655
656
        if (
657 151
            $tmpReturn === true
658
            ||
659 151
            \strpos((string) $offset, $this->pathSeparator) === false
660
        ) {
661 148
            return $tmpReturn;
662
        }
663
664 4
        $offsetExists = false;
665
666
        /**
667
         * https://github.com/vimeo/psalm/issues/2536
668
         *
669
         * @psalm-suppress PossiblyInvalidArgument
670
         * @psalm-suppress InvalidScalarArgument
671
         */
672 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...
673 4
            $this->pathSeparator
674
            &&
675 4
            (string) $offset === $offset
676
            &&
677 4
            \strpos($offset, $this->pathSeparator) !== false
678
        ) {
679 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
680 4
            if ($explodedPath !== false) {
681
                /** @var string $lastOffset - helper for phpstan */
682 4
                $lastOffset = \array_pop($explodedPath);
683 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
684
685
                /**
686
                 * @psalm-suppress MissingClosureReturnType
687
                 * @psalm-suppress MissingClosureParamType
688
                 */
689 4
                $this->callAtPath(
690 4
                    $containerPath,
691
                    static function ($container) use ($lastOffset, &$offsetExists) {
692 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
693 4
                    }
694
                );
695
            }
696
        }
697
698 4
        return $offsetExists;
699
    }
700
701
    /**
702
     * Returns the value at specified offset.
703
     *
704
     * @param int|string $offset
705
     *
706
     * @return mixed
707
     *               <p>Will return null if the offset did not exists.</p>
708
     */
709 126
    public function &offsetGet($offset)
710
    {
711
        // init
712 126
        $value = null;
713
714 126
        if ($this->offsetExists($offset)) {
715 124
            $value = &$this->__get($offset);
716
        }
717
718 126
        return $value;
719
    }
720
721
    /**
722
     * Assigns a value to the specified offset + check the type.
723
     *
724
     * @param int|string|null $offset
725
     * @param mixed           $value
726
     *
727
     * @return void
728
     */
729 27
    public function offsetSet($offset, $value)
730
    {
731 27
        $this->generatorToArray();
732
733 27
        if ($offset === null) {
734 6
            if ($this->properties !== []) {
735 1
                $this->checkType(null, $value);
736
            }
737
738 5
            $this->array[] = $value;
739
        } else {
740 21
            $this->internalSet(
741 21
                $offset,
742 21
                $value,
743 21
                true
744
            );
745
        }
746 26
    }
747
748
    /**
749
     * Unset an offset.
750
     *
751
     * @param int|string $offset
752
     *
753
     * @return void
754
     *              <p>(Mutable) Return nothing.</p>
755
     */
756 25
    public function offsetUnset($offset)
757
    {
758 25
        $this->generatorToArray();
759
760 25
        if ($this->array === []) {
761 6
            return;
762
        }
763
764 20
        if ($this->keyExists($offset)) {
765 13
            unset($this->array[$offset]);
766
767 13
            return;
768
        }
769
770
        /**
771
         * https://github.com/vimeo/psalm/issues/2536
772
         *
773
         * @psalm-suppress PossiblyInvalidArgument
774
         * @psalm-suppress InvalidScalarArgument
775
         */
776 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...
777 10
            $this->pathSeparator
778
            &&
779 10
            (string) $offset === $offset
780
            &&
781 10
            \strpos($offset, $this->pathSeparator) !== false
782
        ) {
783 7
            $path = \explode($this->pathSeparator, (string) $offset);
784
785 7
            if ($path !== false) {
786 7
                $pathToUnset = \array_pop($path);
787
788
                /**
789
                 * @psalm-suppress MissingClosureReturnType
790
                 * @psalm-suppress MissingClosureParamType
791
                 */
792 7
                $this->callAtPath(
793 7
                    \implode($this->pathSeparator, $path),
794
                    static function (&$offset) use ($pathToUnset) {
795 6
                        if (\is_array($offset)) {
796 5
                            unset($offset[$pathToUnset]);
797
                        } else {
798 1
                            $offset = null;
799
                        }
800 7
                    }
801
                );
802
            }
803
        }
804
805 10
        unset($this->array[$offset]);
806 10
    }
807
808
    /**
809
     * Serialize the current "Arrayy"-object.
810
     *
811
     * @return string
812
     */
813 2
    public function serialize(): string
814
    {
815 2
        $this->generatorToArray();
816
817 2
        if (\PHP_VERSION_ID < 70400) {
818 2
            return parent::serialize();
819
        }
820
821
        return \serialize($this);
822
    }
823
824
    /**
825
     * Sets the iterator classname for the current "Arrayy"-object.
826
     *
827
     * @param string $iteratorClass
828
     *
829
     * @throws \InvalidArgumentException
830
     *
831
     * @return void
832
     *
833
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
834
     */
835 1191
    public function setIteratorClass($iteratorClass)
836
    {
837 1191
        if (\class_exists($iteratorClass)) {
838 1191
            $this->iteratorClass = $iteratorClass;
839
840 1191
            return;
841
        }
842
843
        if (\strpos($iteratorClass, '\\') === 0) {
844
            $iteratorClass = '\\' . $iteratorClass;
845
            if (\class_exists($iteratorClass)) {
846
                /**
847
                 * @psalm-suppress PropertyTypeCoercion
848
                 */
849
                $this->iteratorClass = $iteratorClass;
850
851
                return;
852
            }
853
        }
854
855
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
856
    }
857
858
    /**
859
     * Sort the entries with a user-defined comparison function and maintain key association.
860
     *
861
     * @param callable $function
862
     *
863
     * @throws \InvalidArgumentException
864
     *
865
     * @return $this
866
     *               <p>(Mutable) Return this Arrayy object.</p>
867
     *
868
     * @psalm-return static<TKey,T>
869
     */
870 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...
871
    {
872 8
        if (!\is_callable($function)) {
873
            throw new \InvalidArgumentException('Passed function must be callable');
874
        }
875
876 8
        $this->generatorToArray();
877
878 8
        \uasort($this->array, $function);
879
880 8
        return $this;
881
    }
882
883
    /**
884
     * Sort the entries with a user-defined comparison function and maintain key association.
885
     *
886
     * @param callable $function
887
     *
888
     * @throws \InvalidArgumentException
889
     *
890
     * @return $this
891
     *               <p>(Immutable) Return this Arrayy object.</p>
892
     *
893
     * @psalm-return static<TKey,T>
894
     * @psalm-mutation-free
895
     */
896 4
    public function uasortImmutable($function): self
897
    {
898 4
        $that = clone $this;
899
900
        /**
901
         * @psalm-suppress ImpureMethodCall - object is already cloned
902
         */
903 4
        $that->uasort($function);
904
905 4
        return $that;
906
    }
907
908
    /**
909
     * Sort the entries by keys using a user-defined comparison function.
910
     *
911
     * @param callable $function
912
     *
913
     * @throws \InvalidArgumentException
914
     *
915
     * @return static
916
     *                <p>(Mutable) Return this Arrayy object.</p>
917
     *
918
     * @psalm-return static<TKey,T>
919
     */
920 5
    public function uksort($function): self
921
    {
922 5
        return $this->customSortKeys($function);
923
    }
924
925
    /**
926
     * Sort the entries by keys using a user-defined comparison function.
927
     *
928
     * @param callable $function
929
     *
930
     * @throws \InvalidArgumentException
931
     *
932
     * @return static
933
     *                <p>(Immutable) Return this Arrayy object.</p>
934
     *
935
     * @psalm-return static<TKey,T>
936
     * @psalm-mutation-free
937
     */
938 1
    public function uksortImmutable($function): self
939
    {
940 1
        return $this->customSortKeysImmutable($function);
941
    }
942
943
    /**
944
     * Unserialize an string and return the instance of the "Arrayy"-class.
945
     *
946
     * @param string $string
947
     *
948
     * @return $this
949
     *
950
     * @psalm-return static<TKey,T>
951
     */
952 2
    public function unserialize($string): self
953
    {
954 2
        if (\PHP_VERSION_ID < 70400) {
955 2
            parent::unserialize($string);
956
957 2
            return $this;
958
        }
959
960
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
961
    }
962
963
    /**
964
     * Append a (key) + values to the current array.
965
     *
966
     * EXAMPLE: <code>
967
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
968
     * </code>
969
     *
970
     * @param array $values
971
     * @param mixed $key
972
     *
973
     * @return $this
974
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
975
     *
976
     * @psalm-param  array<mixed,T> $values
977
     * @psalm-param  TKey|null $key
978
     * @psalm-return static<TKey,T>
979
     */
980 1
    public function appendArrayValues(array $values, $key = null)
981
    {
982 1
        $this->generatorToArray();
983
984 1
        if ($key !== null) {
985
            if (
986 1
                isset($this->array[$key])
987
                &&
988 1
                \is_array($this->array[$key]) === true
989
            ) {
990 1
                foreach ($values as $value) {
991 1
                    $this->array[$key][] = $value;
992
                }
993
            } else {
994
                foreach ($values as $value) {
995 1
                    $this->array[$key] = $value;
996
                }
997
            }
998
        } else {
999
            foreach ($values as $value) {
1000
                $this->array[] = $value;
1001
            }
1002
        }
1003
1004 1
        return $this;
1005
    }
1006
1007
    /**
1008
     * Add a suffix to each key.
1009
     *
1010
     * @param mixed $prefix
1011
     *
1012
     * @return static
1013
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
1014
     *
1015
     * @psalm-return static<TKey,T>
1016
     * @psalm-mutation-free
1017
     */
1018 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...
1019
    {
1020
        // init
1021 10
        $result = [];
1022
1023 10
        foreach ($this->getGenerator() as $key => $item) {
1024 9
            if ($item instanceof self) {
1025
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
1026 9
            } elseif (\is_array($item) === true) {
1027
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
1028
                    ->appendToEachKey($prefix)
1029
                    ->toArray();
1030
            } else {
1031 9
                $result[$prefix . $key] = $item;
1032
            }
1033
        }
1034
1035 10
        return self::create($result, $this->iteratorClass, false);
1036
    }
1037
1038
    /**
1039
     * Add a prefix to each value.
1040
     *
1041
     * @param mixed $prefix
1042
     *
1043
     * @return static
1044
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1045
     *
1046
     * @psalm-return static<TKey,T>
1047
     * @psalm-mutation-free
1048
     */
1049 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...
1050
    {
1051
        // init
1052 10
        $result = [];
1053
1054 10
        foreach ($this->getGenerator() as $key => $item) {
1055 9
            if ($item instanceof self) {
1056
                $result[$key] = $item->appendToEachValue($prefix);
1057 9
            } elseif (\is_array($item) === true) {
1058
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1059 9
            } elseif (\is_object($item) === true) {
1060 1
                $result[$key] = $item;
1061
            } else {
1062 9
                $result[$key] = $prefix . $item;
1063
            }
1064
        }
1065
1066 10
        return self::create($result, $this->iteratorClass, false);
1067
    }
1068
1069
    /**
1070
     * Sort an array in reverse order and maintain index association.
1071
     *
1072
     * @return $this
1073
     *               <p>(Mutable) Return this Arrayy object.</p>
1074
     *
1075
     * @psalm-return static<TKey,T>
1076
     */
1077 4
    public function arsort(): self
1078
    {
1079 4
        $this->generatorToArray();
1080
1081 4
        \arsort($this->array);
1082
1083 4
        return $this;
1084
    }
1085
1086
    /**
1087
     * Sort an array in reverse order and maintain index association.
1088
     *
1089
     * @return $this
1090
     *               <p>(Immutable) Return this Arrayy object.</p>
1091
     *
1092
     * @psalm-return static<TKey,T>
1093
     * @psalm-mutation-free
1094
     */
1095 10
    public function arsortImmutable(): self
1096
    {
1097 10
        $that = clone $this;
1098
1099 10
        $that->generatorToArray();
1100
1101 10
        \arsort($that->array);
1102
1103 10
        return $that;
1104
    }
1105
1106
    /**
1107
     * Iterate over the current array and execute a callback for each loop.
1108
     *
1109
     * EXAMPLE: <code>
1110
     * $result = A::create();
1111
     * $closure = function ($value, $key) use ($result) {
1112
     *     $result[$key] = ':' . $value . ':';
1113
     * };
1114
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1115
     * </code>
1116
     *
1117
     * @param \Closure $closure
1118
     *
1119
     * @return static
1120
     *                <p>(Immutable)</p>
1121
     *
1122
     * @psalm-return static<TKey,T>
1123
     * @psalm-mutation-free
1124
     */
1125 3
    public function at(\Closure $closure): self
1126
    {
1127 3
        $that = clone $this;
1128
1129 3
        foreach ($that->getGenerator() as $key => $value) {
1130 3
            $closure($value, $key);
1131
        }
1132
1133 3
        return static::create(
1134 3
            $that->toArray(),
1135 3
            $this->iteratorClass,
1136 3
            false
1137
        );
1138
    }
1139
1140
    /**
1141
     * Returns the average value of the current array.
1142
     *
1143
     * EXAMPLE: <code>
1144
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1145
     * </code>
1146
     *
1147
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1148
     *
1149
     * @return float|int
1150
     *                   <p>The average value.</p>
1151
     * @psalm-mutation-free
1152
     */
1153 10
    public function average($decimals = 0)
1154
    {
1155 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1156
1157 10
        if (!$count) {
1158 2
            return 0;
1159
        }
1160
1161 8
        if ((int) $decimals !== $decimals) {
1162 3
            $decimals = 0;
1163
        }
1164
1165 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1166
    }
1167
1168
    /**
1169
     * Changes all keys in an array.
1170
     *
1171
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1172
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1173
     *
1174
     * @return static
1175
     *                <p>(Immutable)</p>
1176
     *
1177
     * @psalm-return static<TKey,T>
1178
     * @psalm-mutation-free
1179
     */
1180 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1181
    {
1182
        if (
1183 1
            $case !== \CASE_LOWER
1184
            &&
1185 1
            $case !== \CASE_UPPER
1186
        ) {
1187
            $case = \CASE_LOWER;
1188
        }
1189
1190 1
        $return = [];
1191 1
        foreach ($this->getGenerator() as $key => $value) {
1192 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1193
1194 1
            if ($case === \CASE_LOWER) {
1195 1
                $key = \mb_strtolower((string) $key);
1196
            } else {
1197 1
                $key = \mb_strtoupper((string) $key);
1198
            }
1199
1200 1
            $return[$key] = $value;
1201
        }
1202
1203 1
        return static::create(
1204 1
            $return,
1205 1
            $this->iteratorClass,
1206 1
            false
1207
        );
1208
    }
1209
1210
    /**
1211
     * Change the path separator of the array wrapper.
1212
     *
1213
     * By default, the separator is: "."
1214
     *
1215
     * @param string $separator <p>Separator to set.</p>
1216
     *
1217
     * @return $this
1218
     *               <p>(Mutable) Return this Arrayy object.</p>
1219
     *
1220
     * @psalm-return static<TKey,T>
1221
     */
1222 11
    public function changeSeparator($separator): self
1223
    {
1224 11
        $this->pathSeparator = $separator;
1225
1226 11
        return $this;
1227
    }
1228
1229
    /**
1230
     * Create a chunked version of the current array.
1231
     *
1232
     * EXAMPLE: <code>
1233
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1234
     * </code>
1235
     *
1236
     * @param int  $size         <p>Size of each chunk.</p>
1237
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1238
     *
1239
     * @return static
1240
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1241
     *
1242
     * @psalm-return static<TKey,T>
1243
     * @psalm-mutation-free
1244
     */
1245 5
    public function chunk($size, $preserveKeys = false): self
1246
    {
1247 5
        return static::create(
1248 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1249 5
            $this->iteratorClass,
1250 5
            false
1251
        );
1252
    }
1253
1254
    /**
1255
     * Clean all falsy values from the current array.
1256
     *
1257
     * EXAMPLE: <code>
1258
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1259
     * </code>
1260
     *
1261
     * @return static
1262
     *                <p>(Immutable)</p>
1263
     *
1264
     * @psalm-return static<TKey,T>
1265
     * @psalm-mutation-free
1266
     */
1267 8
    public function clean(): self
1268
    {
1269 8
        return $this->filter(
1270
            static function ($value) {
1271 7
                return (bool) $value;
1272 8
            }
1273
        );
1274
    }
1275
1276
    /**
1277
     * WARNING!!! -> Clear the current full array or a $key of it.
1278
     *
1279
     * EXAMPLE: <code>
1280
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1281
     * </code>
1282
     *
1283
     * @param int|int[]|string|string[]|null $key
1284
     *
1285
     * @return $this
1286
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1287
     *
1288
     * @psalm-return static<TKey,T>
1289
     */
1290 10
    public function clear($key = null): self
1291
    {
1292 10
        if ($key !== null) {
1293 3
            if (\is_array($key)) {
1294 1
                foreach ($key as $keyTmp) {
1295 1
                    $this->offsetUnset($keyTmp);
1296
                }
1297
            } else {
1298 2
                $this->offsetUnset($key);
1299
            }
1300
1301 3
            return $this;
1302
        }
1303
1304 7
        $this->array = [];
1305 7
        $this->generator = null;
1306
1307 7
        return $this;
1308
    }
1309
1310
    /**
1311
     * Check if an item is in the current array.
1312
     *
1313
     * EXAMPLE: <code>
1314
     * a([1, true])->contains(true); // true
1315
     * </code>
1316
     *
1317
     * @param float|int|string $value
1318
     * @param bool             $recursive
1319
     * @param bool             $strict
1320
     *
1321
     * @return bool
1322
     * @psalm-mutation-free
1323
     */
1324 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1325
    {
1326 23
        if ($recursive === true) {
1327 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1328
        }
1329
1330
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1331 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1332 11
            if ($strict) {
1333 11
                if ($value === $valueFromArray) {
1334 11
                    return true;
1335
                }
1336
            } else {
1337
                /** @noinspection NestedPositiveIfStatementsInspection */
1338
                if ($value == $valueFromArray) {
1339 7
                    return true;
1340
                }
1341
            }
1342
        }
1343
1344 7
        return false;
1345
    }
1346
1347
    /**
1348
     * Check if an (case-insensitive) string is in the current array.
1349
     *
1350
     * EXAMPLE: <code>
1351
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1352
     * </code>
1353
     *
1354
     * @param mixed $value
1355
     * @param bool  $recursive
1356
     *
1357
     * @return bool
1358
     * @psalm-mutation-free
1359
     *
1360
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1361
     */
1362 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1363
    {
1364 26
        if ($value === null) {
1365 2
            return false;
1366
        }
1367
1368 24
        if ($recursive === true) {
1369
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1370 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1371 22
                if (\is_array($valueTmp) === true) {
1372 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1373 5
                    if ($return === true) {
1374 5
                        return $return;
1375
                    }
1376 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1377 22
                    return true;
1378
                }
1379
            }
1380
1381 8
            return false;
1382
        }
1383
1384
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1385 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1386 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1387 11
                return true;
1388
            }
1389
        }
1390
1391 4
        return false;
1392
    }
1393
1394
    /**
1395
     * Check if the given key/index exists in the array.
1396
     *
1397
     * EXAMPLE: <code>
1398
     * a([1 => true])->containsKey(1); // true
1399
     * </code>
1400
     *
1401
     * @param int|string $key <p>key/index to search for</p>
1402
     *
1403
     * @return bool
1404
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1405
     *
1406
     * @psalm-mutation-free
1407
     */
1408 4
    public function containsKey($key): bool
1409
    {
1410 4
        return $this->offsetExists($key);
1411
    }
1412
1413
    /**
1414
     * Check if all given needles are present in the array as key/index.
1415
     *
1416
     * EXAMPLE: <code>
1417
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1418
     * </code>
1419
     *
1420
     * @param array $needles   <p>The keys you are searching for.</p>
1421
     * @param bool  $recursive
1422
     *
1423
     * @return bool
1424
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1425
     *
1426
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1427
     * @psalm-mutation-free
1428
     */
1429 2
    public function containsKeys(array $needles, $recursive = false): bool
1430
    {
1431 2
        if ($recursive === true) {
1432
            return
1433 2
                \count(
1434 2
                    \array_intersect(
1435 2
                        $needles,
1436 2
                        $this->keys(true)->toArray()
1437
                    ),
1438 2
                    \COUNT_RECURSIVE
1439
                )
1440
                ===
1441 2
                \count(
1442 2
                    $needles,
1443 2
                    \COUNT_RECURSIVE
1444
                );
1445
        }
1446
1447 1
        return \count(
1448 1
            \array_intersect($needles, $this->keys()->toArray()),
1449 1
            \COUNT_NORMAL
1450
        )
1451
                ===
1452 1
                \count(
1453 1
                    $needles,
1454 1
                    \COUNT_NORMAL
1455
                );
1456
    }
1457
1458
    /**
1459
     * Check if all given needles are present in the array as key/index.
1460
     *
1461
     * @param array $needles <p>The keys you are searching for.</p>
1462
     *
1463
     * @return bool
1464
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1465
     *
1466
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1467
     * @psalm-mutation-free
1468
     */
1469 1
    public function containsKeysRecursive(array $needles): bool
1470
    {
1471 1
        return $this->containsKeys($needles, true);
1472
    }
1473
1474
    /**
1475
     * alias: for "Arrayy->contains()"
1476
     *
1477
     * @param float|int|string $value
1478
     *
1479
     * @return bool
1480
     *
1481
     * @see Arrayy::contains()
1482
     * @psalm-mutation-free
1483
     */
1484 9
    public function containsValue($value): bool
1485
    {
1486 9
        return $this->contains($value);
1487
    }
1488
1489
    /**
1490
     * alias: for "Arrayy->contains($value, true)"
1491
     *
1492
     * @param float|int|string $value
1493
     *
1494
     * @return bool
1495
     *
1496
     * @see Arrayy::contains()
1497
     * @psalm-mutation-free
1498
     */
1499 18
    public function containsValueRecursive($value): bool
1500
    {
1501 18
        return $this->contains($value, true);
1502
    }
1503
1504
    /**
1505
     * Check if all given needles are present in the array.
1506
     *
1507
     * EXAMPLE: <code>
1508
     * a([1, true])->containsValues(array(1, true)); // true
1509
     * </code>
1510
     *
1511
     * @param array $needles
1512
     *
1513
     * @return bool
1514
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1515
     *
1516
     * @psalm-param array<mixed>|array<T> $needles
1517
     * @psalm-mutation-free
1518
     */
1519 1
    public function containsValues(array $needles): bool
1520
    {
1521 1
        return \count(
1522 1
            \array_intersect(
1523 1
                $needles,
1524 1
                $this->toArray()
1525
            ),
1526 1
            \COUNT_NORMAL
1527
        )
1528
               ===
1529 1
               \count(
1530 1
                   $needles,
1531 1
                   \COUNT_NORMAL
1532
               );
1533
    }
1534
1535
    /**
1536
     * Counts all the values of an array
1537
     *
1538
     * @see          http://php.net/manual/en/function.array-count-values.php
1539
     *
1540
     * @return static
1541
     *                <p>
1542
     *                (Immutable)
1543
     *                An associative Arrayy-object of values from input as
1544
     *                keys and their count as value.
1545
     *                </p>
1546
     *
1547
     * @psalm-return static<TKey,T>
1548
     * @psalm-mutation-free
1549
     */
1550 7
    public function countValues(): self
1551
    {
1552 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1553
    }
1554
1555
    /**
1556
     * Creates an Arrayy object.
1557
     *
1558
     * @param mixed  $data
1559
     * @param string $iteratorClass
1560
     * @param bool   $checkPropertiesInConstructor
1561
     *
1562
     * @return static
1563
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1564
     *
1565
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1566
     *
1567
     * @psalm-mutation-free
1568
     */
1569 720
    public static function create(
1570
        $data = [],
1571
        string $iteratorClass = ArrayyIterator::class,
1572
        bool $checkPropertiesInConstructor = true
1573
    ) {
1574 720
        return new static(
1575 720
            $data,
1576 720
            $iteratorClass,
1577 720
            $checkPropertiesInConstructor
1578
        );
1579
    }
1580
1581
    /**
1582
     * Flatten an array with the given character as a key delimiter
1583
     *
1584
     * @param string     $delimiter
1585
     * @param string     $prepend
1586
     * @param array|null $items
1587
     *
1588
     * @return array
1589
     */
1590 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1591
    {
1592
        // init
1593 2
        $flatten = [];
1594
1595 2
        if ($items === null) {
1596 2
            $items = $this->array;
1597
        }
1598
1599 2
        foreach ($items as $key => $value) {
1600 2
            if (\is_array($value) && !empty($value)) {
1601 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1602
            } else {
1603 2
                $flatten[] = [$prepend . $key => $value];
1604
            }
1605
        }
1606
1607 2
        if (\count($flatten) === 0) {
1608
            return [];
1609
        }
1610
1611 2
        return \array_merge_recursive([], ...$flatten);
1612
    }
1613
1614
    /**
1615
     * WARNING: Creates an Arrayy object by reference.
1616
     *
1617
     * @param array $array
1618
     *
1619
     * @return $this
1620
     *               <p>(Mutable) Return this Arrayy object.</p>
1621
     *
1622
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1623
     */
1624 27
    public function createByReference(array &$array = []): self
1625
    {
1626 27
        $array = $this->fallbackForArray($array);
1627
1628 27
        $this->array = &$array;
1629
1630 27
        return $this;
1631
    }
1632
1633
    /**
1634
     * Create an new instance from a callable function which will return an Generator.
1635
     *
1636
     * @param callable $generatorFunction
1637
     *
1638
     * @return static
1639
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1640
     *
1641
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1642
     *
1643
     * @psalm-mutation-free
1644
     */
1645 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1646
    {
1647 7
        return self::create($generatorFunction);
1648
    }
1649
1650
    /**
1651
     * Create an new instance filled with a copy of values from a "Generator"-object.
1652
     *
1653
     * @param \Generator $generator
1654
     *
1655
     * @return static
1656
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1657
     *
1658
     * @psalm-param \Generator<array-key,mixed> $generator
1659
     *
1660
     * @psalm-mutation-free
1661
     */
1662 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1663
    {
1664 4
        return self::create(\iterator_to_array($generator, true));
1665
    }
1666
1667
    /**
1668
     * Create an new Arrayy object via JSON.
1669
     *
1670
     * @param string $json
1671
     *
1672
     * @return static
1673
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1674
     *
1675
     * @psalm-mutation-free
1676
     */
1677 5
    public static function createFromJson(string $json): self
1678
    {
1679 5
        return static::create(\json_decode($json, true));
1680
    }
1681
1682
    /**
1683
     * Create an new Arrayy object via JSON.
1684
     *
1685
     * @param array $array
1686
     *
1687
     * @return static
1688
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1689
     *
1690
     * @psalm-mutation-free
1691
     */
1692 1
    public static function createFromArray(array $array): self
1693
    {
1694 1
        return static::create($array);
1695
    }
1696
1697
    /**
1698
     * Create an new instance filled with values from an object that is iterable.
1699
     *
1700
     * @param \Traversable $object <p>iterable object</p>
1701
     *
1702
     * @return static
1703
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1704
     *
1705
     * @psalm-param \Traversable<array-key,mixed> $object
1706
     *
1707
     * @psalm-mutation-free
1708
     */
1709 4
    public static function createFromObject(\Traversable $object): self
1710
    {
1711
        // init
1712 4
        $arrayy = new static();
1713
1714 4
        if ($object instanceof self) {
1715 4
            $objectArray = $object->getGenerator();
1716
        } else {
1717
            $objectArray = $object;
1718
        }
1719
1720 4
        foreach ($objectArray as $key => $value) {
1721
            /**
1722
             * @psalm-suppress ImpureMethodCall - object is already re-created
1723
             */
1724 3
            $arrayy->internalSet($key, $value);
1725
        }
1726
1727 4
        return $arrayy;
1728
    }
1729
1730
    /**
1731
     * Create an new instance filled with values from an object.
1732
     *
1733
     * @param object $object
1734
     *
1735
     * @return static
1736
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1737
     *
1738
     * @psalm-mutation-free
1739
     */
1740 5
    public static function createFromObjectVars($object): self
1741
    {
1742 5
        return self::create(self::objectToArray($object));
1743
    }
1744
1745
    /**
1746
     * Create an new Arrayy object via string.
1747
     *
1748
     * @param string      $str       <p>The input string.</p>
1749
     * @param string|null $delimiter <p>The boundary string.</p>
1750
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1751
     *                               used.</p>
1752
     *
1753
     * @return static
1754
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1755
     *
1756
     * @psalm-mutation-free
1757
     */
1758 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1759
    {
1760 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...
1761 1
            \preg_match_all($regEx, $str, $array);
1762
1763 1
            if (!empty($array)) {
1764 1
                $array = $array[0];
1765
            }
1766
        } else {
1767
            /** @noinspection NestedPositiveIfStatementsInspection */
1768 9
            if ($delimiter !== null) {
1769 7
                $array = \explode($delimiter, $str);
1770
            } else {
1771 2
                $array = [$str];
1772
            }
1773
        }
1774
1775
        // trim all string in the array
1776
        /**
1777
         * @psalm-suppress MissingClosureParamType
1778
         */
1779 10
        \array_walk(
1780 10
            $array,
1781
            static function (&$val) {
1782 10
                if ((string) $val === $val) {
1783 10
                    $val = \trim($val);
1784
                }
1785 10
            }
1786
        );
1787
1788 10
        return static::create($array);
1789
    }
1790
1791
    /**
1792
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1793
     *
1794
     * @param \Traversable $traversable
1795
     *
1796
     * @return static
1797
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1798
     *
1799
     * @psalm-param \Traversable<array-key,mixed> $traversable
1800
     *
1801
     * @psalm-mutation-free
1802
     */
1803 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1804
    {
1805 1
        return self::create(\iterator_to_array($traversable, true));
1806
    }
1807
1808
    /**
1809
     * Create an new instance containing a range of elements.
1810
     *
1811
     * @param float|int|string $low  <p>First value of the sequence.</p>
1812
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1813
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1814
     *
1815
     * @return static
1816
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1817
     *
1818
     * @psalm-mutation-free
1819
     */
1820 2
    public static function createWithRange($low, $high, $step = 1): self
1821
    {
1822 2
        return static::create(\range($low, $high, $step));
1823
    }
1824
1825
    /**
1826
     * Gets the element of the array at the current internal iterator position.
1827
     *
1828
     * @return false|mixed
1829
     */
1830
    public function current()
1831
    {
1832
        return \current($this->array);
1833
    }
1834
1835
    /**
1836
     * Custom sort by index via "uksort".
1837
     *
1838
     * EXAMPLE: <code>
1839
     * $callable = function ($a, $b) {
1840
     *     if ($a == $b) {
1841
     *         return 0;
1842
     *     }
1843
     *     return ($a > $b) ? 1 : -1;
1844
     * };
1845
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1846
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1847
     * </code>
1848
     *
1849
     * @see          http://php.net/manual/en/function.uksort.php
1850
     *
1851
     * @param callable $function
1852
     *
1853
     * @throws \InvalidArgumentException
1854
     *
1855
     * @return $this
1856
     *               <p>(Mutable) Return this Arrayy object.</p>
1857
     *
1858
     * @psalm-return static<TKey,T>
1859
     */
1860 5
    public function customSortKeys(callable $function): self
1861
    {
1862 5
        $this->generatorToArray();
1863
1864 5
        \uksort($this->array, $function);
1865
1866 5
        return $this;
1867
    }
1868
1869
    /**
1870
     * Custom sort by index via "uksort".
1871
     *
1872
     * @see          http://php.net/manual/en/function.uksort.php
1873
     *
1874
     * @param callable $function
1875
     *
1876
     * @throws \InvalidArgumentException
1877
     *
1878
     * @return $this
1879
     *               <p>(Immutable) Return this Arrayy object.</p>
1880
     *
1881
     * @psalm-return static<TKey,T>
1882
     * @psalm-mutation-free
1883
     */
1884 1
    public function customSortKeysImmutable(callable $function): self
1885
    {
1886 1
        $that = clone $this;
1887
1888 1
        $that->generatorToArray();
1889
1890
        /**
1891
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1892
         */
1893 1
        \uksort($that->array, $function);
1894
1895 1
        return $that;
1896
    }
1897
1898
    /**
1899
     * Custom sort by value via "usort".
1900
     *
1901
     * EXAMPLE: <code>
1902
     * $callable = function ($a, $b) {
1903
     *     if ($a == $b) {
1904
     *         return 0;
1905
     *     }
1906
     *     return ($a > $b) ? 1 : -1;
1907
     * };
1908
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1909
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
1910
     * </code>
1911
     *
1912
     * @see          http://php.net/manual/en/function.usort.php
1913
     *
1914
     * @param callable $function
1915
     *
1916
     * @throws \InvalidArgumentException
1917
     *
1918
     * @return $this
1919
     *               <p>(Mutable) Return this Arrayy object.</p>
1920
     *
1921
     * @psalm-return static<TKey,T>
1922
     */
1923 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...
1924
    {
1925 10
        if (\is_callable($function) === false) {
1926
            throw new \InvalidArgumentException('Passed function must be callable');
1927
        }
1928
1929 10
        $this->generatorToArray();
1930
1931 10
        \usort($this->array, $function);
1932
1933 10
        return $this;
1934
    }
1935
1936
    /**
1937
     * Custom sort by value via "usort".
1938
     *
1939
     * @see          http://php.net/manual/en/function.usort.php
1940
     *
1941
     * @param callable $function
1942
     *
1943
     * @throws \InvalidArgumentException
1944
     *
1945
     * @return $this
1946
     *               <p>(Immutable) Return this Arrayy object.</p>
1947
     *
1948
     * @psalm-return static<TKey,T>
1949
     * @psalm-mutation-free
1950
     */
1951 4
    public function customSortValuesImmutable($function): self
1952
    {
1953 4
        $that = clone $this;
1954
1955
        /**
1956
         * @psalm-suppress ImpureMethodCall - object is already cloned
1957
         */
1958 4
        $that->customSortValues($function);
1959
1960 4
        return $that;
1961
    }
1962
1963
    /**
1964
     * Delete the given key or keys.
1965
     *
1966
     * @param int|int[]|string|string[] $keyOrKeys
1967
     *
1968
     * @return void
1969
     */
1970 9
    public function delete($keyOrKeys)
1971
    {
1972 9
        $keyOrKeys = (array) $keyOrKeys;
1973
1974 9
        foreach ($keyOrKeys as $key) {
1975 9
            $this->offsetUnset($key);
1976
        }
1977 9
    }
1978
1979
    /**
1980
     * Return values that are only in the current array.
1981
     *
1982
     * EXAMPLE: <code>
1983
     * a([1 => 1, 2 => 2])->diff([1 => 1]); // Arrayy[2 => 2]
1984
     * </code>
1985
     *
1986
     * @param array ...$array
1987
     *
1988
     * @return static
1989
     *                <p>(Immutable)</p>
1990
     *
1991
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1992
     * @psalm-return static<TKey,T>
1993
     * @psalm-mutation-free
1994
     */
1995 13
    public function diff(...$array): self
1996
    {
1997 13
        return static::create(
1998 13
            \array_diff($this->toArray(), ...$array),
1999 13
            $this->iteratorClass,
2000 13
            false
2001
        );
2002
    }
2003
2004
    /**
2005
     * Return values that are only in the current array.
2006
     *
2007
     * @param array ...$array
2008
     *
2009
     * @return static
2010
     *                <p>(Immutable)</p>
2011
     *
2012
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2013
     * @psalm-return static<TKey,T>
2014
     * @psalm-mutation-free
2015
     */
2016 8
    public function diffKey(...$array): self
2017
    {
2018 8
        return static::create(
2019 8
            \array_diff_key($this->toArray(), ...$array),
2020 8
            $this->iteratorClass,
2021 8
            false
2022
        );
2023
    }
2024
2025
    /**
2026
     * Return values and Keys that are only in the current array.
2027
     *
2028
     * @param array $array
2029
     *
2030
     * @return static
2031
     *                <p>(Immutable)</p>
2032
     *
2033
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2034
     * @psalm-return static<TKey,T>
2035
     * @psalm-mutation-free
2036
     */
2037 8
    public function diffKeyAndValue(array $array = []): self
2038
    {
2039 8
        return static::create(
2040 8
            \array_diff_assoc($this->toArray(), $array),
2041 8
            $this->iteratorClass,
2042 8
            false
2043
        );
2044
    }
2045
2046
    /**
2047
     * Return values that are only in the current multi-dimensional array.
2048
     *
2049
     * @param array                 $array
2050
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
2051
     *
2052
     * @return static
2053
     *                <p>(Immutable)</p>
2054
     *
2055
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2056
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2057
     * @psalm-return static<TKey,T>
2058
     * @psalm-mutation-free
2059
     */
2060 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2061
    {
2062
        // init
2063 1
        $result = [];
2064
2065
        if (
2066 1
            $helperVariableForRecursion !== null
2067
            &&
2068 1
            \is_array($helperVariableForRecursion) === true
2069
        ) {
2070
            $arrayForTheLoop = $helperVariableForRecursion;
2071
        } else {
2072 1
            $arrayForTheLoop = $this->getGenerator();
2073
        }
2074
2075 1
        foreach ($arrayForTheLoop as $key => $value) {
2076 1
            if ($value instanceof self) {
2077 1
                $value = $value->toArray();
2078
            }
2079
2080 1
            if (\array_key_exists($key, $array)) {
2081 1
                if ($value !== $array[$key]) {
2082 1
                    $result[$key] = $value;
2083
                }
2084
            } else {
2085 1
                $result[$key] = $value;
2086
            }
2087
        }
2088
2089 1
        return static::create(
2090 1
            $result,
2091 1
            $this->iteratorClass,
2092 1
            false
2093
        );
2094
    }
2095
2096
    /**
2097
     * Return values that are only in the new $array.
2098
     *
2099
     * @param array $array
2100
     *
2101
     * @return static
2102
     *                <p>(Immutable)</p>
2103
     *
2104
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2105
     * @psalm-return static<TKey,T>
2106
     * @psalm-mutation-free
2107
     */
2108 8
    public function diffReverse(array $array = []): self
2109
    {
2110 8
        return static::create(
2111 8
            \array_diff($array, $this->toArray()),
2112 8
            $this->iteratorClass,
2113 8
            false
2114
        );
2115
    }
2116
2117
    /**
2118
     * Divide an array into two arrays. One with keys and the other with values.
2119
     *
2120
     * @return static
2121
     *                <p>(Immutable)</p>
2122
     *
2123
     * @psalm-return static<TKey,T>
2124
     * @psalm-mutation-free
2125
     */
2126 1
    public function divide(): self
2127
    {
2128 1
        return static::create(
2129
            [
2130 1
                $this->keys(),
2131 1
                $this->values(),
2132
            ],
2133 1
            $this->iteratorClass,
2134 1
            false
2135
        );
2136
    }
2137
2138
    /**
2139
     * Iterate over the current array and modify the array's value.
2140
     *
2141
     * @param \Closure $closure
2142
     *
2143
     * @return static
2144
     *                <p>(Immutable)</p>
2145
     *
2146
     * @psalm-return static<TKey,T>
2147
     * @psalm-mutation-free
2148
     */
2149 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...
2150
    {
2151
        // init
2152 5
        $array = [];
2153
2154 5
        foreach ($this->getGenerator() as $key => $value) {
2155 5
            $array[$key] = $closure($value, $key);
2156
        }
2157
2158 5
        return static::create(
2159 5
            $array,
2160 5
            $this->iteratorClass,
2161 5
            false
2162
        );
2163
    }
2164
2165
    /**
2166
     * Sets the internal iterator to the last element in the array and returns this element.
2167
     *
2168
     * @return mixed
2169
     */
2170
    public function end()
2171
    {
2172
        return \end($this->array);
2173
    }
2174
2175
    /**
2176
     * Check if a value is in the current array using a closure.
2177
     *
2178
     * @param \Closure $closure
2179
     *
2180
     * @return bool
2181
     *              <p>Returns true if the given value is found, false otherwise.</p>
2182
     */
2183 4
    public function exists(\Closure $closure): bool
2184
    {
2185
        // init
2186 4
        $isExists = false;
2187
2188 4
        foreach ($this->getGenerator() as $key => $value) {
2189 3
            if ($closure($value, $key)) {
2190 1
                $isExists = true;
2191
2192 3
                break;
2193
            }
2194
        }
2195
2196 4
        return $isExists;
2197
    }
2198
2199
    /**
2200
     * Fill the array until "$num" with "$default" values.
2201
     *
2202
     * @param int   $num
2203
     * @param mixed $default
2204
     *
2205
     * @return static
2206
     *                <p>(Immutable)</p>
2207
     *
2208
     * @psalm-return static<TKey,T>
2209
     * @psalm-mutation-free
2210
     */
2211 8
    public function fillWithDefaults(int $num, $default = null): self
2212
    {
2213 8
        if ($num < 0) {
2214 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2215
        }
2216
2217 7
        $this->generatorToArray();
2218
2219 7
        $tmpArray = $this->array;
2220
2221 7
        $count = \count($tmpArray);
2222
2223 7
        while ($count < $num) {
2224 4
            $tmpArray[] = $default;
2225 4
            ++$count;
2226
        }
2227
2228 7
        return static::create(
2229 7
            $tmpArray,
2230 7
            $this->iteratorClass,
2231 7
            false
2232
        );
2233
    }
2234
2235
    /**
2236
     * Find all items in an array that pass the truth test.
2237
     *
2238
     * @param \Closure|null $closure [optional] <p>
2239
     *                               The callback function to use
2240
     *                               </p>
2241
     *                               <p>
2242
     *                               If no callback is supplied, all entries of
2243
     *                               input equal to false (see
2244
     *                               converting to
2245
     *                               boolean) will be removed.
2246
     *                               </p>
2247
     * @param int           $flag    [optional] <p>
2248
     *                               Flag determining what arguments are sent to <i>callback</i>:
2249
     *                               </p><ul>
2250
     *                               <li>
2251
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
2252
     *                               to <i>callback</i> instead of the value</span>
2253
     *                               </li>
2254
     *                               <li>
2255
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
2256
     *                               arguments to <i>callback</i> instead of the value</span>
2257
     *                               </li>
2258
     *                               </ul>
2259
     *
2260
     * @return static
2261
     *                <p>(Immutable)</p>
2262
     *
2263
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
2264
     * @psalm-return static<TKey,T>
2265
     * @psalm-mutation-free
2266
     */
2267 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2268
    {
2269 12
        if (!$closure) {
2270 1
            return $this->clean();
2271
        }
2272
2273 12
        return static::create(
2274 12
            \array_filter($this->toArray(), $closure, $flag),
2275 12
            $this->iteratorClass,
2276 12
            false
2277
        );
2278
    }
2279
2280
    /**
2281
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2282
     * property within that.
2283
     *
2284
     * @param string          $property
2285
     * @param string|string[] $value
2286
     * @param string          $comparisonOp
2287
     *                                      <p>
2288
     *                                      'eq' (equals),<br />
2289
     *                                      'gt' (greater),<br />
2290
     *                                      'gte' || 'ge' (greater or equals),<br />
2291
     *                                      'lt' (less),<br />
2292
     *                                      'lte' || 'le' (less or equals),<br />
2293
     *                                      'ne' (not equals),<br />
2294
     *                                      'contains',<br />
2295
     *                                      'notContains',<br />
2296
     *                                      'newer' (via strtotime),<br />
2297
     *                                      'older' (via strtotime),<br />
2298
     *                                      </p>
2299
     *
2300
     * @return static
2301
     *                <p>(Immutable)</p>
2302
     *
2303
     * @psalm-return static<TKey,T>
2304
     * @psalm-mutation-free
2305
     *
2306
     * @psalm-suppress MissingClosureReturnType
2307
     * @psalm-suppress MissingClosureParamType
2308
     */
2309 1
    public function filterBy(
2310
        string $property,
2311
        $value,
2312
        string $comparisonOp = null
2313
    ): self {
2314 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...
2315 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2316
        }
2317
2318
        $ops = [
2319
            'eq' => static function ($item, $prop, $value): bool {
2320 1
                return $item[$prop] === $value;
2321 1
            },
2322
            'gt' => static function ($item, $prop, $value): bool {
2323
                return $item[$prop] > $value;
2324 1
            },
2325
            'ge' => static function ($item, $prop, $value): bool {
2326
                return $item[$prop] >= $value;
2327 1
            },
2328
            'gte' => static function ($item, $prop, $value): bool {
2329
                return $item[$prop] >= $value;
2330 1
            },
2331
            'lt' => static function ($item, $prop, $value): bool {
2332 1
                return $item[$prop] < $value;
2333 1
            },
2334
            'le' => static function ($item, $prop, $value): bool {
2335
                return $item[$prop] <= $value;
2336 1
            },
2337
            'lte' => static function ($item, $prop, $value): bool {
2338
                return $item[$prop] <= $value;
2339 1
            },
2340
            'ne' => static function ($item, $prop, $value): bool {
2341
                return $item[$prop] !== $value;
2342 1
            },
2343
            'contains' => static function ($item, $prop, $value): bool {
2344 1
                return \in_array($item[$prop], (array) $value, true);
2345 1
            },
2346
            'notContains' => static function ($item, $prop, $value): bool {
2347
                return !\in_array($item[$prop], (array) $value, true);
2348 1
            },
2349
            'newer' => static function ($item, $prop, $value): bool {
2350
                return \strtotime($item[$prop]) > \strtotime($value);
2351 1
            },
2352
            'older' => static function ($item, $prop, $value): bool {
2353
                return \strtotime($item[$prop]) < \strtotime($value);
2354 1
            },
2355
        ];
2356
2357 1
        $result = \array_values(
2358 1
            \array_filter(
2359 1
                $this->toArray(false, true),
2360
                static function ($item) use (
2361 1
                    $property,
2362 1
                    $value,
2363 1
                    $ops,
2364 1
                    $comparisonOp
2365
                ) {
2366 1
                    $item = (array) $item;
2367 1
                    $itemArrayy = static::create($item);
2368 1
                    $item[$property] = $itemArrayy->get($property, []);
2369
2370 1
                    return $ops[$comparisonOp]($item, $property, $value);
2371 1
                }
2372
            )
2373
        );
2374
2375 1
        return static::create(
2376 1
            $result,
2377 1
            $this->iteratorClass,
2378 1
            false
2379
        );
2380
    }
2381
2382
    /**
2383
     * Find the first item in an array that passes the truth test,
2384
     *  otherwise return false
2385
     *
2386
     * @param \Closure $closure
2387
     *
2388
     * @return false|mixed
2389
     *                     <p>Return false if we did not find the value.</p>
2390
     */
2391 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...
2392
    {
2393 8
        foreach ($this->getGenerator() as $key => $value) {
2394 6
            if ($closure($value, $key)) {
2395 6
                return $value;
2396
            }
2397
        }
2398
2399 3
        return false;
2400
    }
2401
2402
    /**
2403
     * find by ...
2404
     *
2405
     * @param string          $property
2406
     * @param string|string[] $value
2407
     * @param string          $comparisonOp
2408
     *
2409
     * @return static
2410
     *                <p>(Immutable)</p>
2411
     *
2412
     * @psalm-return static<TKey,T>
2413
     * @psalm-mutation-free
2414
     */
2415 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2416
    {
2417 1
        return $this->filterBy($property, $value, $comparisonOp);
2418
    }
2419
2420
    /**
2421
     * Get the first value from the current array.
2422
     *
2423
     * @return mixed
2424
     *               <p>Return null if there wasn't a element.</p>
2425
     */
2426 22
    public function first()
2427
    {
2428 22
        $key_first = $this->firstKey();
2429 22
        if ($key_first === null) {
2430 3
            return null;
2431
        }
2432
2433 19
        return $this->get($key_first);
2434
    }
2435
2436
    /**
2437
     * Get the first key from the current array.
2438
     *
2439
     * @return mixed
2440
     *               <p>Return null if there wasn't a element.</p>
2441
     * @psalm-mutation-free
2442
     */
2443 29
    public function firstKey()
2444
    {
2445 29
        $this->generatorToArray();
2446
2447 29
        return \array_key_first($this->array);
2448
    }
2449
2450
    /**
2451
     * Get the first value(s) from the current array.
2452
     * And will return an empty array if there was no first entry.
2453
     *
2454
     * @param int|null $number <p>How many values you will take?</p>
2455
     *
2456
     * @return static
2457
     *                <p>(Immutable)</p>
2458
     *
2459
     * @psalm-return static<TKey,T>
2460
     * @psalm-mutation-free
2461
     */
2462 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...
2463
    {
2464 37
        $arrayTmp = $this->toArray();
2465
2466 37
        if ($number === null) {
2467 14
            $array = (array) \array_shift($arrayTmp);
2468
        } else {
2469 23
            $array = \array_splice($arrayTmp, 0, $number);
2470
        }
2471
2472 37
        return static::create(
2473 37
            $array,
2474 37
            $this->iteratorClass,
2475 37
            false
2476
        );
2477
    }
2478
2479
    /**
2480
     * Get the first value(s) from the current array.
2481
     * And will return an empty array if there was no first entry.
2482
     *
2483
     * @param int|null $number <p>How many values you will take?</p>
2484
     *
2485
     * @return static
2486
     *                <p>(Immutable)</p>
2487
     *
2488
     * @psalm-return static<TKey,T>
2489
     * @psalm-mutation-free
2490
     */
2491 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...
2492
    {
2493 3
        $arrayTmp = $this->keys()->toArray();
2494
2495 3
        if ($number === null) {
2496
            $array = (array) \array_shift($arrayTmp);
2497
        } else {
2498 3
            $array = \array_splice($arrayTmp, 0, $number);
2499
        }
2500
2501 3
        return static::create(
2502 3
            $array,
2503 3
            $this->iteratorClass,
2504 3
            false
2505
        );
2506
    }
2507
2508
    /**
2509
     * Get and rmove the first value(s) from the current array.
2510
     * And will return an empty array if there was no first entry.
2511
     *
2512
     * @param int|null $number <p>How many values you will take?</p>
2513
     *
2514
     * @return $this
2515
     *               <p>(Mutable)</p>
2516
     *
2517
     * @psalm-return static<TKey,T>
2518
     */
2519 34
    public function firstsMutable(int $number = null): self
2520
    {
2521 34
        $this->generatorToArray();
2522
2523 34
        if ($number === null) {
2524 19
            $this->array = (array) \array_shift($this->array);
2525
        } else {
2526 15
            $this->array = \array_splice($this->array, 0, $number);
2527
        }
2528
2529 34
        return $this;
2530
    }
2531
2532
    /**
2533
     * Exchanges all keys with their associated values in an array.
2534
     *
2535
     * @return static
2536
     *                <p>(Immutable)</p>
2537
     *
2538
     * @psalm-return static<array-key,TKey>
2539
     * @psalm-mutation-free
2540
     */
2541 1
    public function flip(): self
2542
    {
2543
        $generator = function (): \Generator {
2544 1
            foreach ($this->getGenerator() as $key => $value) {
2545 1
                yield (string) $value => $key;
2546
            }
2547 1
        };
2548
2549 1
        return static::create(
2550 1
            $generator,
2551 1
            $this->iteratorClass,
2552 1
            false
2553
        );
2554
    }
2555
2556
    /**
2557
     * Get a value from an array (optional using dot-notation).
2558
     *
2559
     * @param mixed $key            <p>The key to look for.</p>
2560
     * @param mixed $fallback       <p>Value to fallback to.</p>
2561
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2562
     *                              class.</p>
2563
     * @param bool  $useByReference
2564
     *
2565
     * @return mixed|static
2566
     *
2567
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2568
     * @psalm-mutation-free
2569
     */
2570 242
    public function get(
2571
        $key,
2572
        $fallback = null,
2573
        array $array = null,
2574
        bool $useByReference = false
2575
    ) {
2576 242
        if ($array !== null) {
2577 4
            if ($useByReference) {
2578
                $usedArray = &$array;
2579
            } else {
2580 4
                $usedArray = $array;
2581
            }
2582
        } else {
2583 239
            $this->generatorToArray();
2584
2585 239
            if ($useByReference) {
2586 126
                $usedArray = &$this->array;
2587
            } else {
2588 131
                $usedArray = $this->array;
2589
            }
2590
        }
2591
2592 242
        if ($key === null) {
2593 1
            return static::create(
2594 1
                [],
2595 1
                $this->iteratorClass,
2596 1
                false
2597 1
            )->createByReference($usedArray);
2598
        }
2599
2600
        // php cast "bool"-index into "int"-index
2601 242
        if ((bool) $key === $key) {
2602 3
            $key = (int) $key;
2603
        }
2604
2605 242
        if (\array_key_exists($key, $usedArray) === true) {
2606 204 View Code Duplication
            if (\is_array($usedArray[$key]) === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2607 19
                return static::create(
2608 19
                    [],
2609 19
                    $this->iteratorClass,
2610 19
                    false
2611 19
                )->createByReference($usedArray[$key]);
2612
            }
2613
2614 190
            return $usedArray[$key];
2615
        }
2616
2617
        // crawl through array, get key according to object or not
2618 61
        $usePath = false;
2619
        if (
2620 61
            $this->pathSeparator
2621
            &&
2622 61
            (string) $key === $key
2623
            &&
2624 61
            \strpos($key, $this->pathSeparator) !== false
2625
        ) {
2626 31
            $segments = \explode($this->pathSeparator, (string) $key);
2627 31
            if ($segments !== false) {
2628 31
                $usePath = true;
2629 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2630
2631 31
                foreach ($segments as $segment) {
2632
                    if (
2633
                        (
2634 31
                            \is_array($usedArrayTmp) === true
2635
                            ||
2636 31
                            $usedArrayTmp instanceof \ArrayAccess
2637
                        )
2638
                        &&
2639 31
                        isset($usedArrayTmp[$segment])
2640
                    ) {
2641 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2642
2643 30
                        continue;
2644
                    }
2645
2646
                    if (
2647 14
                        \is_object($usedArrayTmp) === true
2648
                        &&
2649 14
                        \property_exists($usedArrayTmp, $segment)
2650
                    ) {
2651 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2652
2653 1
                        continue;
2654
                    }
2655
2656 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2657 1
                        $segmentsTmp = $segments;
2658 1
                        unset($segmentsTmp[0]);
2659 1
                        $keyTmp = \implode('.', $segmentsTmp);
2660 1
                        $returnTmp = static::create(
2661 1
                            [],
2662 1
                            $this->iteratorClass,
2663 1
                            false
2664
                        );
2665 1
                        foreach ($this->getAll() as $dataTmp) {
2666 1
                            if ($dataTmp instanceof self) {
2667
                                $returnTmp->add($dataTmp->get($keyTmp));
2668
2669
                                continue;
2670
                            }
2671
2672
                            if (
2673
                                (
2674 1
                                    \is_array($dataTmp) === true
2675
                                    ||
2676 1
                                    $dataTmp instanceof \ArrayAccess
2677
                                )
2678
                                &&
2679 1
                                isset($dataTmp[$keyTmp])
2680
                            ) {
2681
                                $returnTmp->add($dataTmp[$keyTmp]);
2682
2683
                                continue;
2684
                            }
2685
2686
                            if (
2687 1
                                \is_object($dataTmp) === true
2688
                                &&
2689 1
                                \property_exists($dataTmp, $keyTmp)
2690
                            ) {
2691 1
                                $returnTmp->add($dataTmp->{$keyTmp});
2692
2693
                                /** @noinspection UnnecessaryContinueInspection */
2694 1
                                continue;
2695
                            }
2696
                        }
2697
2698 1
                        if ($returnTmp->count() > 0) {
2699 1
                            return $returnTmp;
2700
                        }
2701
                    }
2702
2703 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2704
                }
2705
            }
2706
        }
2707
2708 58
        if (isset($usedArrayTmp)) {
2709 28 View Code Duplication
            if (!$usePath && !isset($usedArrayTmp[$key])) {
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...
2710
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2711
            }
2712
2713 28 View Code Duplication
            if (\is_array($usedArrayTmp) === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2714 6
                return static::create(
2715 6
                    [],
2716 6
                    $this->iteratorClass,
2717 6
                    false
2718 6
                )->createByReference($usedArrayTmp);
2719
            }
2720
2721 28
            return $usedArrayTmp;
2722
        }
2723
2724 30 View Code Duplication
        if (!$usePath && !isset($usedArray[$key])) {
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...
2725 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2726
        }
2727
2728
        return static::create(
2729
            [],
2730
            $this->iteratorClass,
2731
            false
2732
        )->createByReference($usedArray);
2733
    }
2734
2735
    /**
2736
     * alias: for "Arrayy->toArray()"
2737
     *
2738
     * @return array
2739
     *
2740
     * @see          Arrayy::getArray()
2741
     *
2742
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2743
     */
2744 15
    public function getAll(): array
2745
    {
2746 15
        return $this->toArray();
2747
    }
2748
2749
    /**
2750
     * Get the current array from the "Arrayy"-object.
2751
     *
2752
     * alias for "toArray()"
2753
     *
2754
     * @param bool $convertAllArrayyElements <p>
2755
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2756
     *                                       </p>
2757
     * @param bool $preserveKeys             <p>
2758
     *                                       e.g.: A generator maybe return the same key more then once,
2759
     *                                       so maybe you will ignore the keys.
2760
     *                                       </p>
2761
     *
2762
     * @return array
2763
     *
2764
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2765
     * @psalm-mutation-free
2766
     *
2767
     * @see Arrayy::toArray()
2768
     */
2769 500
    public function getArray(
2770
        bool $convertAllArrayyElements = false,
2771
        bool $preserveKeys = true
2772
    ): array {
2773 500
        return $this->toArray(
2774 500
            $convertAllArrayyElements,
2775 500
            $preserveKeys
2776
        );
2777
    }
2778
2779
    /**
2780
     * @param string $json
2781
     *
2782
     * @return $this
2783
     */
2784 3
    public static function createFromJsonMapper(string $json)
2785
    {
2786
        // init
2787 3
        $class = static::create();
2788
2789 3
        $jsonObject = \json_decode($json, false);
2790
2791 3
        $mapper = new \Arrayy\Mapper\Json();
2792 View Code Duplication
        $mapper->undefinedPropertyHandler = static function ($object, $key, $jsonValue) use ($class) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2793
            if ($class->checkPropertiesMismatchInConstructor) {
2794
                throw new \TypeError('Property mismatch - input: ' . \print_r(['key' => $key, 'jsonValue' => $jsonValue], true) . ' for object: ' . \get_class($object));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...' . \get_class($object).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2795
            }
2796
        };
2797
2798 3
        return $mapper->map($jsonObject, $class);
2799
    }
2800
2801
    /**
2802
     * @return array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
0 ignored issues
show
Documentation introduced by
The doc-type array<int|string,TypeChe...nterface>|TypeInterface could not be parsed: Expected "|" or "end of type", but got "<" at position 57. (view supported doc-types)

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

Loading history...
2803
     *
2804
     * @internal
2805
     */
2806 6
    public function getPhpDocPropertiesFromClass()
2807
    {
2808 6
        if ($this->properties === []) {
2809 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2810
        }
2811
2812 6
        return $this->properties;
2813
    }
2814
2815
    /**
2816
     * Get the current array from the "Arrayy"-object as list.
2817
     *
2818
     * alias for "toList()"
2819
     *
2820
     * @param bool $convertAllArrayyElements <p>
2821
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2822
     *                                       </p>
2823
     *
2824
     * @return array
2825
     *
2826
     * @psalm-return array<int,mixed>|array<int,T>
2827
     * @psalm-mutation-free
2828
     *
2829
     * @see Arrayy::toList()
2830
     */
2831 1
    public function getList(bool $convertAllArrayyElements = false): array
2832
    {
2833 1
        return $this->toList($convertAllArrayyElements);
2834
    }
2835
2836
    /**
2837
     * Returns the values from a single column of the input array, identified by
2838
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2839
     *
2840
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2841
     * array by the values from the $indexKey column in the input array.
2842
     *
2843
     * @param mixed $columnKey
2844
     * @param mixed $indexKey
2845
     *
2846
     * @return static
2847
     *                <p>(Immutable)</p>
2848
     *
2849
     * @psalm-return static<TKey,T>
2850
     * @psalm-mutation-free
2851
     */
2852 1
    public function getColumn($columnKey = null, $indexKey = null): self
2853
    {
2854 1
        return static::create(
2855 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2856 1
            $this->iteratorClass,
2857 1
            false
2858
        );
2859
    }
2860
2861
    /**
2862
     * Get the current array from the "Arrayy"-object as generator by reference.
2863
     *
2864
     * @return \Generator
2865
     *
2866
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2867
     */
2868 75
    public function &getGeneratorByReference(): \Generator
2869
    {
2870 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2871
            // -> false-positive -> see "&" from method
2872
            /** @noinspection YieldFromCanBeUsedInspection */
2873 17
            foreach ($this->generator as $key => $value) {
2874 17
                yield $key => $value;
2875
            }
2876
2877 5
            return;
2878
        }
2879
2880
        // -> false-positive -> see "&$value"
2881
        /** @noinspection YieldFromCanBeUsedInspection */
2882
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
2883 59
        foreach ($this->array as $key => &$value) {
2884 54
            yield $key => $value;
2885
        }
2886 35
    }
2887
2888
    /**
2889
     * Get the current array from the "Arrayy"-object as generator.
2890
     *
2891
     * @return \Generator
2892
     *
2893
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2894
     * @psalm-mutation-free
2895
     */
2896 998
    public function getGenerator(): \Generator
2897
    {
2898 998
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2899 29
            yield from $this->generator;
2900
2901 29
            return;
2902
        }
2903
2904 996
        yield from $this->array;
2905 970
    }
2906
2907
    /**
2908
     * alias: for "Arrayy->keys()"
2909
     *
2910
     * @return static
2911
     *                <p>(Immutable)</p>
2912
     *
2913
     * @see          Arrayy::keys()
2914
     *
2915
     * @psalm-return static<array-key,TKey>
2916
     * @psalm-mutation-free
2917
     */
2918 2
    public function getKeys()
2919
    {
2920 2
        return $this->keys();
2921
    }
2922
2923
    /**
2924
     * Get the current array from the "Arrayy"-object as object.
2925
     *
2926
     * @return \stdClass
2927
     */
2928 4
    public function getObject(): \stdClass
2929
    {
2930 4
        return self::arrayToObject($this->toArray());
2931
    }
2932
2933
    /**
2934
     * alias: for "Arrayy->randomImmutable()"
2935
     *
2936
     * @return static
2937
     *                <p>(Immutable)</p>
2938
     *
2939
     * @see          Arrayy::randomImmutable()
2940
     *
2941
     * @psalm-return static<int|array-key,T>
2942
     */
2943 4
    public function getRandom(): self
2944
    {
2945 4
        return $this->randomImmutable();
2946
    }
2947
2948
    /**
2949
     * alias: for "Arrayy->randomKey()"
2950
     *
2951
     * @return mixed
2952
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2953
     *
2954
     * @see Arrayy::randomKey()
2955
     */
2956 3
    public function getRandomKey()
2957
    {
2958 3
        return $this->randomKey();
2959
    }
2960
2961
    /**
2962
     * alias: for "Arrayy->randomKeys()"
2963
     *
2964
     * @param int $number
2965
     *
2966
     * @return static
2967
     *                <p>(Immutable)</p>
2968
     *
2969
     * @see          Arrayy::randomKeys()
2970
     *
2971
     * @psalm-return static<TKey,T>
2972
     */
2973 8
    public function getRandomKeys(int $number): self
2974
    {
2975 8
        return $this->randomKeys($number);
2976
    }
2977
2978
    /**
2979
     * alias: for "Arrayy->randomValue()"
2980
     *
2981
     * @return mixed
2982
     *               <p>Get a random value or null if there wasn't a value.</p>
2983
     *
2984
     * @see Arrayy::randomValue()
2985
     */
2986 3
    public function getRandomValue()
2987
    {
2988 3
        return $this->randomValue();
2989
    }
2990
2991
    /**
2992
     * alias: for "Arrayy->randomValues()"
2993
     *
2994
     * @param int $number
2995
     *
2996
     * @return static
2997
     *                <p>(Immutable)</p>
2998
     *
2999
     * @see          Arrayy::randomValues()
3000
     *
3001
     * @psalm-return static<TKey,T>
3002
     */
3003 6
    public function getRandomValues(int $number): self
3004
    {
3005 6
        return $this->randomValues($number);
3006
    }
3007
3008
    /**
3009
     * Gets all values.
3010
     *
3011
     * @return static
3012
     *                <p>The values of all elements in this array, in the order they
3013
     *                appear in the array.</p>
3014
     *
3015
     * @psalm-return static<TKey,T>
3016
     */
3017 4
    public function getValues()
3018
    {
3019 4
        $this->generatorToArray(false);
3020
3021 4
        return static::create(
3022 4
            \array_values($this->array),
3023 4
            $this->iteratorClass,
3024 4
            false
3025
        );
3026
    }
3027
3028
    /**
3029
     * Gets all values via Generator.
3030
     *
3031
     * @return \Generator
3032
     *                    <p>The values of all elements in this array, in the order they
3033
     *                    appear in the array as Generator.</p>
3034
     *
3035
     * @psalm-return \Generator<TKey,T>
3036
     */
3037 4
    public function getValuesYield(): \Generator
3038
    {
3039 4
        yield from $this->getGenerator();
3040 4
    }
3041
3042
    /**
3043
     * Group values from a array according to the results of a closure.
3044
     *
3045
     * @param callable|string $grouper  <p>A callable function name.</p>
3046
     * @param bool            $saveKeys
3047
     *
3048
     * @return static
3049
     *                <p>(Immutable)</p>
3050
     *
3051
     * @psalm-return static<TKey,T>
3052
     * @psalm-mutation-free
3053
     */
3054 4
    public function group($grouper, bool $saveKeys = false): self
3055
    {
3056
        // init
3057 4
        $result = [];
3058
3059
        // Iterate over values, group by property/results from closure.
3060 4
        foreach ($this->getGenerator() as $key => $value) {
3061 4
            if (\is_callable($grouper) === true) {
3062 3
                $groupKey = $grouper($value, $key);
3063
            } else {
3064 1
                $groupKey = $this->get($grouper);
3065
            }
3066
3067 4
            $newValue = $this->get($groupKey, null, $result);
3068
3069 4
            if ($groupKey instanceof self) {
3070
                $groupKey = $groupKey->toArray();
3071
            }
3072
3073 4
            if ($newValue instanceof self) {
3074 4
                $newValue = $newValue->toArray();
3075
            }
3076
3077
            // Add to results.
3078 4
            if ($groupKey !== null) {
3079 3
                if ($saveKeys) {
3080 2
                    $result[$groupKey] = $newValue;
3081 2
                    $result[$groupKey][$key] = $value;
3082
                } else {
3083 1
                    $result[$groupKey] = $newValue;
3084 4
                    $result[$groupKey][] = $value;
3085
                }
3086
            }
3087
        }
3088
3089 4
        return static::create(
3090 4
            $result,
3091 4
            $this->iteratorClass,
3092 4
            false
3093
        );
3094
    }
3095
3096
    /**
3097
     * Check if an array has a given key.
3098
     *
3099
     * @param mixed $key
3100
     *
3101
     * @return bool
3102
     */
3103 30
    public function has($key): bool
3104
    {
3105 30
        static $UN_FOUND = null;
3106
3107 30
        if ($UN_FOUND === null) {
3108
            // Generate unique string to use as marker.
3109 1
            $UN_FOUND = \uniqid('arrayy', true);
3110
        }
3111
3112 30
        if (\is_array($key)) {
3113 1
            if ($key === []) {
3114
                return false;
3115
            }
3116
3117 1
            foreach ($key as $keyTmp) {
3118 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3119 1
                if ($found === false) {
3120 1
                    return false;
3121
                }
3122
            }
3123
3124 1
            return true;
3125
        }
3126
3127 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3128
    }
3129
3130
    /**
3131
     * Check if an array has a given value.
3132
     *
3133
     * INFO: if you need to search recursive please use ```contains()```
3134
     *
3135
     * @param mixed $value
3136
     *
3137
     * @return bool
3138
     */
3139 1
    public function hasValue($value): bool
3140
    {
3141 1
        return $this->contains($value);
3142
    }
3143
3144
    /**
3145
     * Implodes the values of this array.
3146
     *
3147
     * @param string $glue
3148
     * @param string $prefix
3149
     *
3150
     * @return string
3151
     * @psalm-mutation-free
3152
     */
3153 28
    public function implode(string $glue = '', string $prefix = ''): string
3154
    {
3155 28
        return $prefix . $this->implode_recursive($glue, $this->toArray(), false);
3156
    }
3157
3158
    /**
3159
     * Implodes the keys of this array.
3160
     *
3161
     * @param string $glue
3162
     *
3163
     * @return string
3164
     * @psalm-mutation-free
3165
     */
3166 8
    public function implodeKeys(string $glue = ''): string
3167
    {
3168 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3169
    }
3170
3171
    /**
3172
     * Given a list and an iterate-function that returns
3173
     * a key for each element in the list (or a property name),
3174
     * returns an object with an index of each item.
3175
     *
3176
     * @param mixed $key
3177
     *
3178
     * @return static
3179
     *                <p>(Immutable)</p>
3180
     *
3181
     * @psalm-return static<TKey,T>
3182
     * @psalm-mutation-free
3183
     */
3184 4 View Code Duplication
    public function indexBy($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...
3185
    {
3186
        // init
3187 4
        $results = [];
3188
3189 4
        foreach ($this->getGenerator() as $a) {
3190 4
            if (\array_key_exists($key, $a) === true) {
3191 4
                $results[$a[$key]] = $a;
3192
            }
3193
        }
3194
3195 4
        return static::create(
3196 4
            $results,
3197 4
            $this->iteratorClass,
3198 4
            false
3199
        );
3200
    }
3201
3202
    /**
3203
     * alias: for "Arrayy->searchIndex()"
3204
     *
3205
     * @param mixed $value <p>The value to search for.</p>
3206
     *
3207
     * @return false|mixed
3208
     *
3209
     * @see Arrayy::searchIndex()
3210
     */
3211 4
    public function indexOf($value)
3212
    {
3213 4
        return $this->searchIndex($value);
3214
    }
3215
3216
    /**
3217
     * Get everything but the last..$to items.
3218
     *
3219
     * @param int $to
3220
     *
3221
     * @return static
3222
     *                <p>(Immutable)</p>
3223
     *
3224
     * @psalm-return static<TKey,T>
3225
     * @psalm-mutation-free
3226
     */
3227 12
    public function initial(int $to = 1): self
3228
    {
3229 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3230
    }
3231
3232
    /**
3233
     * Return an array with all elements found in input array.
3234
     *
3235
     * @param array $search
3236
     * @param bool  $keepKeys
3237
     *
3238
     * @return static
3239
     *                <p>(Immutable)</p>
3240
     *
3241
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3242
     * @psalm-return static<TKey,T>
3243
     * @psalm-mutation-free
3244
     */
3245 4
    public function intersection(array $search, bool $keepKeys = false): self
3246
    {
3247 4
        if ($keepKeys) {
3248
            /**
3249
             * @psalm-suppress MissingClosureReturnType
3250
             * @psalm-suppress MissingClosureParamType
3251
             */
3252 1
            return static::create(
3253 1
                \array_uintersect(
3254 1
                    $this->toArray(),
3255 1
                    $search,
3256
                    static function ($a, $b) {
3257 1
                        return $a === $b ? 0 : -1;
3258 1
                    }
3259
                ),
3260 1
                $this->iteratorClass,
3261 1
                false
3262
            );
3263
        }
3264
3265 3
        return static::create(
3266 3
            \array_values(\array_intersect($this->toArray(), $search)),
3267 3
            $this->iteratorClass,
3268 3
            false
3269
        );
3270
    }
3271
3272
    /**
3273
     * Return an array with all elements found in input array.
3274
     *
3275
     * @param array ...$array
3276
     *
3277
     * @return static
3278
     *                <p>(Immutable)</p>
3279
     *
3280
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3281
     * @psalm-return static<TKey,T>
3282
     * @psalm-mutation-free
3283
     */
3284 1
    public function intersectionMulti(...$array): self
3285
    {
3286 1
        return static::create(
3287 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3288 1
            $this->iteratorClass,
3289 1
            false
3290
        );
3291
    }
3292
3293
    /**
3294
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3295
     *
3296
     * @param array $search
3297
     *
3298
     * @return bool
3299
     *
3300
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3301
     */
3302 1
    public function intersects(array $search): bool
3303
    {
3304 1
        return $this->intersection($search)->count() > 0;
3305
    }
3306
3307
    /**
3308
     * Invoke a function on all of an array's values.
3309
     *
3310
     * @param callable $callable
3311
     * @param mixed    $arguments
3312
     *
3313
     * @return static
3314
     *                <p>(Immutable)</p>
3315
     *
3316
     * @psalm-param  callable(T=,mixed):mixed $callable
3317
     * @psalm-return static<TKey,T>
3318
     * @psalm-mutation-free
3319
     */
3320 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...
3321
    {
3322
        // If one argument given for each iteration, create an array for it.
3323 1
        if (\is_array($arguments) === false) {
3324 1
            $arguments = \array_fill(
3325 1
                0,
3326 1
                $this->count(),
3327 1
                $arguments
3328
            );
3329
        }
3330
3331
        // If the callable has arguments, pass them.
3332 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...
3333 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3334
        } else {
3335 1
            $array = $this->map($callable);
3336
        }
3337
3338 1
        return static::create(
3339 1
            $array,
3340 1
            $this->iteratorClass,
3341 1
            false
3342
        );
3343
    }
3344
3345
    /**
3346
     * Check whether array is associative or not.
3347
     *
3348
     * @param bool $recursive
3349
     *
3350
     * @return bool
3351
     *              <p>Returns true if associative, false otherwise.</p>
3352
     */
3353 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...
3354
    {
3355 15
        if ($this->isEmpty()) {
3356 3
            return false;
3357
        }
3358
3359
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3360 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3361 13
            if ((string) $key !== $key) {
3362 13
                return false;
3363
            }
3364
        }
3365
3366 3
        return true;
3367
    }
3368
3369
    /**
3370
     * Check if a given key or keys are empty.
3371
     *
3372
     * @param int|int[]|string|string[]|null $keys
3373
     *
3374
     * @return bool
3375
     *              <p>Returns true if empty, false otherwise.</p>
3376
     * @psalm-mutation-free
3377
     */
3378 45
    public function isEmpty($keys = null): bool
3379
    {
3380 45
        if ($this->generator) {
3381
            return $this->toArray() === [];
3382
        }
3383
3384 45
        if ($keys === null) {
3385 43
            return $this->array === [];
3386
        }
3387
3388 2
        foreach ((array) $keys as $key) {
3389 2
            if (!empty($this->get($key))) {
3390 2
                return false;
3391
            }
3392
        }
3393
3394 2
        return true;
3395
    }
3396
3397
    /**
3398
     * Check if the current array is equal to the given "$array" or not.
3399
     *
3400
     * @param array $array
3401
     *
3402
     * @return bool
3403
     *
3404
     * @psalm-param array<mixed,mixed> $array
3405
     */
3406 1
    public function isEqual(array $array): bool
3407
    {
3408 1
        return $this->toArray() === $array;
3409
    }
3410
3411
    /**
3412
     * Check if the current array is a multi-array.
3413
     *
3414
     * @return bool
3415
     */
3416 22
    public function isMultiArray(): bool
3417
    {
3418
        return !(
3419 22
            \count($this->toArray(), \COUNT_NORMAL)
3420
            ===
3421 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3422
        );
3423
    }
3424
3425
    /**
3426
     * Check whether array is numeric or not.
3427
     *
3428
     * @return bool
3429
     *              <p>Returns true if numeric, false otherwise.</p>
3430
     */
3431 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...
3432
    {
3433 5
        if ($this->isEmpty()) {
3434 2
            return false;
3435
        }
3436
3437
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3438 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3439 4
            if ((int) $key !== $key) {
3440 4
                return false;
3441
            }
3442
        }
3443
3444 2
        return true;
3445
    }
3446
3447
    /**
3448
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3449
     *
3450
     * @param bool $recursive
3451
     *
3452
     * @return bool
3453
     * @psalm-mutation-free
3454
     */
3455 9
    public function isSequential(bool $recursive = false): bool
3456
    {
3457
3458
        // recursive
3459
3460 9
        if ($recursive === true) {
3461
            return $this->array_keys_recursive($this->toArray())
3462
                   ===
3463
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3464
        }
3465
3466
        // non recursive
3467
3468 9
        return \array_keys($this->toArray())
3469
               ===
3470 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3471
    }
3472
3473
    /**
3474
     * @return array
3475
     *
3476
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3477
     */
3478 2
    public function jsonSerialize(): array
3479
    {
3480 2
        return $this->toArray();
3481
    }
3482
3483
    /**
3484
     * Gets the key/index of the element at the current internal iterator position.
3485
     *
3486
     * @return int|string|null
3487
     */
3488
    public function key()
3489
    {
3490
        return \key($this->array);
3491
    }
3492
3493
    /**
3494
     * Checks if the given key exists in the provided array.
3495
     *
3496
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3497
     *       then you need to use "Arrayy->offsetExists()".
3498
     *
3499
     * @param int|string $key the key to look for
3500
     *
3501
     * @return bool
3502
     * @psalm-mutation-free
3503
     */
3504 162
    public function keyExists($key): bool
3505
    {
3506 162
        return \array_key_exists($key, $this->array);
3507
    }
3508
3509
    /**
3510
     * Get all keys from the current array.
3511
     *
3512
     * @param bool       $recursive     [optional] <p>
3513
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3514
     *                                  </p>
3515
     * @param mixed|null $search_values [optional] <p>
3516
     *                                  If specified, then only keys containing these values are returned.
3517
     *                                  </p>
3518
     * @param bool       $strict        [optional] <p>
3519
     *                                  Determines if strict comparison (===) should be used during the search.
3520
     *                                  </p>
3521
     *
3522
     * @return static
3523
     *                <p>(Immutable) An array of all the keys in input.</p>
3524
     *
3525
     * @psalm-return static<array-key,TKey>
3526
     * @psalm-mutation-free
3527
     */
3528 29
    public function keys(
3529
        bool $recursive = false,
3530
        $search_values = null,
3531
        bool $strict = true
3532
    ): self {
3533
3534
        // recursive
3535
3536 29
        if ($recursive === true) {
3537 4
            $array = $this->array_keys_recursive(
3538 4
                null,
3539 4
                $search_values,
3540 4
                $strict
3541
            );
3542
3543 4
            return static::create(
3544 4
                $array,
3545 4
                $this->iteratorClass,
3546 4
                false
3547
            );
3548
        }
3549
3550
        // non recursive
3551
3552 28
        if ($search_values === null) {
3553
            $arrayFunction = function (): \Generator {
3554 28
                foreach ($this->getGenerator() as $key => $value) {
3555 26
                    yield $key;
3556
                }
3557 28
            };
3558
        } else {
3559
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3560 1
                $is_array_tmp = \is_array($search_values);
3561
3562
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3563 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3564 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...
3565
                        (
3566 1
                            $is_array_tmp === false
3567
                            &&
3568 1
                            $strict === true
3569
                            &&
3570 1
                            $search_values === $value
3571
                        )
3572
                        ||
3573
                        (
3574 1
                            $is_array_tmp === false
3575
                            &&
3576 1
                            $strict === false
3577
                            &&
3578 1
                            $search_values == $value
3579
                        )
3580
                        ||
3581
                        (
3582 1
                            $is_array_tmp === true
3583
                            &&
3584 1
                            \in_array($value, $search_values, $strict)
3585
                        )
3586
                    ) {
3587 1
                        yield $key;
3588
                    }
3589
                }
3590 1
            };
3591
        }
3592
3593 28
        return static::create(
3594 28
            $arrayFunction,
3595 28
            $this->iteratorClass,
3596 28
            false
3597
        );
3598
    }
3599
3600
    /**
3601
     * Sort an array by key in reverse order.
3602
     *
3603
     * @param int $sort_flags [optional] <p>
3604
     *                        You may modify the behavior of the sort using the optional
3605
     *                        parameter sort_flags, for details
3606
     *                        see sort.
3607
     *                        </p>
3608
     *
3609
     * @return $this
3610
     *               <p>(Mutable) Return this Arrayy object.</p>
3611
     *
3612
     * @psalm-return static<TKey,T>
3613
     */
3614 4
    public function krsort(int $sort_flags = 0): self
3615
    {
3616 4
        $this->generatorToArray();
3617
3618 4
        \krsort($this->array, $sort_flags);
3619
3620 4
        return $this;
3621
    }
3622
3623
    /**
3624
     * Sort an array by key in reverse order.
3625
     *
3626
     * @param int $sort_flags [optional] <p>
3627
     *                        You may modify the behavior of the sort using the optional
3628
     *                        parameter sort_flags, for details
3629
     *                        see sort.
3630
     *                        </p>
3631
     *
3632
     * @return $this
3633
     *               <p>(Immutable) Return this Arrayy object.</p>
3634
     *
3635
     * @psalm-return static<TKey,T>
3636
     * @psalm-mutation-free
3637
     */
3638 4
    public function krsortImmutable(int $sort_flags = 0): self
3639
    {
3640 4
        $that = clone $this;
3641
3642
        /**
3643
         * @psalm-suppress ImpureMethodCall - object is already cloned
3644
         */
3645 4
        $that->krsort($sort_flags);
3646
3647 4
        return $that;
3648
    }
3649
3650
    /**
3651
     * Get the last value from the current array.
3652
     *
3653
     * @return mixed|null
3654
     *                    <p>Return null if there wasn't a element.</p>
3655
     * @psalm-mutation-free
3656
     */
3657 17
    public function last()
3658
    {
3659 17
        $key_last = $this->lastKey();
3660 17
        if ($key_last === null) {
3661 2
            return null;
3662
        }
3663
3664 15
        return $this->get($key_last);
3665
    }
3666
3667
    /**
3668
     * Get the last key from the current array.
3669
     *
3670
     * @return mixed|null
3671
     *                    <p>Return null if there wasn't a element.</p>
3672
     * @psalm-mutation-free
3673
     */
3674 21
    public function lastKey()
3675
    {
3676 21
        $this->generatorToArray();
3677
3678 21
        return \array_key_last($this->array);
3679
    }
3680
3681
    /**
3682
     * Get the last value(s) from the current array.
3683
     *
3684
     * @param int|null $number
3685
     *
3686
     * @return static
3687
     *                <p>(Immutable)</p>
3688
     *
3689
     * @psalm-return static<TKey,T>
3690
     * @psalm-mutation-free
3691
     */
3692 13
    public function lastsImmutable(int $number = null): self
3693
    {
3694 13
        if ($this->isEmpty()) {
3695 1
            return static::create(
3696 1
                [],
3697 1
                $this->iteratorClass,
3698 1
                false
3699
            );
3700
        }
3701
3702 12
        if ($number === null) {
3703 8
            $poppedValue = $this->last();
3704
3705 8
            if ($poppedValue === null) {
3706 1
                $poppedValue = [$poppedValue];
3707
            } else {
3708 7
                $poppedValue = (array) $poppedValue;
3709
            }
3710
3711 8
            $arrayy = static::create(
3712 8
                $poppedValue,
3713 8
                $this->iteratorClass,
3714 8
                false
3715
            );
3716
        } else {
3717 4
            $arrayy = $this->rest(-$number);
3718
        }
3719
3720 12
        return $arrayy;
3721
    }
3722
3723
    /**
3724
     * Get the last value(s) from the current array.
3725
     *
3726
     * @param int|null $number
3727
     *
3728
     * @return $this
3729
     *               <p>(Mutable)</p>
3730
     *
3731
     * @psalm-return static<TKey,T>
3732
     */
3733 13
    public function lastsMutable(int $number = null): self
3734
    {
3735 13
        if ($this->isEmpty()) {
3736 1
            return $this;
3737
        }
3738
3739 12
        if ($number === null) {
3740 8
            $poppedValue = $this->last();
3741
3742 8
            if ($poppedValue === null) {
3743 1
                $poppedValue = [$poppedValue];
3744
            } else {
3745 7
                $poppedValue = (array) $poppedValue;
3746
            }
3747
3748 8
            $this->array = static::create(
3749 8
                $poppedValue,
3750 8
                $this->iteratorClass,
3751 8
                false
3752 8
            )->toArray();
3753
        } else {
3754 4
            $this->array = $this->rest(-$number)->toArray();
3755
        }
3756
3757 12
        $this->generator = null;
3758
3759 12
        return $this;
3760
    }
3761
3762
    /**
3763
     * Count the values from the current array.
3764
     *
3765
     * alias: for "Arrayy->count()"
3766
     *
3767
     * @param int $mode
3768
     *
3769
     * @return int
3770
     *
3771
     * @see Arrayy::count()
3772
     */
3773 20
    public function length(int $mode = \COUNT_NORMAL): int
3774
    {
3775 20
        return $this->count($mode);
3776
    }
3777
3778
    /**
3779
     * Apply the given function to the every element of the array,
3780
     * collecting the results.
3781
     *
3782
     * @param callable $callable
3783
     * @param bool     $useKeyAsSecondParameter
3784
     * @param mixed    ...$arguments
3785
     *
3786
     * @return static
3787
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3788
     *
3789
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3790
     * @psalm-return static<TKey,T>
3791
     * @psalm-mutation-free
3792
     */
3793 5
    public function map(
3794
        callable $callable,
3795
        bool $useKeyAsSecondParameter = false,
3796
        ...$arguments
3797
    ) {
3798
        /**
3799
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3800
         */
3801 5
        $useArguments = \func_num_args() > 2;
3802
3803 5
        return static::create(
3804
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3805 5
                foreach ($this->getGenerator() as $key => $value) {
3806 4
                    if ($useArguments) {
3807 3
                        if ($useKeyAsSecondParameter) {
3808
                            yield $key => $callable($value, $key, ...$arguments);
3809
                        } else {
3810 3
                            yield $key => $callable($value, ...$arguments);
3811
                        }
3812
                    } else {
3813
                        /** @noinspection NestedPositiveIfStatementsInspection */
3814 4
                        if ($useKeyAsSecondParameter) {
3815
                            yield $key => $callable($value, $key);
3816
                        } else {
3817 4
                            yield $key => $callable($value);
3818
                        }
3819
                    }
3820
                }
3821 5
            },
3822 5
            $this->iteratorClass,
3823 5
            false
3824
        );
3825
    }
3826
3827
    /**
3828
     * Check if all items in current array match a truth test.
3829
     *
3830
     * @param \Closure $closure
3831
     *
3832
     * @return bool
3833
     */
3834 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...
3835
    {
3836 15
        if ($this->count() === 0) {
3837 2
            return false;
3838
        }
3839
3840 13
        foreach ($this->getGenerator() as $key => $value) {
3841 13
            $value = $closure($value, $key);
3842
3843 13
            if ($value === false) {
3844 13
                return false;
3845
            }
3846
        }
3847
3848 7
        return true;
3849
    }
3850
3851
    /**
3852
     * Check if any item in the current array matches a truth test.
3853
     *
3854
     * @param \Closure $closure
3855
     *
3856
     * @return bool
3857
     */
3858 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...
3859
    {
3860 14
        if ($this->count() === 0) {
3861 2
            return false;
3862
        }
3863
3864 12
        foreach ($this->getGenerator() as $key => $value) {
3865 12
            $value = $closure($value, $key);
3866
3867 12
            if ($value === true) {
3868 12
                return true;
3869
            }
3870
        }
3871
3872 4
        return false;
3873
    }
3874
3875
    /**
3876
     * Get the max value from an array.
3877
     *
3878
     * @return false|mixed
3879
     *                     <p>Will return false if there are no values.</p>
3880
     */
3881 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...
3882
    {
3883 10
        if ($this->count() === 0) {
3884 1
            return false;
3885
        }
3886
3887 9
        $max = false;
3888
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3889 9
        foreach ($this->getGeneratorByReference() as &$value) {
3890
            if (
3891 9
                $max === false
3892
                ||
3893 9
                $value > $max
3894
            ) {
3895 9
                $max = $value;
3896
            }
3897
        }
3898
3899 9
        return $max;
3900
    }
3901
3902
    /**
3903
     * Merge the new $array into the current array.
3904
     *
3905
     * - keep key,value from the current array, also if the index is in the new $array
3906
     *
3907
     * @param array $array
3908
     * @param bool  $recursive
3909
     *
3910
     * @return static
3911
     *                <p>(Immutable)</p>
3912
     *
3913
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3914
     * @psalm-return static<int|TKey,T>
3915
     * @psalm-mutation-free
3916
     */
3917 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...
3918
    {
3919 32
        if ($recursive === true) {
3920 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
3921 9
            $result = \array_replace_recursive($this->toArray(), $array);
3922
        } else {
3923 23
            $result = \array_replace($this->toArray(), $array);
3924
        }
3925
3926 32
        return static::create(
3927 32
            $result,
3928 32
            $this->iteratorClass,
3929 32
            false
3930
        );
3931
    }
3932
3933
    /**
3934
     * Merge the new $array into the current array.
3935
     *
3936
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3937
     * - create new indexes
3938
     *
3939
     * @param array $array
3940
     * @param bool  $recursive
3941
     *
3942
     * @return static
3943
     *                <p>(Immutable)</p>
3944
     *
3945
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3946
     * @psalm-return static<TKey,T>
3947
     * @psalm-mutation-free
3948
     */
3949 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...
3950
    {
3951 19
        if ($recursive === true) {
3952 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
3953 5
            $result = \array_merge_recursive($this->toArray(), $array);
3954
        } else {
3955 14
            $result = \array_merge($this->toArray(), $array);
3956
        }
3957
3958 19
        return static::create(
3959 19
            $result,
3960 19
            $this->iteratorClass,
3961 19
            false
3962
        );
3963
    }
3964
3965
    /**
3966
     * Merge the the current array into the $array.
3967
     *
3968
     * - use key,value from the new $array, also if the index is in the current array
3969
     *
3970
     * @param array $array
3971
     * @param bool  $recursive
3972
     *
3973
     * @return static
3974
     *                <p>(Immutable)</p>
3975
     *
3976
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3977
     * @psalm-return static<TKey,T>
3978
     * @psalm-mutation-free
3979
     */
3980 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...
3981
    {
3982 16
        if ($recursive === true) {
3983 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
3984 4
            $result = \array_replace_recursive($array, $this->toArray());
3985
        } else {
3986 12
            $result = \array_replace($array, $this->toArray());
3987
        }
3988
3989 16
        return static::create(
3990 16
            $result,
3991 16
            $this->iteratorClass,
3992 16
            false
3993
        );
3994
    }
3995
3996
    /**
3997
     * Merge the current array into the new $array.
3998
     *
3999
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
4000
     * - create new indexes
4001
     *
4002
     * @param array $array
4003
     * @param bool  $recursive
4004
     *
4005
     * @return static
4006
     *                <p>(Immutable)</p>
4007
     *
4008
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4009
     * @psalm-return static<TKey,T>
4010
     * @psalm-mutation-free
4011
     */
4012 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...
4013
    {
4014 20
        if ($recursive === true) {
4015 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
4016 7
            $result = \array_merge_recursive($array, $this->toArray());
4017
        } else {
4018 13
            $result = \array_merge($array, $this->toArray());
4019
        }
4020
4021 20
        return static::create(
4022 20
            $result,
4023 20
            $this->iteratorClass,
4024 20
            false
4025
        );
4026
    }
4027
4028
    /**
4029
     * @return ArrayyMeta|static
4030
     */
4031 16
    public static function meta()
4032
    {
4033 16
        return (new ArrayyMeta())->getMetaObject(static::class);
4034
    }
4035
4036
    /**
4037
     * Get the min value from an array.
4038
     *
4039
     * @return false|mixed
4040
     *                     <p>Will return false if there are no values.</p>
4041
     */
4042 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...
4043
    {
4044 10
        if ($this->count() === 0) {
4045 1
            return false;
4046
        }
4047
4048 9
        $min = false;
4049
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4050 9
        foreach ($this->getGeneratorByReference() as &$value) {
4051
            if (
4052 9
                $min === false
4053
                ||
4054 9
                $value < $min
4055
            ) {
4056 9
                $min = $value;
4057
            }
4058
        }
4059
4060 9
        return $min;
4061
    }
4062
4063
    /**
4064
     * Get the most used value from the array.
4065
     *
4066
     * @return mixed|null
4067
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4068
     * @psalm-mutation-free
4069
     */
4070 3
    public function mostUsedValue()
4071
    {
4072 3
        return $this->countValues()->arsortImmutable()->firstKey();
4073
    }
4074
4075
    /**
4076
     * Get the most used value from the array.
4077
     *
4078
     * @param int|null $number <p>How many values you will take?</p>
4079
     *
4080
     * @return static
4081
     *                <p>(Immutable)</p>
4082
     *
4083
     * @psalm-return static<TKey,T>
4084
     * @psalm-mutation-free
4085
     */
4086 3
    public function mostUsedValues(int $number = null): self
4087
    {
4088 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4089
    }
4090
4091
    /**
4092
     * Move an array element to a new index.
4093
     *
4094
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
4095
     *
4096
     * @param int|string $from
4097
     * @param int        $to
4098
     *
4099
     * @return static
4100
     *                <p>(Immutable)</p>
4101
     *
4102
     * @psalm-return static<TKey,T>
4103
     * @psalm-mutation-free
4104
     */
4105 1
    public function moveElement($from, $to): self
4106
    {
4107 1
        $array = $this->toArray();
4108
4109 1
        if ((int) $from === $from) {
4110 1
            $tmp = \array_splice($array, $from, 1);
4111 1
            \array_splice($array, (int) $to, 0, $tmp);
4112 1
            $output = $array;
4113 1
        } elseif ((string) $from === $from) {
4114 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4115 1
            $itemToMove = $array[$from];
4116 1
            if ($indexToMove !== false) {
4117 1
                \array_splice($array, $indexToMove, 1);
4118
            }
4119 1
            $i = 0;
4120 1
            $output = [];
4121 1
            foreach ($array as $key => $item) {
4122 1
                if ($i === $to) {
4123 1
                    $output[$from] = $itemToMove;
4124
                }
4125 1
                $output[$key] = $item;
4126 1
                ++$i;
4127
            }
4128
        } else {
4129
            $output = [];
4130
        }
4131
4132 1
        return static::create(
4133 1
            $output,
4134 1
            $this->iteratorClass,
4135 1
            false
4136
        );
4137
    }
4138
4139
    /**
4140
     * Move an array element to the first place.
4141
     *
4142
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4143
     *       loss the keys of an indexed array.
4144
     *
4145
     * @param int|string $key
4146
     *
4147
     * @return static
4148
     *                <p>(Immutable)</p>
4149
     *
4150
     * @psalm-return static<TKey,T>
4151
     * @psalm-mutation-free
4152
     */
4153 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...
4154
    {
4155 1
        $array = $this->toArray();
4156
4157 1
        if ($this->offsetExists($key)) {
4158 1
            $tmpValue = $this->get($key);
4159 1
            unset($array[$key]);
4160 1
            $array = [$key => $tmpValue] + $array;
4161
        }
4162
4163 1
        return static::create(
4164 1
            $array,
4165 1
            $this->iteratorClass,
4166 1
            false
4167
        );
4168
    }
4169
4170
    /**
4171
     * Move an array element to the last place.
4172
     *
4173
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4174
     *       loss the keys of an indexed array.
4175
     *
4176
     * @param int|string $key
4177
     *
4178
     * @return static
4179
     *                <p>(Immutable)</p>
4180
     *
4181
     * @psalm-return static<TKey,T>
4182
     * @psalm-mutation-free
4183
     */
4184 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...
4185
    {
4186 1
        $array = $this->toArray();
4187
4188 1
        if ($this->offsetExists($key)) {
4189 1
            $tmpValue = $this->get($key);
4190 1
            unset($array[$key]);
4191 1
            $array += [$key => $tmpValue];
4192
        }
4193
4194 1
        return static::create(
4195 1
            $array,
4196 1
            $this->iteratorClass,
4197 1
            false
4198
        );
4199
    }
4200
4201
    /**
4202
     * Moves the internal iterator position to the next element and returns this element.
4203
     *
4204
     * @return false|mixed
4205
     *                     <p>(Mutable) Will return false if there are no values.</p>
4206
     */
4207
    public function next()
4208
    {
4209
        return \next($this->array);
4210
    }
4211
4212
    /**
4213
     * Get the next nth keys and values from the array.
4214
     *
4215
     * @param int $step
4216
     * @param int $offset
4217
     *
4218
     * @return static
4219
     *                <p>(Immutable)</p>
4220
     *
4221
     * @psalm-return static<TKey,T>
4222
     * @psalm-mutation-free
4223
     */
4224 1
    public function nth(int $step, int $offset = 0): self
4225
    {
4226
        $arrayFunction = function () use ($step, $offset): \Generator {
4227 1
            $position = 0;
4228 1
            foreach ($this->getGenerator() as $key => $value) {
4229 1
                if ($position++ % $step !== $offset) {
4230 1
                    continue;
4231
                }
4232
4233 1
                yield $key => $value;
4234
            }
4235 1
        };
4236
4237 1
        return static::create(
4238 1
            $arrayFunction,
4239 1
            $this->iteratorClass,
4240 1
            false
4241
        );
4242
    }
4243
4244
    /**
4245
     * Get a subset of the items from the given array.
4246
     *
4247
     * @param int[]|string[] $keys
4248
     *
4249
     * @return static
4250
     *                <p>(Immutable)</p>
4251
     *
4252
     * @psalm-param array-key[] $keys
4253
     * @psalm-return static<TKey,T>
4254
     * @psalm-mutation-free
4255
     */
4256 1 View Code Duplication
    public function only(array $keys): 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...
4257
    {
4258 1
        $keys = \array_flip($keys);
4259
4260
        $generator = function () use ($keys): \Generator {
4261 1
            foreach ($this->getGenerator() as $key => $value) {
4262 1
                if (isset($keys[$key])) {
4263 1
                    yield $key => $value;
4264
                }
4265
            }
4266 1
        };
4267
4268 1
        return static::create(
4269 1
            $generator,
4270 1
            $this->iteratorClass,
4271 1
            false
4272
        );
4273
    }
4274
4275
    /**
4276
     * Pad array to the specified size with a given value.
4277
     *
4278
     * @param int   $size  <p>Size of the result array.</p>
4279
     * @param mixed $value <p>Empty value by default.</p>
4280
     *
4281
     * @return static
4282
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4283
     *
4284
     * @psalm-return static<TKey,T>
4285
     * @psalm-mutation-free
4286
     */
4287 5
    public function pad(int $size, $value): self
4288
    {
4289 5
        return static::create(
4290 5
            \array_pad($this->toArray(), $size, $value),
4291 5
            $this->iteratorClass,
4292 5
            false
4293
        );
4294
    }
4295
4296
    /**
4297
     * Partitions this array in two array according to a predicate.
4298
     * Keys are preserved in the resulting array.
4299
     *
4300
     * @param \Closure $closure
4301
     *                          <p>The predicate on which to partition.</p>
4302
     *
4303
     * @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...
4304
     *                    <p>An array with two elements. The first element contains the array
4305
     *                    of elements where the predicate returned TRUE, the second element
4306
     *                    contains the array of elements where the predicate returned FALSE.</p>
4307
     *
4308
     * @psalm-return array<int, static<TKey,T>>
4309
     */
4310 1
    public function partition(\Closure $closure): array
4311
    {
4312
        // init
4313 1
        $matches = [];
4314 1
        $noMatches = [];
4315
4316 1
        foreach ($this->getGenerator() as $key => $value) {
4317 1
            if ($closure($value, $key)) {
4318 1
                $matches[$key] = $value;
4319
            } else {
4320 1
                $noMatches[$key] = $value;
4321
            }
4322
        }
4323
4324 1
        return [self::create($matches), self::create($noMatches)];
4325
    }
4326
4327
    /**
4328
     * Pop a specified value off the end of the current array.
4329
     *
4330
     * @return mixed|null
4331
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4332
     */
4333 5
    public function pop()
4334
    {
4335 5
        $this->generatorToArray();
4336
4337 5
        return \array_pop($this->array);
4338
    }
4339
4340
    /**
4341
     * Prepend a (key) + value to the current array.
4342
     *
4343
     * EXAMPLE: <code>
4344
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4345
     * </code>
4346
     *
4347
     * @param mixed $value
4348
     * @param mixed $key
4349
     *
4350
     * @return $this
4351
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4352
     *
4353
     * @psalm-return static<TKey,T>
4354
     */
4355 11
    public function prepend($value, $key = null)
4356
    {
4357 11
        $this->generatorToArray();
4358
4359 11
        if ($this->properties !== []) {
4360 3
            $this->checkType($key, $value);
4361
        }
4362
4363 9
        if ($key === null) {
4364 8
            \array_unshift($this->array, $value);
4365
        } else {
4366 2
            $this->array = [$key => $value] + $this->array;
4367
        }
4368
4369 9
        return $this;
4370
    }
4371
4372
    /**
4373
     * Prepend a (key) + value to the current array.
4374
     *
4375
     * EXAMPLE: <code>
4376
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4377
     * </code>
4378
     *
4379
     * @param mixed $value
4380
     * @param mixed $key
4381
     *
4382
     * @return $this
4383
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4384
     *
4385
     * @psalm-return static<TKey,T>
4386
     * @psalm-mutation-free
4387
     */
4388 1 View Code Duplication
    public function prependImmutable($value, $key = null)
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...
4389
    {
4390
        $generator = function () use ($key, $value): \Generator {
4391 1
            if ($this->properties !== []) {
4392
                $this->checkType($key, $value);
4393
            }
4394
4395 1
            if ($key !== null) {
4396
                yield $key => $value;
4397
            } else {
4398 1
                yield $value;
4399
            }
4400
4401
            /** @noinspection YieldFromCanBeUsedInspection - FP */
4402 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
4403 1
                yield $keyOld => $itemOld;
4404
            }
4405 1
        };
4406
4407 1
        return static::create(
4408 1
            $generator,
4409 1
            $this->iteratorClass,
4410 1
            false
4411
        );
4412
    }
4413
4414
    /**
4415
     * Add a suffix to each key.
4416
     *
4417
     * @param mixed $suffix
4418
     *
4419
     * @return static
4420
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4421
     *
4422
     * @psalm-return static<TKey,T>
4423
     * @psalm-mutation-free
4424
     */
4425 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...
4426
    {
4427
        // init
4428 10
        $result = [];
4429
4430 10
        foreach ($this->getGenerator() as $key => $item) {
4431 9
            if ($item instanceof self) {
4432
                $result[$key] = $item->prependToEachKey($suffix);
4433 9
            } elseif (\is_array($item) === true) {
4434
                $result[$key] = self::create(
4435
                    $item,
4436
                    $this->iteratorClass,
4437
                    false
4438
                )->prependToEachKey($suffix)
4439
                    ->toArray();
4440
            } else {
4441 9
                $result[$key . $suffix] = $item;
4442
            }
4443
        }
4444
4445 10
        return self::create(
4446 10
            $result,
4447 10
            $this->iteratorClass,
4448 10
            false
4449
        );
4450
    }
4451
4452
    /**
4453
     * Add a suffix to each value.
4454
     *
4455
     * @param mixed $suffix
4456
     *
4457
     * @return static
4458
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4459
     *
4460
     * @psalm-return static<TKey,T>
4461
     * @psalm-mutation-free
4462
     */
4463 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...
4464
    {
4465
        // init
4466 10
        $result = [];
4467
4468 10
        foreach ($this->getGenerator() as $key => $item) {
4469 9
            if ($item instanceof self) {
4470
                $result[$key] = $item->prependToEachValue($suffix);
4471 9
            } elseif (\is_array($item) === true) {
4472
                $result[$key] = self::create(
4473
                    $item,
4474
                    $this->iteratorClass,
4475
                    false
4476
                )->prependToEachValue($suffix)
4477
                    ->toArray();
4478 9
            } elseif (\is_object($item) === true) {
4479 1
                $result[$key] = $item;
4480
            } else {
4481 9
                $result[$key] = $item . $suffix;
4482
            }
4483
        }
4484
4485 10
        return self::create(
4486 10
            $result,
4487 10
            $this->iteratorClass,
4488 10
            false
4489
        );
4490
    }
4491
4492
    /**
4493
     * Return the value of a given key and
4494
     * delete the key.
4495
     *
4496
     * @param int|int[]|string|string[]|null $keyOrKeys
4497
     * @param mixed                          $fallback
4498
     *
4499
     * @return mixed
4500
     */
4501 5
    public function pull($keyOrKeys = null, $fallback = null)
4502
    {
4503 5
        if ($keyOrKeys === null) {
4504 1
            $array = $this->toArray();
4505 1
            $this->clear();
4506
4507 1
            return $array;
4508
        }
4509
4510 4
        if (\is_array($keyOrKeys) === true) {
4511 1
            $valueOrValues = [];
4512 1
            foreach ($keyOrKeys as $key) {
4513 1
                $valueOrValues[] = $this->get($key, $fallback);
4514 1
                $this->offsetUnset($key);
4515
            }
4516
        } else {
4517 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4518 4
            $this->offsetUnset($keyOrKeys);
4519
        }
4520
4521 4
        return $valueOrValues;
4522
    }
4523
4524
    /**
4525
     * Push one or more values onto the end of array at once.
4526
     *
4527
     * @param array ...$args
4528
     *
4529
     * @return $this
4530
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4531
     *
4532
     * @noinspection ReturnTypeCanBeDeclaredInspection
4533
     *
4534
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4535
     * @psalm-return static<TKey,T>
4536
     */
4537 7
    public function push(...$args)
4538
    {
4539 7
        $this->generatorToArray();
4540
4541
        if (
4542 7
            $this->checkPropertyTypes
4543
            &&
4544 7
            $this->properties !== []
4545
        ) {
4546 1
            foreach ($args as $key => $value) {
4547 1
                $this->checkType($key, $value);
4548
            }
4549
        }
4550
4551 7
        \array_push($this->array, ...$args);
4552
4553 7
        return $this;
4554
    }
4555
4556
    /**
4557
     * Get a random value from the current array.
4558
     *
4559
     * @param int|null $number <p>How many values you will take?</p>
4560
     *
4561
     * @return static
4562
     *                <p>(Immutable)</p>
4563
     *
4564
     * @psalm-return static<int|array-key,T>
4565
     */
4566 19
    public function randomImmutable(int $number = null): self
4567
    {
4568 19
        $this->generatorToArray();
4569
4570 19
        if ($this->count() === 0) {
4571 1
            return static::create(
4572 1
                [],
4573 1
                $this->iteratorClass,
4574 1
                false
4575
            );
4576
        }
4577
4578 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...
4579
            /** @noinspection NonSecureArrayRandUsageInspection */
4580 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4581
4582 13
            return static::create(
4583 13
                $arrayRandValue,
4584 13
                $this->iteratorClass,
4585 13
                false
4586
            );
4587
        }
4588
4589 6
        $arrayTmp = $this->array;
4590
        /** @noinspection NonSecureShuffleUsageInspection */
4591 6
        \shuffle($arrayTmp);
4592
4593 6
        return static::create(
4594 6
            $arrayTmp,
4595 6
            $this->iteratorClass,
4596 6
            false
4597 6
        )->firstsImmutable($number);
4598
    }
4599
4600
    /**
4601
     * Pick a random key/index from the keys of this array.
4602
     *
4603
     * @throws \RangeException If array is empty
4604
     *
4605
     * @return mixed
4606
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4607
     */
4608 4
    public function randomKey()
4609
    {
4610 4
        $result = $this->randomKeys(1);
4611
4612 4
        if (!isset($result[0])) {
4613
            $result[0] = null;
4614
        }
4615
4616 4
        return $result[0];
4617
    }
4618
4619
    /**
4620
     * Pick a given number of random keys/indexes out of this array.
4621
     *
4622
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4623
     *
4624
     * @throws \RangeException If array is empty
4625
     *
4626
     * @return static
4627
     *                <p>(Immutable)</p>
4628
     *
4629
     * @psalm-return static<TKey,T>
4630
     */
4631 13
    public function randomKeys(int $number): self
4632
    {
4633 13
        $this->generatorToArray();
4634
4635 13
        $count = $this->count();
4636
4637
        if (
4638 13
            $number === 0
4639
            ||
4640 13
            $number > $count
4641
        ) {
4642 2
            throw new \RangeException(
4643 2
                \sprintf(
4644 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4645 2
                    $number,
4646 2
                    $count
4647
                )
4648
            );
4649
        }
4650
4651 11
        $result = (array) \array_rand($this->array, $number);
4652
4653 11
        return static::create(
4654 11
            $result,
4655 11
            $this->iteratorClass,
4656 11
            false
4657
        );
4658
    }
4659
4660
    /**
4661
     * Get a random value from the current array.
4662
     *
4663
     * @param int|null $number <p>How many values you will take?</p>
4664
     *
4665
     * @return $this
4666
     *               <p>(Mutable) Return this Arrayy object.</p>
4667
     *
4668
     * @psalm-return static<TKey,T>
4669
     */
4670 17
    public function randomMutable(int $number = null): self
4671
    {
4672 17
        $this->generatorToArray();
4673
4674 17
        if ($this->count() === 0) {
4675
            return static::create(
4676
                [],
4677
                $this->iteratorClass,
4678
                false
4679
            );
4680
        }
4681
4682 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...
4683
            /** @noinspection NonSecureArrayRandUsageInspection */
4684 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4685 7
            $this->array = $arrayRandValue;
4686
4687 7
            return $this;
4688
        }
4689
4690
        /** @noinspection NonSecureShuffleUsageInspection */
4691 11
        \shuffle($this->array);
4692
4693 11
        return $this->firstsMutable($number);
4694
    }
4695
4696
    /**
4697
     * Pick a random value from the values of this array.
4698
     *
4699
     * @return mixed
4700
     *               <p>Get a random value or null if there wasn't a value.</p>
4701
     */
4702 4
    public function randomValue()
4703
    {
4704 4
        $result = $this->randomImmutable();
4705
4706 4
        if (!isset($result[0])) {
4707
            $result[0] = null;
4708
        }
4709
4710 4
        return $result[0];
4711
    }
4712
4713
    /**
4714
     * Pick a given number of random values out of this array.
4715
     *
4716
     * @param int $number
4717
     *
4718
     * @return static
4719
     *                <p>(Mutable)</p>
4720
     *
4721
     * @psalm-return static<TKey,T>
4722
     */
4723 7
    public function randomValues(int $number): self
4724
    {
4725 7
        return $this->randomMutable($number);
4726
    }
4727
4728
    /**
4729
     * Get a random value from an array, with the ability to skew the results.
4730
     *
4731
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4732
     *
4733
     * @param array    $array
4734
     * @param int|null $number <p>How many values you will take?</p>
4735
     *
4736
     * @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...
4737
     *                           <p>(Immutable)</p>
4738
     *
4739
     * @psalm-param  array<mixed,mixed> $array
4740
     * @psalm-return static<int|array-key,T>
4741
     */
4742 9
    public function randomWeighted(array $array, int $number = null): self
4743
    {
4744
        // init
4745 9
        $options = [];
4746
4747 9
        foreach ($array as $option => $weight) {
4748 9
            if ($this->searchIndex($option) !== false) {
4749 9
                for ($i = 0; $i < $weight; ++$i) {
4750 1
                    $options[] = $option;
4751
                }
4752
            }
4753
        }
4754
4755 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4756
    }
4757
4758
    /**
4759
     * Reduce the current array via callable e.g. anonymous-function.
4760
     *
4761
     * @param callable $callable
4762
     * @param mixed    $init
4763
     *
4764
     * @return static
4765
     *                <p>(Immutable)</p>
4766
     *
4767
     * @psalm-return static<TKey,T>
4768
     * @psalm-mutation-free
4769
     */
4770 18
    public function reduce($callable, $init = []): self
4771
    {
4772 18
        if ($this->generator) {
4773 1
            $result = $init;
4774
4775 1
            foreach ($this->getGenerator() as $value) {
4776 1
                $result = $callable($result, $value);
4777
            }
4778
4779 1
            return static::create(
4780 1
                $result,
4781 1
                $this->iteratorClass,
4782 1
                false
4783
            );
4784
        }
4785
4786 18
        $result = \array_reduce($this->array, $callable, $init);
4787
4788 18
        if ($result === null) {
4789
            $this->array = [];
4790
        } else {
4791 18
            $this->array = (array) $result;
4792
        }
4793
4794 18
        return static::create(
4795 18
            $this->array,
4796 18
            $this->iteratorClass,
4797 18
            false
4798
        );
4799
    }
4800
4801
    /**
4802
     * @param bool $unique
4803
     *
4804
     * @return static
4805
     *                <p>(Immutable)</p>
4806
     *
4807
     * @psalm-return static<TKey,T>
4808
     * @psalm-mutation-free
4809
     */
4810 14
    public function reduce_dimension(bool $unique = true): self
4811
    {
4812
        // init
4813 14
        $result = [];
4814
4815 14
        foreach ($this->getGenerator() as $val) {
4816 12
            if (\is_array($val) === true) {
4817 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4818
            } else {
4819 12
                $result[] = [$val];
4820
            }
4821
        }
4822
4823 14
        $result = $result === [] ? [] : \array_merge(...$result);
4824
4825 14
        $resultArrayy = new static($result);
4826
4827
        /**
4828
         * @psalm-suppress ImpureMethodCall - object is already re-created
4829
         * @psalm-suppress InvalidReturnStatement - why?
4830
         */
4831 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4832
    }
4833
4834
    /**
4835
     * Create a numerically re-indexed Arrayy object.
4836
     *
4837
     * @return $this
4838
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4839
     *
4840
     * @psalm-return static<TKey,T>
4841
     */
4842 9
    public function reindex(): self
4843
    {
4844 9
        $this->generatorToArray(false);
4845
4846 9
        $this->array = \array_values($this->array);
4847
4848 9
        return $this;
4849
    }
4850
4851
    /**
4852
     * Return all items that fail the truth test.
4853
     *
4854
     * @param \Closure $closure
4855
     *
4856
     * @return static
4857
     *                <p>(Immutable)</p>
4858
     *
4859
     * @psalm-return static<TKey,T>
4860
     * @psalm-mutation-free
4861
     */
4862 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...
4863
    {
4864
        // init
4865 1
        $filtered = [];
4866
4867 1
        foreach ($this->getGenerator() as $key => $value) {
4868 1
            if (!$closure($value, $key)) {
4869 1
                $filtered[$key] = $value;
4870
            }
4871
        }
4872
4873 1
        return static::create(
4874 1
            $filtered,
4875 1
            $this->iteratorClass,
4876 1
            false
4877
        );
4878
    }
4879
4880
    /**
4881
     * Remove a value from the current array (optional using dot-notation).
4882
     *
4883
     * @param mixed $key
4884
     *
4885
     * @return static
4886
     *                <p>(Mutable)</p>
4887
     *
4888
     * @psalm-param  TKey $key
4889
     * @psalm-return static<TKey,T>
4890
     */
4891 21
    public function remove($key)
4892
    {
4893
        // recursive call
4894 21
        if (\is_array($key) === true) {
4895
            foreach ($key as $k) {
4896
                $this->internalRemove($k);
4897
            }
4898
4899
            return static::create(
4900
                $this->toArray(),
4901
                $this->iteratorClass,
4902
                false
4903
            );
4904
        }
4905
4906 21
        $this->internalRemove($key);
4907
4908 21
        return static::create(
4909 21
            $this->toArray(),
4910 21
            $this->iteratorClass,
4911 21
            false
4912
        );
4913
    }
4914
4915
    /**
4916
     * alias: for "Arrayy->removeValue()"
4917
     *
4918
     * @param mixed $element
4919
     *
4920
     * @return static
4921
     *                <p>(Immutable)</p>
4922
     *
4923
     * @psalm-param  T $element
4924
     * @psalm-return static<TKey,T>
4925
     * @psalm-mutation-free
4926
     */
4927 8
    public function removeElement($element)
4928
    {
4929 8
        return $this->removeValue($element);
4930
    }
4931
4932
    /**
4933
     * Remove the first value from the current array.
4934
     *
4935
     * @return static
4936
     *                <p>(Immutable)</p>
4937
     *
4938
     * @psalm-return static<TKey,T>
4939
     * @psalm-mutation-free
4940
     */
4941 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...
4942
    {
4943 7
        $tmpArray = $this->toArray();
4944
4945 7
        \array_shift($tmpArray);
4946
4947 7
        return static::create(
4948 7
            $tmpArray,
4949 7
            $this->iteratorClass,
4950 7
            false
4951
        );
4952
    }
4953
4954
    /**
4955
     * Remove the last value from the current array.
4956
     *
4957
     * @return static
4958
     *                <p>(Immutable)</p>
4959
     *
4960
     * @psalm-return static<TKey,T>
4961
     * @psalm-mutation-free
4962
     */
4963 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...
4964
    {
4965 7
        $tmpArray = $this->toArray();
4966
4967 7
        \array_pop($tmpArray);
4968
4969 7
        return static::create(
4970 7
            $tmpArray,
4971 7
            $this->iteratorClass,
4972 7
            false
4973
        );
4974
    }
4975
4976
    /**
4977
     * Removes a particular value from an array (numeric or associative).
4978
     *
4979
     * @param mixed $value
4980
     *
4981
     * @return static
4982
     *                <p>(Immutable)</p>
4983
     *
4984
     * @psalm-param  T $value
4985
     * @psalm-return static<TKey,T>
4986
     * @psalm-mutation-free
4987
     */
4988 8
    public function removeValue($value): self
4989
    {
4990 8
        $this->generatorToArray();
4991
4992
        // init
4993 8
        $isSequentialArray = $this->isSequential();
4994
4995 8
        foreach ($this->array as $key => $item) {
4996 7
            if ($item === $value) {
4997 7
                unset($this->array[$key]);
4998
            }
4999
        }
5000
5001 8
        if ($isSequentialArray) {
5002 6
            $this->array = \array_values($this->array);
5003
        }
5004
5005 8
        return static::create(
5006 8
            $this->array,
5007 8
            $this->iteratorClass,
5008 8
            false
5009
        );
5010
    }
5011
5012
    /**
5013
     * Generate array of repeated arrays.
5014
     *
5015
     * @param int $times <p>How many times has to be repeated.</p>
5016
     *
5017
     * @return static
5018
     *                <p>(Immutable)</p>
5019
     *
5020
     * @psalm-return static<TKey,T>
5021
     * @psalm-mutation-free
5022
     */
5023 1
    public function repeat($times): self
5024
    {
5025 1
        if ($times === 0) {
5026 1
            return static::create([], $this->iteratorClass);
5027
        }
5028
5029 1
        return static::create(
5030 1
            \array_fill(0, (int) $times, $this->toArray()),
5031 1
            $this->iteratorClass,
5032 1
            false
5033
        );
5034
    }
5035
5036
    /**
5037
     * Replace a key with a new key/value pair.
5038
     *
5039
     * @param mixed $oldKey
5040
     * @param mixed $newKey
5041
     * @param mixed $newValue
5042
     *
5043
     * @return static
5044
     *                <p>(Immutable)</p>
5045
     *
5046
     * @psalm-return static<TKey,T>
5047
     * @psalm-mutation-free
5048
     */
5049 5
    public function replace($oldKey, $newKey, $newValue): self
5050
    {
5051 5
        $that = clone $this;
5052
5053
        /**
5054
         * @psalm-suppress ImpureMethodCall - object is already cloned
5055
         */
5056 5
        return $that->remove($oldKey)
5057 5
            ->set($newKey, $newValue);
5058
    }
5059
5060
    /**
5061
     * Create an array using the current array as values and the other array as keys.
5062
     *
5063
     * @param array $keys <p>An array of keys.</p>
5064
     *
5065
     * @return static
5066
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
5067
     *
5068
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5069
     * @psalm-return static<TKey,T>
5070
     * @psalm-mutation-free
5071
     */
5072 2
    public function replaceAllKeys(array $keys): self
5073
    {
5074 2
        return static::create(
5075 2
            \array_combine($keys, $this->toArray()),
5076 2
            $this->iteratorClass,
5077 2
            false
5078
        );
5079
    }
5080
5081
    /**
5082
     * Create an array using the current array as keys and the other array as values.
5083
     *
5084
     * @param array $array <p>An array o values.</p>
5085
     *
5086
     * @return static
5087
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
5088
     *
5089
     * @psalm-param  array<mixed,T> $array
5090
     * @psalm-return static<TKey,T>
5091
     * @psalm-mutation-free
5092
     */
5093 2
    public function replaceAllValues(array $array): self
5094
    {
5095 2
        return static::create(
5096 2
            \array_combine($this->array, $array),
5097 2
            $this->iteratorClass,
5098 2
            false
5099
        );
5100
    }
5101
5102
    /**
5103
     * Replace the keys in an array with another set.
5104
     *
5105
     * @param array $keys <p>An array of keys matching the array's size</p>
5106
     *
5107
     * @return static
5108
     *                <p>(Immutable)</p>
5109
     *
5110
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5111
     * @psalm-return static<TKey,T>
5112
     * @psalm-mutation-free
5113
     */
5114 1
    public function replaceKeys(array $keys): self
5115
    {
5116 1
        $values = \array_values($this->toArray());
5117 1
        $result = \array_combine($keys, $values);
5118
5119 1
        return static::create(
5120 1
            $result,
5121 1
            $this->iteratorClass,
5122 1
            false
5123
        );
5124
    }
5125
5126
    /**
5127
     * Replace the first matched value in an array.
5128
     *
5129
     * @param mixed $search      <p>The value to replace.</p>
5130
     * @param mixed $replacement <p>The value to replace.</p>
5131
     *
5132
     * @return static
5133
     *                <p>(Immutable)</p>
5134
     *
5135
     * @psalm-return static<TKey,T>
5136
     * @psalm-mutation-free
5137
     */
5138 3 View Code Duplication
    public function replaceOneValue($search, $replacement = ''): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
5139
    {
5140 3
        $array = $this->toArray();
5141 3
        $key = \array_search($search, $array, true);
5142
5143 3
        if ($key !== false) {
5144 3
            $array[$key] = $replacement;
5145
        }
5146
5147 3
        return static::create(
5148 3
            $array,
5149 3
            $this->iteratorClass,
5150 3
            false
5151
        );
5152
    }
5153
5154
    /**
5155
     * Replace values in the current array.
5156
     *
5157
     * @param mixed $search      <p>The value to replace.</p>
5158
     * @param mixed $replacement <p>What to replace it with.</p>
5159
     *
5160
     * @return static
5161
     *                <p>(Immutable)</p>
5162
     *
5163
     * @psalm-return static<TKey,T>
5164
     * @psalm-mutation-free
5165
     */
5166 1
    public function replaceValues($search, $replacement = ''): self
5167
    {
5168
        /**
5169
         * @psalm-suppress MissingClosureReturnType
5170
         * @psalm-suppress MissingClosureParamType
5171
         */
5172 1
        return $this->each(
5173
            static function ($value) use ($search, $replacement) {
5174 1
                return \str_replace($search, $replacement, $value);
5175 1
            }
5176
        );
5177
    }
5178
5179
    /**
5180
     * Get the last elements from index $from until the end of this array.
5181
     *
5182
     * @param int $from
5183
     *
5184
     * @return static
5185
     *                <p>(Immutable)</p>
5186
     *
5187
     * @psalm-return static<TKey,T>
5188
     * @psalm-mutation-free
5189
     */
5190 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...
5191
    {
5192 15
        $tmpArray = $this->toArray();
5193
5194 15
        return static::create(
5195 15
            \array_splice($tmpArray, $from),
5196 15
            $this->iteratorClass,
5197 15
            false
5198
        );
5199
    }
5200
5201
    /**
5202
     * Return the array in the reverse order.
5203
     *
5204
     * @return $this
5205
     *               <p>(Mutable) Return this Arrayy object.</p>
5206
     *
5207
     * @psalm-return static<TKey,T>
5208
     */
5209 9
    public function reverse(): self
5210
    {
5211 9
        $this->generatorToArray();
5212
5213 9
        $this->array = \array_reverse($this->array);
5214
5215 9
        return $this;
5216
    }
5217
5218
    /**
5219
     * Sort an array in reverse order.
5220
     *
5221
     * @param int $sort_flags [optional] <p>
5222
     *                        You may modify the behavior of the sort using the optional
5223
     *                        parameter sort_flags, for details
5224
     *                        see sort.
5225
     *                        </p>
5226
     *
5227
     * @return $this
5228
     *               <p>(Mutable) Return this Arrayy object.</p>
5229
     *
5230
     * @psalm-return static<TKey,T>
5231
     */
5232 4
    public function rsort(int $sort_flags = 0): self
5233
    {
5234 4
        $this->generatorToArray();
5235
5236 4
        \rsort($this->array, $sort_flags);
5237
5238 4
        return $this;
5239
    }
5240
5241
    /**
5242
     * Sort an array in reverse order.
5243
     *
5244
     * @param int $sort_flags [optional] <p>
5245
     *                        You may modify the behavior of the sort using the optional
5246
     *                        parameter sort_flags, for details
5247
     *                        see sort.
5248
     *                        </p>
5249
     *
5250
     * @return $this
5251
     *               <p>(Immutable) Return this Arrayy object.</p>
5252
     *
5253
     * @psalm-return static<TKey,T>
5254
     * @psalm-mutation-free
5255
     */
5256 4
    public function rsortImmutable(int $sort_flags = 0): self
5257
    {
5258 4
        $that = clone $this;
5259
5260
        /**
5261
         * @psalm-suppress ImpureMethodCall - object is already cloned
5262
         */
5263 4
        $that->rsort($sort_flags);
5264
5265 4
        return $that;
5266
    }
5267
5268
    /**
5269
     * Search for the first index of the current array via $value.
5270
     *
5271
     * @param mixed $value
5272
     *
5273
     * @return false|float|int|string
5274
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5275
     * @psalm-mutation-free
5276
     */
5277 21
    public function searchIndex($value)
5278
    {
5279 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5280 20
            if ($value === $valueFromArray) {
5281 20
                return $keyFromArray;
5282
            }
5283
        }
5284
5285 11
        return false;
5286
    }
5287
5288
    /**
5289
     * Search for the value of the current array via $index.
5290
     *
5291
     * @param mixed $index
5292
     *
5293
     * @return static
5294
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5295
     *
5296
     * @psalm-return static<TKey,T>
5297
     * @psalm-mutation-free
5298
     */
5299 9
    public function searchValue($index): self
5300
    {
5301 9
        $this->generatorToArray();
5302
5303
        // init
5304 9
        $return = [];
5305
5306 9
        if ($this->array === []) {
5307
            return static::create(
5308
                [],
5309
                $this->iteratorClass,
5310
                false
5311
            );
5312
        }
5313
5314
        // php cast "bool"-index into "int"-index
5315 9
        if ((bool) $index === $index) {
5316 1
            $index = (int) $index;
5317
        }
5318
5319 9
        if ($this->offsetExists($index)) {
5320 7
            $return = [$this->array[$index]];
5321
        }
5322
5323 9
        return static::create(
5324 9
            $return,
5325 9
            $this->iteratorClass,
5326 9
            false
5327
        );
5328
    }
5329
5330
    /**
5331
     * Set a value for the current array (optional using dot-notation).
5332
     *
5333
     * @param string $key   <p>The key to set.</p>
5334
     * @param mixed  $value <p>Its value.</p>
5335
     *
5336
     * @return $this
5337
     *               <p>(Mutable) Return this Arrayy object.</p>
5338
     *
5339
     * @psalm-param  TKey $key
5340
     * @psalm-param  T $value
5341
     * @psalm-return static<TKey,T>
5342
     */
5343 28
    public function set($key, $value): self
5344
    {
5345 28
        $this->internalSet($key, $value);
5346
5347 27
        return $this;
5348
    }
5349
5350
    /**
5351
     * Get a value from a array and set it if it was not.
5352
     *
5353
     * WARNING: this method only set the value, if the $key is not already set
5354
     *
5355
     * @param mixed $key      <p>The key</p>
5356
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5357
     *
5358
     * @return mixed
5359
     *               <p>(Mutable)</p>
5360
     */
5361 11
    public function setAndGet($key, $fallback = null)
5362
    {
5363 11
        $this->generatorToArray();
5364
5365
        // If the key doesn't exist, set it.
5366 11
        if (!$this->has($key)) {
5367 4
            $this->array = $this->set($key, $fallback)->toArray();
5368
        }
5369
5370 11
        return $this->get($key);
5371
    }
5372
5373
    /**
5374
     * Shifts a specified value off the beginning of array.
5375
     *
5376
     * @return mixed
5377
     *               <p>(Mutable) A shifted element from the current array.</p>
5378
     */
5379 5
    public function shift()
5380
    {
5381 5
        $this->generatorToArray();
5382
5383 5
        return \array_shift($this->array);
5384
    }
5385
5386
    /**
5387
     * Shuffle the current array.
5388
     *
5389
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5390
     * @param array $array  [optional]
5391
     *
5392
     * @return static
5393
     *                <p>(Immutable)</p>
5394
     *
5395
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5396
     * @psalm-return static<TKey,T>
5397
     *
5398
     * @noinspection BadExceptionsProcessingInspection
5399
     * @noinspection RandomApiMigrationInspection
5400
     * @noinspection NonSecureShuffleUsageInspection
5401
     */
5402 2
    public function shuffle(bool $secure = false, array $array = null): self
5403
    {
5404 2
        if ($array === null) {
5405 2
            $array = $this->toArray(false);
5406
        }
5407
5408 2
        if ($secure !== true) {
5409 2
            \shuffle($array);
5410
        } else {
5411 1
            $size = \count($array, \COUNT_NORMAL);
5412 1
            $keys = \array_keys($array);
5413 1
            for ($i = $size - 1; $i > 0; --$i) {
5414
                try {
5415 1
                    $r = \random_int(0, $i);
5416
                } catch (\Exception $e) {
5417
                    $r = \mt_rand(0, $i);
5418
                }
5419 1
                if ($r !== $i) {
5420 1
                    $temp = $array[$keys[$r]];
5421 1
                    $array[$keys[$r]] = $array[$keys[$i]];
5422 1
                    $array[$keys[$i]] = $temp;
5423
                }
5424
            }
5425
        }
5426
5427 2
        foreach ($array as $key => $value) {
5428
            // check if recursive is needed
5429 2
            if (\is_array($value) === true) {
5430 2
                $array[$key] = $this->shuffle($secure, $value);
5431
            }
5432
        }
5433
5434 2
        return static::create(
5435 2
            $array,
5436 2
            $this->iteratorClass,
5437 2
            false
5438
        );
5439
    }
5440
5441
    /**
5442
     * Count the values from the current array.
5443
     *
5444
     * alias: for "Arrayy->count()"
5445
     *
5446
     * @param int $mode
5447
     *
5448
     * @return int
5449
     */
5450 20
    public function size(int $mode = \COUNT_NORMAL): int
5451
    {
5452 20
        return $this->count($mode);
5453
    }
5454
5455
    /**
5456
     * Checks whether array has exactly $size items.
5457
     *
5458
     * @param int $size
5459
     *
5460
     * @return bool
5461
     */
5462 1
    public function sizeIs(int $size): bool
5463
    {
5464
        // init
5465 1
        $itemsTempCount = 0;
5466
5467
        /** @noinspection PhpUnusedLocalVariableInspection */
5468
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
5469 1
        foreach ($this->getGeneratorByReference() as &$value) {
5470 1
            ++$itemsTempCount;
5471 1
            if ($itemsTempCount > $size) {
5472 1
                return false;
5473
            }
5474
        }
5475
5476 1
        return $itemsTempCount === $size;
5477
    }
5478
5479
    /**
5480
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5481
     * smaller than $fromSize.
5482
     *
5483
     * @param int $fromSize
5484
     * @param int $toSize
5485
     *
5486
     * @return bool
5487
     */
5488 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5489
    {
5490 1
        if ($fromSize > $toSize) {
5491 1
            $tmp = $toSize;
5492 1
            $toSize = $fromSize;
5493 1
            $fromSize = $tmp;
5494
        }
5495
5496
        // init
5497 1
        $itemsTempCount = 0;
5498
5499 1
        foreach ($this->getGenerator() as $key => $value) {
5500 1
            ++$itemsTempCount;
5501 1
            if ($itemsTempCount > $toSize) {
5502 1
                return false;
5503
            }
5504
        }
5505
5506 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5507
    }
5508
5509
    /**
5510
     * Checks whether array has more than $size items.
5511
     *
5512
     * @param int $size
5513
     *
5514
     * @return bool
5515
     */
5516 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...
5517
    {
5518
        // init
5519 1
        $itemsTempCount = 0;
5520
5521 1
        foreach ($this->getGenerator() as $key => $value) {
5522 1
            ++$itemsTempCount;
5523 1
            if ($itemsTempCount > $size) {
5524 1
                return true;
5525
            }
5526
        }
5527
5528 1
        return $itemsTempCount > $size;
5529
    }
5530
5531
    /**
5532
     * Checks whether array has less than $size items.
5533
     *
5534
     * @param int $size
5535
     *
5536
     * @return bool
5537
     */
5538 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...
5539
    {
5540
        // init
5541 1
        $itemsTempCount = 0;
5542
5543 1
        foreach ($this->getGenerator() as $key => $value) {
5544 1
            ++$itemsTempCount;
5545 1
            if ($itemsTempCount > $size) {
5546 1
                return false;
5547
            }
5548
        }
5549
5550 1
        return $itemsTempCount < $size;
5551
    }
5552
5553
    /**
5554
     * Counts all elements in an array, or something in an object.
5555
     *
5556
     * <p>
5557
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5558
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5559
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5560
     * implemented and used in PHP.
5561
     * </p>
5562
     *
5563
     * @return int
5564
     *             <p>
5565
     *             The number of elements in var, which is
5566
     *             typically an array, since anything else will have one
5567
     *             element.
5568
     *             </p>
5569
     *             <p>
5570
     *             If var is not an array or an object with
5571
     *             implemented Countable interface,
5572
     *             1 will be returned.
5573
     *             There is one exception, if var is &null;,
5574
     *             0 will be returned.
5575
     *             </p>
5576
     *             <p>
5577
     *             Caution: count may return 0 for a variable that isn't set,
5578
     *             but it may also return 0 for a variable that has been initialized with an
5579
     *             empty array. Use isset to test if a variable is set.
5580
     *             </p>
5581
     */
5582 10
    public function sizeRecursive(): int
5583
    {
5584 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5585
    }
5586
5587
    /**
5588
     * Extract a slice of the array.
5589
     *
5590
     * @param int      $offset       <p>Slice begin index.</p>
5591
     * @param int|null $length       <p>Length of the slice.</p>
5592
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5593
     *
5594
     * @return static
5595
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5596
     *
5597
     * @psalm-return static<TKey,T>
5598
     * @psalm-mutation-free
5599
     */
5600 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5601
    {
5602 5
        return static::create(
5603 5
            \array_slice(
5604 5
                $this->toArray(),
5605 5
                $offset,
5606 5
                $length,
5607 5
                $preserveKeys
5608
            ),
5609 5
            $this->iteratorClass,
5610 5
            false
5611
        );
5612
    }
5613
5614
    /**
5615
     * Sort the current array and optional you can keep the keys.
5616
     *
5617
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5618
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5619
     *                              <strong>SORT_NATURAL</strong></p>
5620
     * @param bool       $keepKeys
5621
     *
5622
     * @return static
5623
     *                <p>(Mutable) Return this Arrayy object.</p>
5624
     *
5625
     * @psalm-return static<TKey,T>
5626
     */
5627 20
    public function sort(
5628
        $direction = \SORT_ASC,
5629
        int $strategy = \SORT_REGULAR,
5630
        bool $keepKeys = false
5631
    ): self {
5632 20
        $this->generatorToArray();
5633
5634 20
        return $this->sorting(
5635 20
            $this->array,
5636 20
            $direction,
5637 20
            $strategy,
5638 20
            $keepKeys
5639
        );
5640
    }
5641
5642
    /**
5643
     * Sort the current array and optional you can keep the keys.
5644
     *
5645
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5646
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5647
     *                              <strong>SORT_NATURAL</strong></p>
5648
     * @param bool       $keepKeys
5649
     *
5650
     * @return static
5651
     *                <p>(Immutable) Return this Arrayy object.</p>
5652
     *
5653
     * @psalm-return static<TKey,T>
5654
     */
5655 12
    public function sortImmutable(
5656
        $direction = \SORT_ASC,
5657
        int $strategy = \SORT_REGULAR,
5658
        bool $keepKeys = false
5659
    ): self {
5660 12
        $that = clone $this;
5661
5662 12
        $that->generatorToArray();
5663
5664 12
        return $that->sorting(
5665 12
            $that->array,
5666 12
            $direction,
5667 12
            $strategy,
5668 12
            $keepKeys
5669
        );
5670
    }
5671
5672
    /**
5673
     * Sort the current array by key.
5674
     *
5675
     * @see          http://php.net/manual/en/function.ksort.php
5676
     * @see          http://php.net/manual/en/function.krsort.php
5677
     *
5678
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5679
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5680
     *                              <strong>SORT_NATURAL</strong></p>
5681
     *
5682
     * @return $this
5683
     *               <p>(Mutable) Return this Arrayy object.</p>
5684
     *
5685
     * @psalm-return static<TKey,T>
5686
     */
5687 18
    public function sortKeys(
5688
        $direction = \SORT_ASC,
5689
        int $strategy = \SORT_REGULAR
5690
    ): self {
5691 18
        $this->generatorToArray();
5692
5693 18
        $this->sorterKeys($this->array, $direction, $strategy);
5694
5695 18
        return $this;
5696
    }
5697
5698
    /**
5699
     * Sort the current array by key.
5700
     *
5701
     * @see          http://php.net/manual/en/function.ksort.php
5702
     * @see          http://php.net/manual/en/function.krsort.php
5703
     *
5704
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5705
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5706
     *                              <strong>SORT_NATURAL</strong></p>
5707
     *
5708
     * @return $this
5709
     *               <p>(Immutable) Return this Arrayy object.</p>
5710
     *
5711
     * @psalm-return static<TKey,T>
5712
     * @psalm-mutation-free
5713
     */
5714 8
    public function sortKeysImmutable(
5715
        $direction = \SORT_ASC,
5716
        int $strategy = \SORT_REGULAR
5717
    ): self {
5718 8
        $that = clone $this;
5719
5720
        /**
5721
         * @psalm-suppress ImpureMethodCall - object is already cloned
5722
         */
5723 8
        $that->sortKeys($direction, $strategy);
5724
5725 8
        return $that;
5726
    }
5727
5728
    /**
5729
     * Sort the current array by value.
5730
     *
5731
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5732
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5733
     *                              <strong>SORT_NATURAL</strong></p>
5734
     *
5735
     * @return static
5736
     *                <p>(Mutable)</p>
5737
     *
5738
     * @psalm-return static<TKey,T>
5739
     */
5740 1
    public function sortValueKeepIndex(
5741
        $direction = \SORT_ASC,
5742
        int $strategy = \SORT_REGULAR
5743
    ): self {
5744 1
        return $this->sort($direction, $strategy, true);
5745
    }
5746
5747
    /**
5748
     * Sort the current array by value.
5749
     *
5750
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5751
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5752
     *                              <strong>SORT_NATURAL</strong></p>
5753
     *
5754
     * @return static
5755
     *                <p>(Mutable)</p>
5756
     *
5757
     * @psalm-return static<TKey,T>
5758
     */
5759 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5760
    {
5761 1
        return $this->sort($direction, $strategy, false);
5762
    }
5763
5764
    /**
5765
     * Sort a array by value, by a closure or by a property.
5766
     *
5767
     * - If the sorter is null, the array is sorted naturally.
5768
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5769
     *
5770
     * @param callable|string|null $sorter
5771
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5772
     *                                        <strong>SORT_DESC</strong></p>
5773
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5774
     *                                        <strong>SORT_NATURAL</strong></p>
5775
     *
5776
     * @return static
5777
     *                <p>(Immutable)</p>
5778
     *
5779
     * @psalm-return static<TKey,T>
5780
     * @psalm-mutation-free
5781
     */
5782 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5783
    {
5784 1
        $array = $this->toArray();
5785 1
        $direction = $this->getDirection($direction);
5786
5787
        // Transform all values into their results.
5788 1
        if ($sorter) {
5789 1
            $arrayy = static::create(
5790 1
                $array,
5791 1
                $this->iteratorClass,
5792 1
                false
5793
            );
5794
5795
            /**
5796
             * @psalm-suppress MissingClosureReturnType
5797
             * @psalm-suppress MissingClosureParamType
5798
             */
5799 1
            $results = $arrayy->each(
5800
                function ($value) use ($sorter) {
5801 1
                    if (\is_callable($sorter) === true) {
5802 1
                        return $sorter($value);
5803
                    }
5804
5805 1
                    return $this->get($sorter);
5806 1
                }
5807
            );
5808
5809 1
            $results = $results->toArray();
5810
        } else {
5811 1
            $results = $array;
5812
        }
5813
5814
        // Sort by the results and replace by original values
5815 1
        \array_multisort($results, $direction, $strategy, $array);
5816
5817 1
        return static::create(
5818 1
            $array,
5819 1
            $this->iteratorClass,
5820 1
            false
5821
        );
5822
    }
5823
5824
    /**
5825
     * @param int      $offset
5826
     * @param int|null $length
5827
     * @param array    $replacement
5828
     *
5829
     * @return static
5830
     *                <p>(Immutable)</p>
5831
     *
5832
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5833
     * @psalm-return static<TKey,T>
5834
     * @psalm-mutation-free
5835
     */
5836 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5837
    {
5838 1
        $tmpArray = $this->toArray();
5839
5840 1
        \array_splice(
5841 1
            $tmpArray,
5842 1
            $offset,
5843 1
            $length ?? $this->count(),
5844 1
            $replacement
5845
        );
5846
5847 1
        return static::create(
5848 1
            $tmpArray,
5849 1
            $this->iteratorClass,
5850 1
            false
5851
        );
5852
    }
5853
5854
    /**
5855
     * Split an array in the given amount of pieces.
5856
     *
5857
     * @param int  $numberOfPieces
5858
     * @param bool $keepKeys
5859
     *
5860
     * @return static
5861
     *                <p>(Immutable)</p>
5862
     *
5863
     * @psalm-return static<TKey,T>
5864
     * @psalm-mutation-free
5865
     */
5866 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5867
    {
5868 1
        $this->generatorToArray();
5869
5870 1
        $count = $this->count();
5871
5872 1
        if ($count === 0) {
5873 1
            $result = [];
5874
        } else {
5875 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5876 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5877
        }
5878
5879 1
        return static::create(
5880 1
            $result,
5881 1
            $this->iteratorClass,
5882 1
            false
5883
        );
5884
    }
5885
5886
    /**
5887
     * Stripe all empty items.
5888
     *
5889
     * @return static
5890
     *                <p>(Immutable)</p>
5891
     *
5892
     * @psalm-return static<TKey,T>
5893
     * @psalm-mutation-free
5894
     */
5895 1
    public function stripEmpty(): self
5896
    {
5897 1
        return $this->filter(
5898
            static function ($item) {
5899 1
                if ($item === null) {
5900 1
                    return false;
5901
                }
5902
5903 1
                return (bool) \trim((string) $item);
5904 1
            }
5905
        );
5906
    }
5907
5908
    /**
5909
     * Swap two values between positions by key.
5910
     *
5911
     * @param int|string $swapA <p>a key in the array</p>
5912
     * @param int|string $swapB <p>a key in the array</p>
5913
     *
5914
     * @return static
5915
     *                <p>(Immutable)</p>
5916
     *
5917
     * @psalm-return static<TKey,T>
5918
     * @psalm-mutation-free
5919
     */
5920 1
    public function swap($swapA, $swapB): self
5921
    {
5922 1
        $array = $this->toArray();
5923
5924 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5925
5926 1
        return static::create(
5927 1
            $array,
5928 1
            $this->iteratorClass,
5929 1
            false
5930
        );
5931
    }
5932
5933
    /**
5934
     * Get the current array from the "Arrayy"-object.
5935
     * alias for "getArray()"
5936
     *
5937
     * @param bool $convertAllArrayyElements <p>
5938
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5939
     *                                       </p>
5940
     * @param bool $preserveKeys             <p>
5941
     *                                       e.g.: A generator maybe return the same key more then once,
5942
     *                                       so maybe you will ignore the keys.
5943
     *                                       </p>
5944
     *
5945
     * @return array
5946
     *
5947
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5948
     * @psalm-mutation-free
5949
     */
5950 947
    public function toArray(
5951
        bool $convertAllArrayyElements = false,
5952
        bool $preserveKeys = true
5953
    ): array {
5954
        // init
5955 947
        $array = [];
5956
5957 947
        if ($convertAllArrayyElements) {
5958 2
            foreach ($this->getGenerator() as $key => $value) {
5959 2
                if ($value instanceof self) {
5960 1
                    $value = $value->toArray(
5961 1
                        $convertAllArrayyElements,
5962 1
                        $preserveKeys
5963
                    );
5964
                }
5965
5966 2
                if ($preserveKeys) {
5967 1
                    $array[$key] = $value;
5968
                } else {
5969 2
                    $array[] = $value;
5970
                }
5971
            }
5972
        } else {
5973 947
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5974
        }
5975
5976 947
        return $array;
5977
    }
5978
5979
    /**
5980
     * Get the current array from the "Arrayy"-object as list.
5981
     *
5982
     * @param bool $convertAllArrayyElements <p>
5983
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5984
     *                                       </p>
5985
     *
5986
     * @return array
5987
     *
5988
     * @psalm-return list<array<TKey,T>>
5989
     * @psalm-mutation-free
5990
     */
5991 1
    public function toList(bool $convertAllArrayyElements = false): array
5992
    {
5993 1
        return $this->toArray(
5994 1
            $convertAllArrayyElements,
5995 1
            false
5996
        );
5997
    }
5998
5999
    /**
6000
     * Convert the current array to JSON.
6001
     *
6002
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6003
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6004
     *
6005
     * @return string
6006
     */
6007 12
    public function toJson(int $options = 0, int $depth = 512): string
6008
    {
6009 12
        $return = \json_encode($this->toArray(), $options, $depth);
6010 12
        if ($return === false) {
6011
            return '';
6012
        }
6013
6014 12
        return $return;
6015
    }
6016
6017
    /**
6018
     * @param string[]|null $items  [optional]
6019
     * @param string[]      $helper [optional]
6020
     *
6021
     * @return static|static[]
6022
     *
6023
     * @psalm-return static<int, static<TKey,T>>
6024
     */
6025 1
    public function toPermutation(array $items = null, array $helper = []): self
6026
    {
6027
        // init
6028 1
        $return = [];
6029
6030 1
        if ($items === null) {
6031 1
            $items = $this->toArray();
6032
        }
6033
6034 1
        if (empty($items)) {
6035 1
            $return[] = $helper;
6036
        } else {
6037 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6038 1
                $new_items = $items;
6039 1
                $new_helper = $helper;
6040 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6041
                /** @noinspection PhpSillyAssignmentInspection */
6042
                /** @var string[] $new_items */
6043 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...
6044 1
                \array_unshift($new_helper, $tmp_helper);
6045
                /** @noinspection SlowArrayOperationsInLoopInspection */
6046 1
                $return = \array_merge(
6047 1
                    $return,
6048 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6049
                );
6050
            }
6051
        }
6052
6053 1
        return static::create(
6054 1
            $return,
6055 1
            $this->iteratorClass,
6056 1
            false
6057
        );
6058
    }
6059
6060
    /**
6061
     * Implodes array to a string with specified separator.
6062
     *
6063
     * @param string $separator [optional] <p>The element's separator.</p>
6064
     *
6065
     * @return string
6066
     *                <p>The string representation of array, separated by ",".</p>
6067
     */
6068 19
    public function toString(string $separator = ','): string
6069
    {
6070 19
        return $this->implode($separator);
6071
    }
6072
6073
    /**
6074
     * Return a duplicate free copy of the current array.
6075
     *
6076
     * @return $this
6077
     *               <p>(Mutable)</p>
6078
     *
6079
     * @psalm-return static<TKey,T>
6080
     */
6081 13
    public function unique(): self
6082
    {
6083
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6084
6085
        /**
6086
         * @psalm-suppress MissingClosureReturnType
6087
         * @psalm-suppress MissingClosureParamType
6088
         */
6089 13
        $this->array = $this->reduce(
6090
            static function ($resultArray, $value) {
6091 12
                if (!\in_array($value, $resultArray, true)) {
6092 12
                    $resultArray[] = $value;
6093
                }
6094
6095 12
                return $resultArray;
6096 13
            },
6097 13
            []
6098 13
        )->toArray();
6099 13
        $this->generator = null;
6100
6101 13
        return $this;
6102
    }
6103
6104
    /**
6105
     * Return a duplicate free copy of the current array. (with the old keys)
6106
     *
6107
     * @return $this
6108
     *               <p>(Mutable)</p>
6109
     *
6110
     * @psalm-return static<TKey,T>
6111
     */
6112 11
    public function uniqueKeepIndex(): self
6113
    {
6114
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6115
6116
        // init
6117 11
        $array = $this->toArray();
6118
6119
        /**
6120
         * @psalm-suppress MissingClosureReturnType
6121
         * @psalm-suppress MissingClosureParamType
6122
         */
6123 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...
6124 11
            \array_keys($array),
6125
            static function ($resultArray, $key) use ($array) {
6126 10
                if (!\in_array($array[$key], $resultArray, true)) {
6127 10
                    $resultArray[$key] = $array[$key];
6128
                }
6129
6130 10
                return $resultArray;
6131 11
            },
6132 11
            []
6133
        );
6134 11
        $this->generator = null;
6135
6136 11
        return $this;
6137
    }
6138
6139
    /**
6140
     * alias: for "Arrayy->unique()"
6141
     *
6142
     * @return static
6143
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6144
     *
6145
     * @see          Arrayy::unique()
6146
     *
6147
     * @psalm-return static<TKey,T>
6148
     */
6149 10
    public function uniqueNewIndex(): self
6150
    {
6151 10
        return $this->unique();
6152
    }
6153
6154
    /**
6155
     * Prepends one or more values to the beginning of array at once.
6156
     *
6157
     * @param array ...$args
6158
     *
6159
     * @return $this
6160
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6161
     *
6162
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
6163
     * @psalm-return static<TKey,T>
6164
     */
6165 4
    public function unshift(...$args): self
6166
    {
6167 4
        $this->generatorToArray();
6168
6169 4
        \array_unshift($this->array, ...$args);
6170
6171 4
        return $this;
6172
    }
6173
6174
    /**
6175
     * Tests whether the given closure return something valid for all elements of this array.
6176
     *
6177
     * @param \Closure $closure the predicate
6178
     *
6179
     * @return bool
6180
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6181
     */
6182 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...
6183
    {
6184 1
        foreach ($this->getGenerator() as $key => $value) {
6185 1
            if (!$closure($value, $key)) {
6186 1
                return false;
6187
            }
6188
        }
6189
6190 1
        return true;
6191
    }
6192
6193
    /**
6194
     * Get all values from a array.
6195
     *
6196
     * @return static
6197
     *                <p>(Immutable)</p>
6198
     *
6199
     * @psalm-return static<TKey,T>
6200
     * @psalm-mutation-free
6201
     */
6202 2
    public function values(): self
6203
    {
6204 2
        return static::create(
6205
            function () {
6206
                /** @noinspection YieldFromCanBeUsedInspection */
6207 2
                foreach ($this->getGenerator() as $value) {
6208 2
                    yield $value;
6209
                }
6210 2
            },
6211 2
            $this->iteratorClass,
6212 2
            false
6213
        );
6214
    }
6215
6216
    /**
6217
     * Apply the given function to every element in the array, discarding the results.
6218
     *
6219
     * @param callable $callable
6220
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6221
     * @param mixed    $userData  [optional] <p>
6222
     *                            If the optional $userData parameter is supplied,
6223
     *                            it will be passed as the third parameter to the $callable.
6224
     *                            </p>
6225
     *
6226
     * @return $this
6227
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6228
     *
6229
     * @psalm-return static<TKey,T>
6230
     */
6231 12
    public function walk($callable, bool $recursive = false, $userData = self::ARRAYY_HELPER_WALK): self
6232
    {
6233 12
        $this->generatorToArray();
6234
6235 12
        if ($this->array !== []) {
6236 10
            if ($recursive === true) {
6237 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6238
                    \array_walk_recursive($this->array, $callable, $userData);
6239
                } else {
6240 5
                    \array_walk_recursive($this->array, $callable);
6241
                }
6242
            } else {
6243
                /** @noinspection NestedPositiveIfStatementsInspection */
6244 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6245
                    \array_walk($this->array, $callable, $userData);
6246
                } else {
6247 5
                    \array_walk($this->array, $callable);
6248
                }
6249
            }
6250
        }
6251
6252 12
        return $this;
6253
    }
6254
6255
    /**
6256
     * Returns a collection of matching items.
6257
     *
6258
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
6259
     * @param mixed  $value                 the value to match
6260
     *
6261
     * @throws \InvalidArgumentException if property or method is not defined
6262
     *
6263
     * @return static
6264
     *
6265
     * @psalm-param  T $value
6266
     * @psalm-return static<TKey,T>
6267
     */
6268 1
    public function where(string $keyOrPropertyOrMethod, $value): self
6269
    {
6270 1
        return $this->filter(
6271
            function ($item) use ($keyOrPropertyOrMethod, $value) {
6272 1
                $accessorValue = $this->extractValue(
6273 1
                    $item,
6274 1
                    $keyOrPropertyOrMethod
6275
                );
6276
6277 1
                return $accessorValue === $value;
6278 1
            }
6279
        );
6280
    }
6281
6282
    /**
6283
     * Convert an array into a object.
6284
     *
6285
     * @param array $array
6286
     *
6287
     * @return \stdClass
6288
     *
6289
     * @psalm-param array<mixed,mixed> $array
6290
     */
6291 4
    final protected static function arrayToObject(array $array = []): \stdClass
6292
    {
6293
        // init
6294 4
        $object = new \stdClass();
6295
6296 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6297 1
            return $object;
6298
        }
6299
6300 3
        foreach ($array as $name => $value) {
6301 3
            if (\is_array($value) === true) {
6302 1
                $object->{$name} = static::arrayToObject($value);
6303
            } else {
6304 3
                $object->{$name} = $value;
6305
            }
6306
        }
6307
6308 3
        return $object;
6309
    }
6310
6311
    /**
6312
     * @param array|\Generator|null $input         <p>
6313
     *                                             An array containing keys to return.
6314
     *                                             </p>
6315
     * @param mixed|null            $search_values [optional] <p>
6316
     *                                             If specified, then only keys containing these values are returned.
6317
     *                                             </p>
6318
     * @param bool                  $strict        [optional] <p>
6319
     *                                             Determines if strict comparison (===) should be used during the
6320
     *                                             search.
6321
     *                                             </p>
6322
     *
6323
     * @return array
6324
     *               <p>an array of all the keys in input</p>
6325
     *
6326
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6327
     * @psalm-return array<TKey|mixed>
6328
     * @psalm-mutation-free
6329
     */
6330 11
    protected function array_keys_recursive(
6331
        $input = null,
6332
        $search_values = null,
6333
        bool $strict = true
6334
    ): array {
6335
        // init
6336 11
        $keys = [];
6337 11
        $keysTmp = [];
6338
6339 11
        if ($input === null) {
6340 4
            $input = $this->getGenerator();
6341
        }
6342
6343 11
        if ($search_values === null) {
6344 11
            foreach ($input as $key => $value) {
6345 11
                $keys[] = $key;
6346
6347
                // check if recursive is needed
6348 11
                if (\is_array($value) === true) {
6349 11
                    $keysTmp[] = $this->array_keys_recursive($value);
6350
                }
6351
            }
6352
        } else {
6353 1
            $is_array_tmp = \is_array($search_values);
6354
6355 1
            foreach ($input as $key => $value) {
6356 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...
6357
                    (
6358 1
                        $is_array_tmp === false
6359
                        &&
6360 1
                        $strict === true
6361
                        &&
6362 1
                        $search_values === $value
6363
                    )
6364
                    ||
6365
                    (
6366 1
                        $is_array_tmp === false
6367
                        &&
6368 1
                        $strict === false
6369
                        &&
6370 1
                        $search_values == $value
6371
                    )
6372
                    ||
6373
                    (
6374 1
                        $is_array_tmp === true
6375
                        &&
6376 1
                        \in_array($value, $search_values, $strict)
6377
                    )
6378
                ) {
6379 1
                    $keys[] = $key;
6380
                }
6381
6382
                // check if recursive is needed
6383 1
                if (\is_array($value) === true) {
6384 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6385
                }
6386
            }
6387
        }
6388
6389 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6390
    }
6391
6392
    /**
6393
     * @param mixed      $path
6394
     * @param callable   $callable
6395
     * @param array|null $currentOffset
6396
     *
6397
     * @return void
6398
     *
6399
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6400
     * @psalm-mutation-free
6401
     */
6402 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6403
    {
6404 10
        $this->generatorToArray();
6405
6406 10
        if ($currentOffset === null) {
6407 10
            $currentOffset = &$this->array;
6408
        }
6409
6410 10
        $explodedPath = \explode($this->pathSeparator, $path);
6411 10
        if ($explodedPath === false) {
6412
            return;
6413
        }
6414
6415 10
        $nextPath = \array_shift($explodedPath);
6416
6417 10
        if (!isset($currentOffset[$nextPath])) {
6418 1
            return;
6419
        }
6420
6421 9
        if (!empty($explodedPath)) {
6422 1
            $this->callAtPath(
6423 1
                \implode($this->pathSeparator, $explodedPath),
6424 1
                $callable,
6425 1
                $currentOffset[$nextPath]
6426
            );
6427
        } else {
6428 9
            $callable($currentOffset[$nextPath]);
6429
        }
6430 9
    }
6431
6432
    /**
6433
     * Extracts the value of the given property or method from the object.
6434
     *
6435
     * @param static $object                <p>The object to extract the value from.</p>
6436
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
6437
     *                                      value should be extracted.</p>
6438
     *
6439
     * @throws \InvalidArgumentException if the method or property is not defined
6440
     *
6441
     * @return mixed
6442
     *               <p>The value extracted from the specified property or method.</p>
6443
     *
6444
     * @psalm-param self<TKey,T> $object
6445
     */
6446 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
6447
    {
6448 2
        if (isset($object[$keyOrPropertyOrMethod])) {
6449 2
            $return = $object->get($keyOrPropertyOrMethod);
6450
6451 2
            if ($return instanceof self) {
6452 1
                return $return->toArray();
6453
            }
6454
6455 1
            return $return;
6456
        }
6457
6458
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
6459
            return $object->{$keyOrPropertyOrMethod};
6460
        }
6461
6462
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
6463
            return $object->{$keyOrPropertyOrMethod}();
6464
        }
6465
6466
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6467
    }
6468
6469
    /**
6470
     * create a fallback for array
6471
     *
6472
     * 1. use the current array, if it's a array
6473
     * 2. fallback to empty array, if there is nothing
6474
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6475
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6476
     * 5. call "__toArray()" on object, if the method exists
6477
     * 6. cast a string or object with "__toString()" into an array
6478
     * 7. throw a "InvalidArgumentException"-Exception
6479
     *
6480
     * @param mixed $data
6481
     *
6482
     * @throws \InvalidArgumentException
6483
     *
6484
     * @return array
6485
     *
6486
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6487
     */
6488 1200
    protected function fallbackForArray(&$data): array
6489
    {
6490 1200
        $data = $this->internalGetArray($data);
6491
6492 1200
        if ($data === null) {
6493 2
            throw new \InvalidArgumentException('Passed value should be a array');
6494
        }
6495
6496 1198
        return $data;
6497
    }
6498
6499
    /**
6500
     * @param bool $preserveKeys <p>
6501
     *                           e.g.: A generator maybe return the same key more then once,
6502
     *                           so maybe you will ignore the keys.
6503
     *                           </p>
6504
     *
6505
     * @return bool
6506
     *
6507
     * @noinspection ReturnTypeCanBeDeclaredInspection
6508
     * @psalm-mutation-free :/
6509
     */
6510 1112
    protected function generatorToArray(bool $preserveKeys = true)
6511
    {
6512 1112
        if ($this->generator) {
6513 2
            $this->array = $this->toArray(false, $preserveKeys);
6514 2
            $this->generator = null;
6515
6516 2
            return true;
6517
        }
6518
6519 1112
        return false;
6520
    }
6521
6522
    /**
6523
     * Get correct PHP constant for direction.
6524
     *
6525
     * @param int|string $direction
6526
     *
6527
     * @return int
6528
     * @psalm-mutation-free
6529
     */
6530 43
    protected function getDirection($direction): int
6531
    {
6532 43
        if ((string) $direction === $direction) {
6533 10
            $direction = \strtolower($direction);
6534
6535 10
            if ($direction === 'desc') {
6536 2
                $direction = \SORT_DESC;
6537
            } else {
6538 8
                $direction = \SORT_ASC;
6539
            }
6540
        }
6541
6542
        if (
6543 43
            $direction !== \SORT_DESC
6544
            &&
6545 43
            $direction !== \SORT_ASC
6546
        ) {
6547
            $direction = \SORT_ASC;
6548
        }
6549
6550 43
        return $direction;
6551
    }
6552
6553
    /**
6554
     * @return TypeCheckInterface[]
6555
     *
6556
     * @noinspection ReturnTypeCanBeDeclaredInspection
6557
     */
6558 22
    protected function getPropertiesFromPhpDoc()
6559
    {
6560 22
        static $PROPERTY_CACHE = [];
6561 22
        $cacheKey = 'Class::' . static::class;
6562
6563 22
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6564 21
            return $PROPERTY_CACHE[$cacheKey];
6565
        }
6566
6567
        // init
6568 3
        $properties = [];
6569
6570 3
        $reflector = new \ReflectionClass($this);
6571 3
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6572 3
        $docComment = $reflector->getDocComment();
6573 3
        if ($docComment) {
6574 2
            $docblock = $factory->create($docComment);
6575
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6576 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6577 2
                $typeName = $tag->getVariableName();
6578
                /** @var string|null $typeName */
6579 2
                if ($typeName !== null) {
6580 2
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
6581 2
                    if ($typeCheckPhpDoc !== null) {
6582 2
                        $properties[$typeName] = $typeCheckPhpDoc;
6583
                    }
6584
                }
6585
            }
6586
        }
6587
6588 3
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6589
    }
6590
6591
    /**
6592
     * @param mixed $glue
6593
     * @param mixed $pieces
6594
     * @param bool  $useKeys
6595
     *
6596
     * @return string
6597
     * @psalm-mutation-free
6598
     */
6599 36
    protected function implode_recursive(
6600
        $glue = '',
6601
        $pieces = [],
6602
        bool $useKeys = false
6603
    ): string {
6604 36
        if ($pieces instanceof self) {
6605 1
            $pieces = $pieces->toArray();
6606
        }
6607
6608 36
        if (\is_array($pieces) === true) {
6609 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6610 36
            $pieces_count_not_zero = $pieces_count > 0;
6611
6612 36
            return \implode(
6613 36
                $glue,
6614 36
                \array_map(
6615 36
                    [$this, 'implode_recursive'],
6616 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6617 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6618
                )
6619
            );
6620
        }
6621
6622
        if (
6623 36
            \is_scalar($pieces) === true
6624
            ||
6625 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6626
        ) {
6627 32
            return (string) $pieces;
6628
        }
6629
6630 8
        return '';
6631
    }
6632
6633
    /**
6634
     * @param mixed                 $needle   <p>
6635
     *                                        The searched value.
6636
     *                                        </p>
6637
     *                                        <p>
6638
     *                                        If needle is a string, the comparison is done
6639
     *                                        in a case-sensitive manner.
6640
     *                                        </p>
6641
     * @param array|\Generator|null $haystack <p>
6642
     *                                        The array.
6643
     *                                        </p>
6644
     * @param bool                  $strict   [optional] <p>
6645
     *                                        If the third parameter strict is set to true
6646
     *                                        then the in_array function will also check the
6647
     *                                        types of the
6648
     *                                        needle in the haystack.
6649
     *                                        </p>
6650
     *
6651
     * @return bool
6652
     *              <p>true if needle is found in the array, false otherwise</p>
6653
     *
6654
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6655
     * @psalm-mutation-free
6656
     */
6657 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6658
    {
6659 18
        if ($haystack === null) {
6660
            $haystack = $this->getGenerator();
6661
        }
6662
6663 18
        foreach ($haystack as $item) {
6664 14
            if (\is_array($item) === true) {
6665 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6666
            } else {
6667
                /** @noinspection NestedPositiveIfStatementsInspection */
6668 14
                if ($strict === true) {
6669 14
                    $returnTmp = $item === $needle;
6670
                } else {
6671
                    $returnTmp = $item == $needle;
6672
                }
6673
            }
6674
6675 14
            if ($returnTmp === true) {
6676 14
                return true;
6677
            }
6678
        }
6679
6680 8
        return false;
6681
    }
6682
6683
    /**
6684
     * @param mixed $data
6685
     *
6686
     * @return array|null
6687
     *
6688
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6689
     */
6690 1200
    protected function internalGetArray(&$data)
6691
    {
6692 1200
        if (\is_array($data) === true) {
6693 1194
            return $data;
6694
        }
6695
6696 60
        if (!$data) {
6697 6
            return [];
6698
        }
6699
6700 59
        if (\is_object($data) === true) {
6701 53
            if ($data instanceof \ArrayObject) {
6702 5
                return $data->getArrayCopy();
6703
            }
6704
6705 49
            if ($data instanceof \Generator) {
6706
                return static::createFromGeneratorImmutable($data)->toArray();
6707
            }
6708
6709 49
            if ($data instanceof \Traversable) {
6710
                return static::createFromObject($data)->toArray();
6711
            }
6712
6713 49
            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...
6714
                return (array) $data->jsonSerialize();
6715
            }
6716
6717 49
            if (\method_exists($data, '__toArray')) {
6718
                return (array) $data->__toArray();
6719
            }
6720
6721 49
            if (\method_exists($data, '__toString')) {
6722
                return [(string) $data];
6723
            }
6724
        }
6725
6726 55
        if (\is_callable($data)) {
6727
            /**
6728
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6729
             */
6730 47
            $this->generator = new ArrayyRewindableGenerator($data);
6731
6732 47
            return [];
6733
        }
6734
6735 10
        if (\is_scalar($data)) {
6736 8
            return [$data];
6737
        }
6738
6739 2
        return null;
6740
    }
6741
6742
    /**
6743
     * Internal mechanics of remove method.
6744
     *
6745
     * @param mixed $key
6746
     *
6747
     * @return bool
6748
     */
6749 21
    protected function internalRemove($key): bool
6750
    {
6751 21
        $this->generatorToArray();
6752
6753
        if (
6754 21
            $this->pathSeparator
6755
            &&
6756 21
            (string) $key === $key
6757
            &&
6758 21
            \strpos($key, $this->pathSeparator) !== false
6759
        ) {
6760
            $path = \explode($this->pathSeparator, (string) $key);
6761
6762
            if ($path !== false) {
6763
                // crawl though the keys
6764
                while (\count($path, \COUNT_NORMAL) > 1) {
6765
                    $key = \array_shift($path);
6766
6767
                    if (!$this->has($key)) {
6768
                        return false;
6769
                    }
6770
6771
                    $this->array = &$this->array[$key];
6772
                }
6773
6774
                $key = \array_shift($path);
6775
            }
6776
        }
6777
6778 21
        unset($this->array[$key]);
6779
6780 21
        return true;
6781
    }
6782
6783
    /**
6784
     * Internal mechanic of set method.
6785
     *
6786
     * @param int|string|null $key
6787
     * @param mixed           $value
6788
     * @param bool            $checkProperties
6789
     *
6790
     * @return bool
6791
     */
6792 1050
    protected function internalSet(
6793
        $key,
6794
        &$value,
6795
        bool $checkProperties = true
6796
    ): bool {
6797
        if (
6798 1050
            $checkProperties === true
6799
            &&
6800 1050
            $this->properties !== []
6801
        ) {
6802 109
            $this->checkType($key, $value);
6803
        }
6804
6805 1048
        if ($key === null) {
6806
            return false;
6807
        }
6808
6809 1048
        $this->generatorToArray();
6810
6811
        /** @psalm-var array<int|string,mixed> $array */
6812 1048
        $array = &$this->array;
6813
6814
        /**
6815
         * https://github.com/vimeo/psalm/issues/2536
6816
         *
6817
         * @psalm-suppress PossiblyInvalidArgument
6818
         * @psalm-suppress InvalidScalarArgument
6819
         */
6820
        if (
6821 1048
            $this->pathSeparator
6822
            &&
6823 1048
            (string) $key === $key
6824
            &&
6825 1048
            \strpos($key, $this->pathSeparator) !== false
6826
        ) {
6827 9
            $path = \explode($this->pathSeparator, (string) $key);
6828
6829 9
            if ($path !== false) {
6830
                // crawl through the keys
6831 9
                while (\count($path, \COUNT_NORMAL) > 1) {
6832 9
                    $key = \array_shift($path);
6833
6834 9
                    $array = &$array[$key];
6835
                }
6836
6837 9
                $key = \array_shift($path);
6838
            }
6839
        }
6840
6841 1048
        if ($array === null) {
6842 4
            $array = [];
6843 1045
        } elseif (!\is_array($array)) {
6844 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
6845
        }
6846
6847 1048
        $array[$key] = $value;
6848
6849 1048
        return true;
6850
    }
6851
6852
    /**
6853
     * Convert a object into an array.
6854
     *
6855
     * @param mixed|object $object
6856
     *
6857
     * @return array|mixed
6858
     *
6859
     * @psalm-mutation-free
6860
     */
6861 5
    protected static function objectToArray($object)
6862
    {
6863 5
        if (!\is_object($object)) {
6864 4
            return $object;
6865
        }
6866
6867 5
        $object = \get_object_vars($object);
6868
6869
        /**
6870
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6871
         */
6872 5
        return \array_map(['static', 'objectToArray'], $object);
6873
    }
6874
6875
    /**
6876
     * @param array $data
6877
     * @param bool  $checkPropertiesInConstructor
6878
     *
6879
     * @return void
6880
     *
6881
     * @psalm-param array<mixed,T> $data
6882
     */
6883 1198
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6884
    {
6885 1198
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6886
                                        &&
6887 1198
                                        $checkPropertiesInConstructor === true;
6888
6889 1198
        if ($this->properties !== []) {
6890 98
            foreach ($data as $key => &$valueInner) {
6891 98
                $this->internalSet(
6892 98
                    $key,
6893 98
                    $valueInner,
6894 98
                    $checkPropertiesInConstructor
6895
                );
6896
            }
6897
        } else {
6898
            if (
6899 1119
                $this->checkPropertyTypes === true
6900
                ||
6901 1119
                $checkPropertiesInConstructor === true
6902
            ) {
6903 21
                $this->properties = $this->getPropertiesFromPhpDoc();
6904
            }
6905
6906
            /** @var TypeCheckInterface[] $properties */
6907 1119
            $properties = $this->properties;
6908
6909
            if (
6910 1119
                $this->checkPropertiesMismatchInConstructor === true
6911
                &&
6912 1119
                \count($data) !== 0
6913
                &&
6914 1119
                \count(\array_diff_key($properties, $data)) > 0
6915
            ) {
6916 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...
6917
            }
6918
6919 1118
            foreach ($data as $key => &$valueInner) {
6920 947
                $this->internalSet(
6921 947
                    $key,
6922 947
                    $valueInner,
6923 947
                    $checkPropertiesInConstructor
6924
                );
6925
            }
6926
        }
6927 1191
    }
6928
6929
    /**
6930
     * sorting keys
6931
     *
6932
     * @param array      $elements
6933
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6934
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6935
     *                              <strong>SORT_NATURAL</strong></p>
6936
     *
6937
     * @return $this
6938
     *               <p>(Mutable) Return this Arrayy object.</p>
6939
     *
6940
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6941
     * @psalm-return static<TKey,T>
6942
     */
6943 18
    protected function sorterKeys(
6944
        array &$elements,
6945
        $direction = \SORT_ASC,
6946
        int $strategy = \SORT_REGULAR
6947
    ): self {
6948 18
        $direction = $this->getDirection($direction);
6949
6950
        switch ($direction) {
6951 18
            case 'desc':
6952 18
            case \SORT_DESC:
6953 6
                \krsort($elements, $strategy);
6954
6955 6
                break;
6956 13
            case 'asc':
6957 13
            case \SORT_ASC:
6958
            default:
6959 13
                \ksort($elements, $strategy);
6960
        }
6961
6962 18
        return $this;
6963
    }
6964
6965
    /**
6966
     * @param array      $elements  <p>Warning: used as reference</p>
6967
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6968
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6969
     *                              <strong>SORT_NATURAL</strong></p>
6970
     * @param bool       $keepKeys
6971
     *
6972
     * @return $this
6973
     *               <p>(Mutable) Return this Arrayy object.</p>
6974
     *
6975
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6976
     * @psalm-return static<TKey,T>
6977
     */
6978 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6979
    {
6980 24
        $direction = $this->getDirection($direction);
6981
6982 24
        if (!$strategy) {
6983 24
            $strategy = \SORT_REGULAR;
6984
        }
6985
6986
        switch ($direction) {
6987 24
            case 'desc':
6988 24
            case \SORT_DESC:
6989 13
                if ($keepKeys) {
6990 9
                    \arsort($elements, $strategy);
6991
                } else {
6992 4
                    \rsort($elements, $strategy);
6993
                }
6994
6995 13
                break;
6996 11
            case 'asc':
6997 11
            case \SORT_ASC:
6998
            default:
6999 11
                if ($keepKeys) {
7000 4
                    \asort($elements, $strategy);
7001
                } else {
7002 7
                    \sort($elements, $strategy);
7003
                }
7004
        }
7005
7006 24
        return $this;
7007
    }
7008
7009
    /**
7010
     * @param array $array
7011
     *
7012
     * @return array
7013
     *
7014
     * @psalm-mutation-free
7015
     */
7016 25
    private function getArrayRecursiveHelperArrayy(array $array)
7017
    {
7018 25
        if ($array === []) {
7019
            return [];
7020
        }
7021
7022 25
        \array_walk_recursive(
7023 25
            $array,
7024
            /**
7025
             * @param array|self $item
7026
             *
7027
             * @return void
7028
             */
7029
            static function (&$item) {
7030 25
                if ($item instanceof self) {
7031 1
                    $item = $item->getArray();
7032
                }
7033 25
            }
7034
        );
7035
7036 25
        return $array;
7037
    }
7038
7039
    /**
7040
     * @param int|string|null $key
7041
     * @param mixed           $value
7042
     *
7043
     * @return void
7044
     */
7045 109
    private function checkType($key, $value)
7046
    {
7047
        if (
7048 109
            $key !== null
7049
            &&
7050 109
            isset($this->properties[$key]) === false
7051
            &&
7052 109
            $this->checkPropertiesMismatch === true
7053
        ) {
7054
            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...
7055
        }
7056
7057 109
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7058 96
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7059 22
        } elseif ($key !== null && isset($this->properties[$key])) {
7060 22
            $this->properties[$key]->checkType($value);
7061
        }
7062 107
    }
7063
}
7064