Completed
Push — master ( 5d67ac...3f6dc1 )
by Lars
01:32
created

Arrayy::split()   B

Complexity

Conditions 8
Paths 2

Size

Total Lines 56

Duplication

Lines 44
Ratio 78.57 %

Code Coverage

Tests 32
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
nc 2
nop 2
dl 44
loc 56
ccs 32
cts 32
cp 1
crap 8
rs 7.7155
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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