Completed
Push — master ( 498931...d26e7d )
by Lars
02:01
created

Arrayy::unserialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.2559

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 10
ccs 3
cts 5
cp 0.6
crap 2.2559
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
use Arrayy\TypeCheck\TypeCheckArray;
8
use Arrayy\TypeCheck\TypeCheckInterface;
9
use Arrayy\TypeCheck\TypeCheckPhpDoc;
10
11
/**
12
 * Methods to manage arrays.
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 *
17
 * @template TKey of array-key
18
 * @template T
19
 * @template-extends \ArrayObject<TKey,T>
20
 * @template-implements \IteratorAggregate<TKey,T>
21
 * @template-implements \ArrayAccess<TKey,T>
22
 */
23
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
24
{
25
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
26
27
    const ARRAYY_HELPER_WALK = '!!!!Arrayy_Helper_Walk!!!!';
28
29
    /**
30
     * @var array
31
     *
32
     * @phpstan-var array<array-key|TKey,T>
33
     */
34
    protected $array = [];
35
36
    /**
37
     * @var \Arrayy\ArrayyRewindableGenerator|null
38
     *
39
     * @phpstan-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
40
     */
41
    protected $generator;
42
43
    /**
44
     * @var string
45
     *
46
     * @phpstan-var class-string<\Arrayy\ArrayyIterator>
47
     */
48
    protected $iteratorClass = ArrayyIterator::class;
49
50
    /**
51
     * @var string
52
     */
53
    protected $pathSeparator = '.';
54
55
    /**
56
     * @var bool
57
     */
58
    protected $checkPropertyTypes = false;
59
60
    /**
61
     * @var bool
62
     */
63
    protected $checkForMissingPropertiesInConstructor = false;
64
65
    /**
66
     * @var bool
67
     */
68
    protected $checkPropertiesMismatchInConstructor = false;
69
70
    /**
71
     * @var bool
72
     */
73
    protected $checkPropertiesMismatch = true;
74
75
    /**
76
     * @var array<array-key,TypeCheckInterface>|TypeCheckArray<array-key,TypeCheckInterface>
77
     */
78
    protected $properties = [];
79
80
    /**
81
     * Initializes
82
     *
83
     * @param mixed  $data                         <p>
84
     *                                             Should be an array or a generator, otherwise it will try
85
     *                                             to convert it into an array.
86
     *                                             </p>
87
     * @param string $iteratorClass                optional <p>
88
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
89
     *                                             need this option.
90
     *                                             </p>
91
     * @param bool   $checkPropertiesInConstructor optional <p>
92
     *                                             You need to extend the "Arrayy"-class and you need to set
93
     *                                             the $checkPropertiesMismatchInConstructor class property
94
     *                                             to
95
     *                                             true, otherwise this option didn't not work anyway.
96
     *                                             </p>
97
     *
98
     * @phpstan-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
99
     */
100 1214
    public function __construct(
101
        $data = [],
102
        string $iteratorClass = ArrayyIterator::class,
103
        bool $checkPropertiesInConstructor = true
104
    ) {
105 1214
        $data = $this->fallbackForArray($data);
106
107
        // used only for serialize + unserialize, all other methods are overwritten
108
        /**
109
         * @psalm-suppress InvalidArgument - why?
110
         */
111 1212
        parent::__construct([], 0, $iteratorClass);
112
113 1212
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
114
115 1204
        $this->setIteratorClass($iteratorClass);
116 1204
    }
117
118
    /**
119
     * @return void
120
     */
121 52
    public function __clone()
122
    {
123 52
        if (!\is_array($this->properties)) {
124
            $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...
125
        }
126
127 52
        if ($this->generator !== null) {
128
            $this->generator = clone $this->generator;
129
        }
130 52
    }
131
132
    /**
133
     * Call object as function.
134
     *
135
     * @param mixed $key
136
     *
137
     * @return mixed
138
     *
139
     * @phpstan-param TKey $key
140
     * @phpstan-return false|T|array<TKey,T>
141
     */
142 1
    public function __invoke($key = null)
143
    {
144 1
        if ($key !== null) {
145 1
            $this->generatorToArray();
146
147 1
            return $this->array[$key] ?? false;
148
        }
149
150
        return $this->toArray();
151
    }
152
153
    /**
154
     * Whether or not an element exists by key.
155
     *
156
     * @param mixed $key
157
     *
158
     * @return bool
159
     *              <p>True is the key/index exists, otherwise false.</p>
160
     *
161
     * @phpstan-param TKey $key
162
     */
163
    public function __isset($key): bool
164
    {
165
        return $this->offsetExists($key);
166
    }
167
168
    /**
169
     * Assigns a value to the specified element.
170
     *
171
     * @param mixed $key
172
     * @param mixed $value
173
     *
174
     * @return void
175
     *
176
     * @phpstan-param TKey $key
177
     * @phpstan-param T $value
178
     */
179 3
    public function __set($key, $value)
180
    {
181 3
        $this->internalSet($key, $value);
182 3
    }
183
184
    /**
185
     * magic to string
186
     *
187
     * @return string
188
     */
189 15
    public function __toString(): string
190
    {
191 15
        return $this->toString();
192
    }
193
194
    /**
195
     * Unset element by key.
196
     *
197
     * @param mixed $key
198
     *
199
     * @phpstan-param TKey $key
200
     */
201
    public function __unset($key)
202
    {
203
        $this->internalRemove($key);
204
    }
205
206
    /**
207
     * Get a value by key.
208
     *
209
     * @param mixed $key
210
     *
211
     * @return mixed
212
     *               <p>Get a Value from the current array.</p>
213
     *
214
     * @phpstan-param TKey $key
215
     * @phpstan-return null|self<array-key,T>|T
216
     */
217 133
    public function &__get($key)
218
    {
219 133
        $return = $this->get($key, null, null, true);
220
221 133
        if (\is_array($return) === true) {
222
            $return = static::create(
223
                [],
224
                $this->iteratorClass,
225
                false
226
            )->createByReference($return);
227
        }
228
229 133
        return $return;
230
    }
231
232
    /**
233
     * Add new values (optional using dot-notation).
234
     *
235
     * @param mixed           $value
236
     * @param int|string|null $key
237
     *
238
     * @return static
239
     *                <p>(Immutable) Return this Arrayy object, with the appended values.</p>
240
     *
241
     * @phpstan-param T $value
242
     * @phpstan-param TKey $key
243
     * @phpstan-return static<TKey,T>
244
     *
245
     * @psalm-mutation-free
246
     */
247 13
    public function add($value, $key = null)
248
    {
249 13
        if ($key !== null) {
250 5
            $get = $this->get($key);
251 5
            if ($get !== null) {
252 1
                $value = \array_merge_recursive(
253 1
                    !$get instanceof self ? [$get] : $get->getArray(),
254 1
                    !\is_array($value) ? [$value] : $value
255
                );
256
            }
257
258 5
            $this->internalSet($key, $value);
259
260 4
            return $this;
261
        }
262
263 8
        return $this->append($value);
264
    }
265
266
    /**
267
     * Append a (key) + value to the current array.
268
     *
269
     * EXAMPLE: <code>
270
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
271
     * </code>
272
     *
273
     * @param mixed $value
274
     * @param mixed $key
275
     *
276
     * @return $this
277
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
278
     *
279
     * @phpstan-param T $value
280
     * @phpstan-param TKey|null $key
281
     * @phpstan-return static<TKey,T>
282
     */
283 20
    public function append($value, $key = null): self
284
    {
285 20
        $this->generatorToArray();
286
287 20
        if ($this->properties !== []) {
288 6
            $this->checkType($key, $value);
289
        }
290
291 19
        if ($key !== null) {
292
            if (
293 2
                isset($this->array[$key])
294
                &&
295 2
                \is_array($this->array[$key])
296
            ) {
297
                $this->array[$key][] = $value;
298
            } else {
299 2
                $this->array[$key] = $value;
300
            }
301
        } else {
302 17
            $this->array[] = $value;
303
        }
304
305 19
        return $this;
306
    }
307
308
    /**
309
     * Append a (key) + value to the current array.
310
     *
311
     * EXAMPLE: <code>
312
     * a(['fòô' => 'bàř'])->appendImmutable('foo')->getArray(); // ['fòô' => 'bàř', 0 => 'foo']
313
     * </code>
314
     *
315
     * @param mixed $value
316
     * @param mixed $key
317
     *
318
     * @return $this
319
     *               <p>(Immutable) Return this Arrayy object, with the appended values.</p>
320
     *
321
     * @phpstan-param T $value
322
     * @phpstan-param TKey $key
323
     * @phpstan-return static<TKey,T>
324
     * @psalm-mutation-free
325
     */
326 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...
327
    {
328
        /**
329
         * @phpstan-return \Generator<TKey,T> $generator
330
         */
331 1
        $generator = function () use ($key, $value): \Generator {
332 1
            if ($this->properties !== []) {
333
                $this->checkType($key, $value);
334
            }
335
336 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
337 1
                yield $keyOld => $itemOld;
338
            }
339
340 1
            if ($key !== null) {
341
                yield $key => $value;
342
            } else {
343 1
                yield $value;
344
            }
345 1
        };
346
347 1
        return static::create(
348 1
            $generator,
349 1
            $this->iteratorClass,
350 1
            false
351
        );
352
    }
353
354
    /**
355
     * Sort the entries by value.
356
     *
357
     * @param int $sort_flags [optional] <p>
358
     *                        You may modify the behavior of the sort using the optional
359
     *                        parameter sort_flags, for details
360
     *                        see sort.
361
     *                        </p>
362
     *
363
     * @return $this
364
     *               <p>(Mutable) Return this Arrayy object.</p>
365
     *
366
     * @phpstan-return static<TKey,T>
367
     */
368 4
    public function asort(int $sort_flags = 0): self
369
    {
370 4
        $this->generatorToArray();
371
372 4
        \asort($this->array, $sort_flags);
373
374 4
        return $this;
375
    }
376
377
    /**
378
     * Sort the entries by value.
379
     *
380
     * @param int $sort_flags [optional] <p>
381
     *                        You may modify the behavior of the sort using the optional
382
     *                        parameter sort_flags, for details
383
     *                        see sort.
384
     *                        </p>
385
     *
386
     * @return $this
387
     *               <p>(Immutable) Return this Arrayy object.</p>
388
     *
389
     * @phpstan-return static<TKey,T>
390
     * @psalm-mutation-free
391
     */
392 4
    public function asortImmutable(int $sort_flags = 0): self
393
    {
394 4
        $that = clone $this;
395
396
        /**
397
         * @psalm-suppress ImpureMethodCall - object is already cloned
398
         */
399 4
        $that->asort($sort_flags);
400
401 4
        return $that;
402
    }
403
404
    /**
405
     * Counts all elements in an array, or something in an object.
406
     *
407
     * EXAMPLE: <code>
408
     * a([-9, -8, -7, 1.32])->count(); // 4
409
     * </code>
410
     *
411
     * <p>
412
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
413
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
414
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
415
     * implemented and used in PHP.
416
     * </p>
417
     *
418
     * @see http://php.net/manual/en/function.count.php
419
     *
420
     * @param int $mode [optional] If the optional mode parameter is set to
421
     *                  COUNT_RECURSIVE (or 1), count
422
     *                  will recursively count the array. This is particularly useful for
423
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
424
     *
425
     * @return int
426
     *             <p>
427
     *             The number of elements in var, which is
428
     *             typically an array, since anything else will have one
429
     *             element.
430
     *             </p>
431
     *             <p>
432
     *             If var is not an array or an object with
433
     *             implemented Countable interface,
434
     *             1 will be returned.
435
     *             There is one exception, if var is &null;,
436
     *             0 will be returned.
437
     *             </p>
438
     *             <p>
439
     *             Caution: count may return 0 for a variable that isn't set,
440
     *             but it may also return 0 for a variable that has been initialized with an
441
     *             empty array. Use isset to test if a variable is set.
442
     *             </p>
443
     * @psalm-mutation-free
444
     */
445 147
    public function count(int $mode = \COUNT_NORMAL): int
446
    {
447
        if (
448 147
            $this->generator
449
            &&
450 147
            $mode === \COUNT_NORMAL
451
        ) {
452 4
            return \iterator_count($this->generator);
453
        }
454
455 143
        return \count($this->toArray(), $mode);
456
    }
457
458
    /**
459
     * Exchange the array for another one.
460
     *
461
     * @param array|mixed|static $data
462
     *
463
     * 1. use the current array, if it's a array
464
     * 2. fallback to empty array, if there is nothing
465
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
466
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
467
     * 5. call "__toArray()" on object, if the method exists
468
     * 6. cast a string or object with "__toString()" into an array
469
     * 7. throw a "InvalidArgumentException"-Exception
470
     *
471
     * @return array
472
     *
473
     * @phpstan-param  T|array<TKey,T>|self<TKey,T> $data
474
     * @phpstan-return array<TKey,T>
475
     */
476 1
    public function exchangeArray($data): array
477
    {
478
        /** @phpstan-var array<TKey,T> array */
479 1
        $array = $this->fallbackForArray($data);
480
481 1
        $this->array = $array;
482 1
        $this->generator = null;
483
484 1
        return $this->array;
485
    }
486
487
    /**
488
     * Creates a copy of the ArrayyObject.
489
     *
490
     * @return array
491
     *
492
     * @phpstan-return array<int|string|TKey,T>
493
     */
494 6
    public function getArrayCopy(): array
495
    {
496 6
        $this->generatorToArray();
497
498 6
        return $this->array;
499
    }
500
501
    /**
502
     * Returns a new iterator, thus implementing the \Iterator interface.
503
     *
504
     * EXAMPLE: <code>
505
     * a(['foo', 'bar'])->getIterator(); // ArrayyIterator['foo', 'bar']
506
     * </code>
507
     *
508
     * @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...
509
     *                          <p>An iterator for the values in the array.</p>
510
     * @phpstan-return \Iterator<TKey, T>
511
     */
512 28
    public function getIterator(): \Iterator
513
    {
514 28
        if ($this->generator instanceof ArrayyRewindableGenerator) {
515 1
            $generator = clone $this->generator;
516
517
            /** @phpstan-var \Arrayy\ArrayyRewindableGenerator<TKey,T> */
518 1
            $generatorTmp = new ArrayyRewindableExtendedGenerator(
519 1
                static function () use ($generator): \Generator {
520 1
                    yield from $generator;
521 1
                },
522 1
                null,
523 1
                static::class
524
            );
525
526 1
            $this->generator = $generatorTmp;
527
528 1
            return $this->generator;
529
        }
530
531 27
        $iterator = $this->getIteratorClass();
532
533 27
        if ($iterator === ArrayyIterator::class) {
534 27
            return new $iterator($this->toArray(), 0, static::class);
535
        }
536
537
        $return = new $iterator($this->toArray());
538
        \assert($return instanceof \Iterator);
539
540
        return $return;
541
    }
542
543
    /**
544
     * Gets the iterator classname for the ArrayObject.
545
     *
546
     * @return string
547
     *
548
     * @phpstan-return class-string
549
     */
550 27
    public function getIteratorClass(): string
551
    {
552 27
        return $this->iteratorClass;
553
    }
554
555
    /**
556
     * Sort the entries by key.
557
     *
558
     * @param int $sort_flags [optional] <p>
559
     *                        You may modify the behavior of the sort using the optional
560
     *                        parameter sort_flags, for details
561
     *                        see sort.
562
     *                        </p>
563
     *
564
     * @return $this
565
     *               <p>(Mutable) Return this Arrayy object.</p>
566
     *
567
     * @phpstan-return static<TKey,T>
568
     */
569 4
    public function ksort(int $sort_flags = 0): self
570
    {
571 4
        $this->generatorToArray();
572
573 4
        \ksort($this->array, $sort_flags);
574
575 4
        return $this;
576
    }
577
578
    /**
579
     * Sort the entries by key.
580
     *
581
     * @param int $sort_flags [optional] <p>
582
     *                        You may modify the behavior of the sort using the optional
583
     *                        parameter sort_flags, for details
584
     *                        see sort.
585
     *                        </p>
586
     *
587
     * @return $this
588
     *               <p>(Immutable) Return this Arrayy object.</p>
589
     *
590
     * @phpstan-return static<TKey,T>
591
     */
592 4
    public function ksortImmutable(int $sort_flags = 0): self
593
    {
594 4
        $that = clone $this;
595
596
        /**
597
         * @psalm-suppress ImpureMethodCall - object is already cloned
598
         */
599 4
        $that->ksort($sort_flags);
600
601 4
        return $that;
602
    }
603
604
    /**
605
     * Sort an array using a case insensitive "natural order" algorithm.
606
     *
607
     * @return $this
608
     *               <p>(Mutable) Return this Arrayy object.</p>
609
     *
610
     * @phpstan-return static<TKey,T>
611
     */
612 8
    public function natcasesort(): self
613
    {
614 8
        $this->generatorToArray();
615
616 8
        \natcasesort($this->array);
617
618 8
        return $this;
619
    }
620
621
    /**
622
     * Sort an array using a case insensitive "natural order" algorithm.
623
     *
624
     * @return $this
625
     *               <p>(Immutable) Return this Arrayy object.</p>
626
     *
627
     * @phpstan-return static<TKey,T>
628
     * @psalm-mutation-free
629
     */
630 4
    public function natcasesortImmutable(): self
631
    {
632 4
        $that = clone $this;
633
634
        /**
635
         * @psalm-suppress ImpureMethodCall - object is already cloned
636
         */
637 4
        $that->natcasesort();
638
639 4
        return $that;
640
    }
641
642
    /**
643
     * Sort entries using a "natural order" algorithm.
644
     *
645
     * @return $this
646
     *               <p>(Mutable) Return this Arrayy object.</p>
647
     *
648
     * @phpstan-return static<TKey,T>
649
     */
650 10
    public function natsort(): self
651
    {
652 10
        $this->generatorToArray();
653
654 10
        \natsort($this->array);
655
656 10
        return $this;
657
    }
658
659
    /**
660
     * Sort entries using a "natural order" algorithm.
661
     *
662
     * @return $this
663
     *               <p>(Immutable) Return this Arrayy object.</p>
664
     *
665
     * @phpstan-return static<TKey,T>
666
     * @psalm-mutation-free
667
     */
668 4
    public function natsortImmutable(): self
669
    {
670 4
        $that = clone $this;
671
672
        /**
673
         * @psalm-suppress ImpureMethodCall - object is already cloned
674
         */
675 4
        $that->natsort();
676
677 4
        return $that;
678
    }
679
680
    /**
681
     * Whether or not an offset exists.
682
     *
683
     * @param bool|int|string $offset
684
     *
685
     * @return bool
686
     *
687
     * @psalm-mutation-free
688
     */
689 164
    public function offsetExists($offset): bool
690
    {
691
        // php cast "bool"-index into "int"-index
692 164
        if ((bool) $offset === $offset) {
693 1
            $offset = (int) $offset;
694
        }
695
        \assert(\is_int($offset) || \is_string($offset));
696
697 164
        $offsetExists = $this->keyExists($offset);
698 164
        if ($offsetExists === true) {
699 143
            return true;
700
        }
701
702
        /**
703
         * https://github.com/vimeo/psalm/issues/2536
704
         *
705
         * @psalm-suppress PossiblyInvalidArgument
706
         * @psalm-suppress InvalidScalarArgument
707
         */
708 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...
709 124
            $this->pathSeparator
710
            &&
711 124
            (string) $offset === $offset
712
            &&
713 124
            \strpos($offset, $this->pathSeparator) !== false
714
        ) {
715 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
716 4
            if ($explodedPath !== false) {
717
                /** @var string $lastOffset - helper for phpstan */
718 4
                $lastOffset = \array_pop($explodedPath);
719 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
720
721
                /**
722
                 * @psalm-suppress MissingClosureReturnType
723
                 * @psalm-suppress MissingClosureParamType
724
                 */
725 4
                $this->callAtPath(
726 4
                    $containerPath,
727 4
                    static function ($container) use ($lastOffset, &$offsetExists) {
728 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
729 4
                    }
730
                );
731
            }
732
        }
733
734 124
        return $offsetExists;
735
    }
736
737
    /**
738
     * Returns the value at specified offset.
739
     *
740
     * @param int|string $offset
741
     *
742
     * @return mixed
743
     *               <p>Will return null if the offset did not exists.</p>
744
     *
745
     * @phpstan-param TKey $offset
746
     */
747 133
    public function &offsetGet($offset)
748
    {
749
        // init
750 133
        $value = null;
751
752 133
        if ($this->offsetExists($offset)) {
753 131
            $value = &$this->__get($offset);
754
        }
755
756 133
        return $value;
757
    }
758
759
    /**
760
     * Assigns a value to the specified offset + check the type.
761
     *
762
     * @param int|string|null $offset
763
     * @param mixed           $value
764
     *
765
     * @return void
766
     */
767 28
    public function offsetSet($offset, $value)
768
    {
769 28
        $this->generatorToArray();
770
771 28
        if ($offset === null) {
772 7
            if ($this->properties !== []) {
773 2
                $this->checkType(null, $value);
774
            }
775
776 6
            $this->array[] = $value;
777
        } else {
778 21
            $this->internalSet(
779 21
                $offset,
780
                $value,
781 21
                true
782
            );
783
        }
784 27
    }
785
786
    /**
787
     * Unset an offset.
788
     *
789
     * @param int|string $offset
790
     *
791
     * @return void
792
     *              <p>(Mutable) Return nothing.</p>
793
     */
794 26
    public function offsetUnset($offset)
795
    {
796 26
        $this->generatorToArray();
797
798 26
        if ($this->array === []) {
799 6
            return;
800
        }
801
802 21
        if ($this->keyExists($offset)) {
803 14
            unset($this->array[$offset]);
804
805 14
            return;
806
        }
807
808
        /**
809
         * https://github.com/vimeo/psalm/issues/2536
810
         *
811
         * @psalm-suppress PossiblyInvalidArgument
812
         * @psalm-suppress InvalidScalarArgument
813
         */
814 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...
815 10
            $this->pathSeparator
816
            &&
817 10
            (string) $offset === $offset
818
            &&
819 10
            \strpos($offset, $this->pathSeparator) !== false
820
        ) {
821 7
            $path = \explode($this->pathSeparator, (string) $offset);
822
823 7
            if ($path !== false) {
824 7
                $pathToUnset = \array_pop($path);
825
826
                /**
827
                 * @psalm-suppress MissingClosureReturnType
828
                 * @psalm-suppress MissingClosureParamType
829
                 */
830 7
                $this->callAtPath(
831 7
                    \implode($this->pathSeparator, $path),
832 7
                    static function (&$offset) use ($pathToUnset) {
833 6
                        if (\is_array($offset)) {
834 5
                            unset($offset[$pathToUnset]);
835
                        } else {
836 1
                            $offset = null;
837
                        }
838 7
                    }
839
                );
840
            }
841
        }
842
843 10
        unset($this->array[$offset]);
844 10
    }
845
846
    /**
847
     * Serialize the current "Arrayy"-object.
848
     *
849
     * EXAMPLE: <code>
850
     * a([1, 4, 7])->serialize();
851
     * </code>
852
     *
853
     * @return string
854
     */
855 1
    public function serialize(): string
856
    {
857 1
        $this->generatorToArray();
858
859 1
        if (\PHP_VERSION_ID < 70400) {
860
            return parent::serialize();
861
        }
862
863 1
        return \serialize($this);
864
    }
865
866
    /**
867
     * Sets the iterator classname for the current "Arrayy"-object.
868
     *
869
     * @param string $iteratorClass
870
     *
871
     * @throws \InvalidArgumentException
872
     *
873
     * @return void
874
     *
875
     * @phpstan-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
876
     */
877 1204
    public function setIteratorClass($iteratorClass)
878
    {
879 1204
        if (\class_exists($iteratorClass)) {
880 1204
            $this->iteratorClass = $iteratorClass;
881
882 1204
            return;
883
        }
884
885
        if (\strpos($iteratorClass, '\\') === 0) {
886
            /** @var class-string<\Arrayy\ArrayyIterator<TKey,T>> $iteratorClass */
0 ignored issues
show
Documentation introduced by
The doc-type class-string<\Arrayy\ArrayyIterator<TKey,T>> could not be parsed: Unknown type name "class-string" at position 0. (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...
887
            $iteratorClass = '\\' . $iteratorClass;
888
            if (\class_exists($iteratorClass)) {
889
                /**
890
                 * @psalm-suppress PropertyTypeCoercion
891
                 */
892
                $this->iteratorClass = $iteratorClass;
893
894
                return;
895
            }
896
        }
897
898
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
899
    }
900
901
    /**
902
     * Sort the entries with a user-defined comparison function and maintain key association.
903
     *
904
     * @param callable $function
905
     *
906
     * @throws \InvalidArgumentException
907
     *
908
     * @return $this
909
     *               <p>(Mutable) Return this Arrayy object.</p>
910
     *
911
     * @phpstan-return static<TKey,T>
912
     */
913 8
    public function uasort($function): self
914
    {
915 8
        if (!\is_callable($function)) {
916
            throw new \InvalidArgumentException('Passed function must be callable');
917
        }
918
919 8
        $this->generatorToArray();
920
921 8
        \uasort($this->array, $function);
922
923 8
        return $this;
924
    }
925
926
    /**
927
     * Sort the entries with a user-defined comparison function and maintain key association.
928
     *
929
     * @param callable $function
930
     *
931
     * @throws \InvalidArgumentException
932
     *
933
     * @return $this
934
     *               <p>(Immutable) Return this Arrayy object.</p>
935
     *
936
     * @phpstan-return static<TKey,T>
937
     * @psalm-mutation-free
938
     */
939 4
    public function uasortImmutable($function): self
940
    {
941 4
        $that = clone $this;
942
943
        /**
944
         * @psalm-suppress ImpureMethodCall - object is already cloned
945
         */
946 4
        $that->uasort($function);
947
948 4
        return $that;
949
    }
950
951
    /**
952
     * Sort the entries by keys using a user-defined comparison function.
953
     *
954
     * @param callable $function
955
     *
956
     * @throws \InvalidArgumentException
957
     *
958
     * @return static
959
     *                <p>(Mutable) Return this Arrayy object.</p>
960
     *
961
     * @phpstan-return static<TKey,T>
962
     */
963 5
    public function uksort($function): self
964
    {
965 5
        return $this->customSortKeys($function);
966
    }
967
968
    /**
969
     * Sort the entries by keys using a user-defined comparison function.
970
     *
971
     * @param callable $function
972
     *
973
     * @throws \InvalidArgumentException
974
     *
975
     * @return static
976
     *                <p>(Immutable) Return this Arrayy object.</p>
977
     *
978
     * @phpstan-return static<TKey,T>
979
     * @psalm-mutation-free
980
     */
981 1
    public function uksortImmutable($function): self
982
    {
983 1
        return $this->customSortKeysImmutable($function);
984
    }
985
986
    /**
987
     * Unserialize an string and return the instance of the "Arrayy"-class.
988
     *
989
     * EXAMPLE: <code>
990
     * $serialized = a([1, 4, 7])->serialize();
991
     * a()->unserialize($serialized);
992
     * </code>
993
     *
994
     * @param string $string
995
     *
996
     * @return $this
997
     *
998
     * @phpstan-return static<TKey,T>
999
     */
1000 1
    public function unserialize($string): self
1001
    {
1002 1
        if (\PHP_VERSION_ID < 70400) {
1003
            parent::unserialize($string);
1004
1005
            return $this;
1006
        }
1007
1008 1
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
1009
    }
1010
1011
    /**
1012
     * Append a (key) + values to the current array.
1013
     *
1014
     * EXAMPLE: <code>
1015
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
1016
     * </code>
1017
     *
1018
     * @param array $values
1019
     * @param mixed $key
1020
     *
1021
     * @return $this
1022
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
1023
     *
1024
     * @phpstan-param  array<array-key,T> $values
1025
     * @phpstan-param  TKey|null $key
1026
     * @phpstan-return static<TKey,T>
1027
     */
1028 1
    public function appendArrayValues(array $values, $key = null)
1029
    {
1030 1
        $this->generatorToArray();
1031
1032 1
        if ($key !== null) {
1033
            if (
1034 1
                isset($this->array[$key])
1035
                &&
1036 1
                \is_array($this->array[$key])
1037
            ) {
1038 1
                foreach ($values as $value) {
1039 1
                    $this->array[$key][] = $value;
1040
                }
1041
            } else {
1042 1
                foreach ($values as $value) {
1043
                    $this->array[$key] = $value;
1044
                }
1045
            }
1046
        } else {
1047
            foreach ($values as $value) {
1048
                $this->array[] = $value;
1049
            }
1050
        }
1051
1052 1
        return $this;
1053
    }
1054
1055
    /**
1056
     * Add a suffix to each key.
1057
     *
1058
     * @param int|string $prefix
1059
     *
1060
     * @return static
1061
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
1062
     *
1063
     * @phpstan-return static<TKey,T>
1064
     * @psalm-mutation-free
1065
     */
1066 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...
1067
    {
1068
        // init
1069 10
        $result = [];
1070
1071 10
        foreach ($this->getGenerator() as $key => $item) {
1072 9
            if ($item instanceof self) {
1073
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
1074 9
            } elseif (\is_array($item)) {
1075
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
1076
                    ->appendToEachKey($prefix)
1077
                    ->toArray();
1078
            } else {
1079 9
                $result[$prefix . $key] = $item;
1080
            }
1081
        }
1082
1083 10
        return self::create(
1084 10
            $result,
1085 10
            $this->iteratorClass,
1086 10
            false
1087
        );
1088
    }
1089
1090
    /**
1091
     * Add a prefix to each value.
1092
     *
1093
     * @param float|int|string $prefix
1094
     *
1095
     * @return static
1096
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1097
     *
1098
     * @phpstan-return static<TKey,T>
1099
     * @psalm-mutation-free
1100
     */
1101 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...
1102
    {
1103
        // init
1104 10
        $result = [];
1105
1106 10
        foreach ($this->getGenerator() as $key => $item) {
1107 9
            if ($item instanceof self) {
1108
                $result[$key] = $item->appendToEachValue($prefix);
1109 9
            } elseif (\is_array($item)) {
1110
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1111 9
            } elseif (\is_object($item) === true) {
1112 1
                $result[$key] = $item;
1113
            } else {
1114 8
                $result[$key] = $prefix . $item;
1115
            }
1116
        }
1117
1118 10
        return self::create($result, $this->iteratorClass, false);
1119
    }
1120
1121
    /**
1122
     * Sort an array in reverse order and maintain index association.
1123
     *
1124
     * @return $this
1125
     *               <p>(Mutable) Return this Arrayy object.</p>
1126
     *
1127
     * @phpstan-return static<TKey,T>
1128
     */
1129 4
    public function arsort(): self
1130
    {
1131 4
        $this->generatorToArray();
1132
1133 4
        \arsort($this->array);
1134
1135 4
        return $this;
1136
    }
1137
1138
    /**
1139
     * Sort an array in reverse order and maintain index association.
1140
     *
1141
     * @return $this
1142
     *               <p>(Immutable) Return this Arrayy object.</p>
1143
     *
1144
     * @phpstan-return static<TKey,T>
1145
     * @psalm-mutation-free
1146
     */
1147 10
    public function arsortImmutable(): self
1148
    {
1149 10
        $that = clone $this;
1150
1151 10
        $that->generatorToArray();
1152
1153 10
        \arsort($that->array);
1154
1155 10
        return $that;
1156
    }
1157
1158
    /**
1159
     * Iterate over the current array and execute a callback for each loop.
1160
     *
1161
     * EXAMPLE: <code>
1162
     * $result = A::create();
1163
     * $closure = function ($value, $key) use ($result) {
1164
     *     $result[$key] = ':' . $value . ':';
1165
     * };
1166
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1167
     * </code>
1168
     *
1169
     * @param \Closure $closure
1170
     *
1171
     * @return static
1172
     *                <p>(Immutable)</p>
1173
     *
1174
     * @phpstan-param \Closure(T=,TKey=):mixed $closure <p>INFO: \Closure result is not used, but void is not supported in PHP 7.0</p>
1175
     * @phpstan-return static<TKey,T>
1176
     * @psalm-mutation-free
1177
     */
1178 3
    public function at(\Closure $closure): self
1179
    {
1180 3
        $that = clone $this;
1181
1182 3
        foreach ($that->getGenerator() as $key => $value) {
1183 3
            $closure($value, $key);
1184
        }
1185
1186 3
        return static::create(
1187 3
            $that->toArray(),
1188 3
            $this->iteratorClass,
1189 3
            false
1190
        );
1191
    }
1192
1193
    /**
1194
     * Returns the average value of the current array.
1195
     *
1196
     * EXAMPLE: <code>
1197
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1198
     * </code>
1199
     *
1200
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1201
     *
1202
     * @return float|int
1203
     *                   <p>The average value.</p>
1204
     * @psalm-mutation-free
1205
     */
1206 10
    public function average($decimals = 0)
1207
    {
1208 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1209
1210 10
        if (!$count) {
1211 2
            return 0;
1212
        }
1213
1214 8
        if ((int) $decimals !== $decimals) {
1215 3
            $decimals = 0;
1216
        }
1217
1218 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1219
    }
1220
1221
    /**
1222
     * Changes all keys in an array.
1223
     *
1224
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1225
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1226
     *
1227
     * @return static
1228
     *                <p>(Immutable)</p>
1229
     *
1230
     * @phpstan-return static<TKey,T>
1231
     * @psalm-mutation-free
1232
     */
1233 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1234
    {
1235
        if (
1236 1
            $case !== \CASE_LOWER
1237
            &&
1238 1
            $case !== \CASE_UPPER
1239
        ) {
1240
            $case = \CASE_LOWER;
1241
        }
1242
1243 1
        $return = [];
1244 1
        foreach ($this->getGenerator() as $key => $value) {
1245
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1246
1247 1
            if ($case === \CASE_LOWER) {
1248 1
                $key = \mb_strtolower((string) $key);
1249
            } else {
1250 1
                $key = \mb_strtoupper((string) $key);
1251
            }
1252
1253 1
            $return[$key] = $value;
1254
        }
1255
1256 1
        return static::create(
1257 1
            $return,
1258 1
            $this->iteratorClass,
1259 1
            false
1260
        );
1261
    }
1262
1263
    /**
1264
     * Change the path separator of the array wrapper.
1265
     *
1266
     * By default, the separator is: "."
1267
     *
1268
     * @param string $separator <p>Separator to set.</p>
1269
     *
1270
     * @return $this
1271
     *               <p>(Mutable) Return this Arrayy object.</p>
1272
     *
1273
     * @phpstan-return static<TKey,T>
1274
     */
1275 11
    public function changeSeparator($separator): self
1276
    {
1277 11
        $this->pathSeparator = $separator;
1278
1279 11
        return $this;
1280
    }
1281
1282
    /**
1283
     * Create a chunked version of the current array.
1284
     *
1285
     * EXAMPLE: <code>
1286
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1287
     * </code>
1288
     *
1289
     * @param int  $size         <p>Size of each chunk.</p>
1290
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1291
     *
1292
     * @return static
1293
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1294
     *
1295
     * @phpstan-return static<TKey,T>
1296
     * @psalm-mutation-free
1297
     */
1298 5
    public function chunk($size, $preserveKeys = false): self
1299
    {
1300 5
        if ($preserveKeys) {
1301
            $generator = function () use ($size) {
1302
                $values = [];
1303
                $tmpCounter = 0;
1304
                foreach ($this->getGenerator() as $key => $value) {
1305
                    ++$tmpCounter;
1306
1307
                    $values[$key] = $value;
1308
                    if ($tmpCounter === $size) {
1309
                        yield $values;
1310
1311
                        $values = [];
1312
                        $tmpCounter = 0;
1313
                    }
1314
                }
1315
1316
                if ($values !== []) {
1317
                    yield $values;
1318
                }
1319
            };
1320 View Code Duplication
        } else {
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...
1321 5
            $generator = function () use ($size) {
1322 5
                $values = [];
1323 5
                $tmpCounter = 0;
1324 5
                foreach ($this->getGenerator() as $value) {
1325 5
                    ++$tmpCounter;
1326
1327 5
                    $values[] = $value;
1328 5
                    if ($tmpCounter === $size) {
1329 5
                        yield $values;
1330
1331 5
                        $values = [];
1332 5
                        $tmpCounter = 0;
1333
                    }
1334
                }
1335
1336 5
                if ($values !== []) {
1337 4
                    yield $values;
1338
                }
1339 5
            };
1340
        }
1341
1342 5
        return static::create(
1343 5
            $generator,
1344 5
            $this->iteratorClass,
1345 5
            false
1346
        );
1347
    }
1348
1349
    /**
1350
     * Clean all falsy values from the current array.
1351
     *
1352
     * EXAMPLE: <code>
1353
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1354
     * </code>
1355
     *
1356
     * @return static
1357
     *                <p>(Immutable)</p>
1358
     *
1359
     * @phpstan-return static<TKey,T>
1360
     * @psalm-mutation-free
1361
     */
1362 8
    public function clean(): self
1363
    {
1364 8
        return $this->filter(
1365 8
            static function ($value) {
1366 7
                return (bool) $value;
1367 8
            }
1368
        );
1369
    }
1370
1371
    /**
1372
     * WARNING!!! -> Clear the current full array or a $key of it.
1373
     *
1374
     * EXAMPLE: <code>
1375
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1376
     * </code>
1377
     *
1378
     * @param int|int[]|string|string[]|null $key
1379
     *
1380
     * @return $this
1381
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1382
     *
1383
     * @phpstan-return static<TKey,T>
1384
     */
1385 10
    public function clear($key = null): self
1386
    {
1387 10
        if ($key !== null) {
1388 3
            if (\is_array($key)) {
1389 1
                foreach ($key as $keyTmp) {
1390 1
                    $this->offsetUnset($keyTmp);
1391
                }
1392
            } else {
1393 2
                $this->offsetUnset($key);
1394
            }
1395
1396 3
            return $this;
1397
        }
1398
1399 7
        $this->array = [];
1400 7
        $this->generator = null;
1401
1402 7
        return $this;
1403
    }
1404
1405
    /**
1406
     * Check if an item is in the current array.
1407
     *
1408
     * EXAMPLE: <code>
1409
     * a([1, true])->contains(true); // true
1410
     * </code>
1411
     *
1412
     * @param float|int|string $value
1413
     * @param bool             $recursive
1414
     * @param bool             $strict
1415
     *
1416
     * @return bool
1417
     * @psalm-mutation-free
1418
     */
1419 24
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1420
    {
1421 24
        if ($recursive === true) {
1422 19
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1423
        }
1424
1425
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1426 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1427 11
            if ($strict) {
1428 11
                if ($value === $valueFromArray) {
1429 11
                    return true;
1430
                }
1431
            } else {
1432
                /** @noinspection NestedPositiveIfStatementsInspection */
1433
                if ($value == $valueFromArray) {
1434
                    return true;
1435
                }
1436
            }
1437
        }
1438
1439 7
        return false;
1440
    }
1441
1442
    /**
1443
     * Check if an (case-insensitive) string is in the current array.
1444
     *
1445
     * EXAMPLE: <code>
1446
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1447
     * </code>
1448
     *
1449
     * @param mixed $value
1450
     * @param bool  $recursive
1451
     *
1452
     * @return bool
1453
     * @psalm-mutation-free
1454
     *
1455
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1456
     */
1457 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1458
    {
1459 26
        if ($value === null) {
1460 2
            return false;
1461
        }
1462
1463 24
        if ($recursive === true) {
1464
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1465 24
            foreach ($this->getGeneratorByReference() as &$valueTmp) {
1466 22
                if (\is_array($valueTmp)) {
1467 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1468 5
                    if ($return === true) {
1469 5
                        return $return;
1470
                    }
1471 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1472 16
                    return true;
1473
                }
1474
            }
1475
1476 8
            return false;
1477
        }
1478
1479
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1480 12
        foreach ($this->getGeneratorByReference() as &$valueTmp) {
1481 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1482 8
                return true;
1483
            }
1484
        }
1485
1486 4
        return false;
1487
    }
1488
1489
    /**
1490
     * Check if the given key/index exists in the array.
1491
     *
1492
     * EXAMPLE: <code>
1493
     * a([1 => true])->containsKey(1); // true
1494
     * </code>
1495
     *
1496
     * @param int|string $key <p>key/index to search for</p>
1497
     *
1498
     * @return bool
1499
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1500
     *
1501
     * @psalm-mutation-free
1502
     */
1503 4
    public function containsKey($key): bool
1504
    {
1505 4
        return $this->offsetExists($key);
1506
    }
1507
1508
    /**
1509
     * Check if all given needles are present in the array as key/index.
1510
     *
1511
     * EXAMPLE: <code>
1512
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1513
     * </code>
1514
     *
1515
     * @param array $needles   <p>The keys you are searching for.</p>
1516
     * @param bool  $recursive
1517
     *
1518
     * @return bool
1519
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1520
     *
1521
     * @phpstan-param array<array-key>|array<TKey> $needles
1522
     * @psalm-mutation-free
1523
     */
1524 2
    public function containsKeys(array $needles, $recursive = false): bool
1525
    {
1526 2
        if ($recursive === true) {
1527
            return
1528 2
                \count(
1529 2
                    \array_intersect(
1530 2
                        $needles,
1531 2
                        $this->keys(true)->toArray()
1532
                    ),
1533 2
                    \COUNT_RECURSIVE
1534
                )
1535
                ===
1536 2
                \count(
1537 2
                    $needles,
1538 2
                    \COUNT_RECURSIVE
1539
                );
1540
        }
1541
1542 1
        return \count(
1543 1
            \array_intersect($needles, $this->keys()->toArray()),
1544 1
            \COUNT_NORMAL
1545
        )
1546
                ===
1547 1
                \count(
1548 1
                    $needles,
1549 1
                    \COUNT_NORMAL
1550
                );
1551
    }
1552
1553
    /**
1554
     * Check if all given needles are present in the array as key/index.
1555
     *
1556
     * @param array $needles <p>The keys you are searching for.</p>
1557
     *
1558
     * @return bool
1559
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1560
     *
1561
     * @phpstan-param array<array-key>|array<TKey> $needles
1562
     * @psalm-mutation-free
1563
     */
1564 1
    public function containsKeysRecursive(array $needles): bool
1565
    {
1566 1
        return $this->containsKeys($needles, true);
1567
    }
1568
1569
    /**
1570
     * alias: for "Arrayy->contains()"
1571
     *
1572
     * @param float|int|string $value
1573
     *
1574
     * @return bool
1575
     *
1576
     * @see Arrayy::contains()
1577
     * @psalm-mutation-free
1578
     */
1579 9
    public function containsValue($value): bool
1580
    {
1581 9
        return $this->contains($value);
1582
    }
1583
1584
    /**
1585
     * alias: for "Arrayy->contains($value, true)"
1586
     *
1587
     * @param float|int|string $value
1588
     *
1589
     * @return bool
1590
     *
1591
     * @see Arrayy::contains()
1592
     * @psalm-mutation-free
1593
     */
1594 18
    public function containsValueRecursive($value): bool
1595
    {
1596 18
        return $this->contains($value, true);
1597
    }
1598
1599
    /**
1600
     * Check if all given needles are present in the array.
1601
     *
1602
     * EXAMPLE: <code>
1603
     * a([1, true])->containsValues(array(1, true)); // true
1604
     * </code>
1605
     *
1606
     * @param array $needles
1607
     *
1608
     * @return bool
1609
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1610
     *
1611
     * @phpstan-param array<mixed>|array<T> $needles
1612
     * @psalm-mutation-free
1613
     */
1614 1
    public function containsValues(array $needles): bool
1615
    {
1616 1
        return \count(
1617 1
            \array_intersect(
1618 1
                $needles,
1619 1
                $this->toArray()
1620
            ),
1621 1
            \COUNT_NORMAL
1622
        )
1623
               ===
1624 1
               \count(
1625 1
                   $needles,
1626 1
                   \COUNT_NORMAL
1627
               );
1628
    }
1629
1630
    /**
1631
     * Counts all the values of an array
1632
     *
1633
     * @see          http://php.net/manual/en/function.array-count-values.php
1634
     *
1635
     * @return static
1636
     *                <p>
1637
     *                (Immutable)
1638
     *                An associative Arrayy-object of values from input as
1639
     *                keys and their count as value.
1640
     *                </p>
1641
     *
1642
     * @phpstan-return static<array-key,int>
1643
     * @psalm-mutation-free
1644
     */
1645 7
    public function countValues(): self
1646
    {
1647 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1648
    }
1649
1650
    /**
1651
     * Creates an Arrayy object.
1652
     *
1653
     * @param mixed  $data
1654
     * @param string $iteratorClass
1655
     * @param bool   $checkPropertiesInConstructor
1656
     *
1657
     * @return static
1658
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1659
     *
1660
     * @phpstan-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1661
     * @phpstan-return static<TKey,T>
1662
     * @psalm-mutation-free
1663
     */
1664 732
    public static function create(
1665
        $data = [],
1666
        string $iteratorClass = ArrayyIterator::class,
1667
        bool $checkPropertiesInConstructor = true
1668
    ) {
1669 732
        return new static(
1670 732
            $data,
1671
            $iteratorClass,
1672
            $checkPropertiesInConstructor
1673
        );
1674
    }
1675
1676
    /**
1677
     * Flatten an array with the given character as a key delimiter.
1678
     *
1679
     * EXAMPLE: <code>
1680
     * $dot = a(['foo' => ['abc' => 'xyz', 'bar' => ['baz']]]);
1681
     * $flatten = $dot->flatten();
1682
     * $flatten['foo.abc']; // 'xyz'
1683
     * $flatten['foo.bar.0']; // 'baz'
1684
     * </code>
1685
     *
1686
     * @param string     $delimiter
1687
     * @param string     $prepend
1688
     * @param array|null $items
1689
     *
1690
     * @return array
1691
     */
1692 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1693
    {
1694
        // init
1695 2
        $flatten = [];
1696
1697 2
        if ($items === null) {
1698 2
            $items = $this->getArray();
1699
        }
1700
1701 2
        foreach ($items as $key => $value) {
1702 2
            if (\is_array($value) && $value !== []) {
1703 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1704
            } else {
1705 2
                $flatten[] = [$prepend . $key => $value];
1706
            }
1707
        }
1708
1709 2
        if (\count($flatten) === 0) {
1710
            return [];
1711
        }
1712
1713 2
        return \array_merge_recursive([], ...$flatten);
1714
    }
1715
1716
    /**
1717
     * WARNING: Creates an Arrayy object by reference.
1718
     *
1719
     * @param array $array
1720
     *
1721
     * @return $this
1722
     *               <p>(Mutable) Return this Arrayy object.</p>
1723
     *
1724
     * @phpstan-param  array<TKey,T> $array
1725
     * @phpstan-return $this<TKey,T>
1726
     *
1727
     * @internal this will not check any types because it's set directly as reference
1728
     */
1729 27
    public function createByReference(array &$array = []): self
1730
    {
1731 27
        $this->array = &$array;
1732 27
        $this->generator = null;
1733
1734 27
        return $this;
1735
    }
1736
1737
    /**
1738
     * Create an new instance from a callable function which will return an Generator.
1739
     *
1740
     * @param callable $generatorFunction
1741
     *
1742
     * @return static
1743
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1744
     *
1745
     * @phpstan-param callable():\Generator<TKey,T> $generatorFunction
1746
     * @phpstan-return static<TKey,T>
1747
     * @psalm-mutation-free
1748
     */
1749 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1750
    {
1751 7
        return self::create($generatorFunction);
1752
    }
1753
1754
    /**
1755
     * Create an new instance filled with a copy of values from a "Generator"-object.
1756
     *
1757
     * @param \Generator $generator
1758
     *
1759
     * @return static
1760
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1761
     *
1762
     * @phpstan-param \Generator<TKey,T> $generator
1763
     * @phpstan-return static<TKey,T>
1764
     * @psalm-mutation-free
1765
     */
1766 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1767
    {
1768 4
        return self::create(\iterator_to_array($generator, true));
1769
    }
1770
1771
    /**
1772
     * Create an new Arrayy object via JSON.
1773
     *
1774
     * @param string $json
1775
     *
1776
     * @return static
1777
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1778
     *
1779
     * @phpstan-return static<int|string,mixed>
1780
     * @psalm-mutation-free
1781
     */
1782 6
    public static function createFromJson(string $json): self
1783
    {
1784 6
        return static::create(\json_decode($json, true));
1785
    }
1786
1787
    /**
1788
     * Create an new Arrayy object via JSON.
1789
     *
1790
     * @param array $array
1791
     *
1792
     * @return static
1793
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1794
     *
1795
     * @phpstan-param array<TKey,T> $array
1796
     * @phpstan-return static<TKey,T>
1797
     * @psalm-mutation-free
1798
     */
1799 1
    public static function createFromArray(array $array): self
1800
    {
1801 1
        return static::create($array);
1802
    }
1803
1804
    /**
1805
     * Create an new instance filled with values from an object that is iterable.
1806
     *
1807
     * @param \Traversable $object <p>iterable object</p>
1808
     *
1809
     * @return static
1810
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1811
     *
1812
     * @phpstan-param \Traversable<array-key,T> $object
1813
     * @phpstan-return static<array-key,T>
1814
     * @psalm-mutation-free
1815
     */
1816 4
    public static function createFromObject(\Traversable $object): self
1817
    {
1818
        // init
1819 4
        $arrayy = new static();
1820
1821 4
        if ($object instanceof self) {
1822 4
            $objectArray = $object->getGenerator();
1823
        } else {
1824
            $objectArray = $object;
1825
        }
1826
1827 4
        foreach ($objectArray as $key => $value) {
1828
            /**
1829
             * @psalm-suppress ImpureMethodCall - object is already re-created
1830
             */
1831 3
            $arrayy->internalSet($key, $value);
1832
        }
1833
1834 4
        return $arrayy;
1835
    }
1836
1837
    /**
1838
     * Create an new instance filled with values from an object.
1839
     *
1840
     * @param object $object
1841
     *
1842
     * @return static
1843
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1844
     *
1845
     * @phpstan-return static<array-key,mixed>
1846
     * @psalm-mutation-free
1847
     */
1848 5
    public static function createFromObjectVars($object): self
1849
    {
1850 5
        return self::create(self::objectToArray($object));
1851
    }
1852
1853
    /**
1854
     * Create an new Arrayy object via string.
1855
     *
1856
     * @param string      $str       <p>The input string.</p>
1857
     * @param string|null $delimiter <p>The boundary string.</p>
1858
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1859
     *                               used.</p>
1860
     *
1861
     * @return static
1862
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1863
     *
1864
     * @phpstan-return static<int,string>
1865
     * @psalm-mutation-free
1866
     */
1867 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1868
    {
1869 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...
1870 1
            \preg_match_all($regEx, $str, $array);
1871
1872 1
            if (!empty($array)) {
1873 1
                $array = $array[0];
1874
            }
1875
        } else {
1876
            /** @noinspection NestedPositiveIfStatementsInspection */
1877 9
            if ($delimiter !== null) {
1878 7
                $array = \explode($delimiter, $str);
1879
            } else {
1880 2
                $array = [$str];
1881
            }
1882
        }
1883
1884
        // trim all string in the array
1885
        /**
1886
         * @psalm-suppress MissingClosureParamType
1887
         */
1888 10
        \array_walk(
1889 10
            $array,
1890 10
            static function (&$val) {
1891 10
                if ((string) $val === $val) {
1892 10
                    $val = \trim($val);
1893
                }
1894 10
            }
1895
        );
1896
1897 10
        return static::create($array);
1898
    }
1899
1900
    /**
1901
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1902
     *
1903
     * @param \Traversable $traversable
1904
     * @param bool         $use_keys    [optional] <p>
1905
     *                                  Whether to use the iterator element keys as index.
1906
     *                                  </p>
1907
     *
1908
     * @return static
1909
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1910
     *
1911
     * @phpstan-param \Traversable<array-key|TKey,T> $traversable
1912
     * @phpstan-return static<int|TKey,T>
1913
     * @psalm-mutation-free
1914
     */
1915 1
    public static function createFromTraversableImmutable(\Traversable $traversable, bool $use_keys = true): self
1916
    {
1917 1
        return self::create(\iterator_to_array($traversable, $use_keys));
1918
    }
1919
1920
    /**
1921
     * Create an new instance containing a range of elements.
1922
     *
1923
     * @param float|int|string $low  <p>First value of the sequence.</p>
1924
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1925
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1926
     *
1927
     * @return static
1928
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1929
     *
1930
     * @phpstan-return static<int,int|string>
1931
     * @psalm-mutation-free
1932
     */
1933 2
    public static function createWithRange($low, $high, $step = 1): self
1934
    {
1935 2
        return static::create(\range($low, $high, $step));
1936
    }
1937
1938
    /**
1939
     * Gets the element of the array at the current internal iterator position.
1940
     *
1941
     * @return false|mixed
1942
     *
1943
     * @phpstan-return false|T
1944
     */
1945
    public function current()
1946
    {
1947
        if ($this->generator) {
1948
            return $this->generator->current();
1949
        }
1950
1951
        return \current($this->array);
1952
    }
1953
1954
    /**
1955
     * Custom sort by index via "uksort".
1956
     *
1957
     * EXAMPLE: <code>
1958
     * $callable = function ($a, $b) {
1959
     *     if ($a == $b) {
1960
     *         return 0;
1961
     *     }
1962
     *     return ($a > $b) ? 1 : -1;
1963
     * };
1964
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1965
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1966
     * </code>
1967
     *
1968
     * @see          http://php.net/manual/en/function.uksort.php
1969
     *
1970
     * @param callable $function
1971
     *
1972
     * @throws \InvalidArgumentException
1973
     *
1974
     * @return $this
1975
     *               <p>(Mutable) Return this Arrayy object.</p>
1976
     *
1977
     * @phpstan-return static<TKey,T>
1978
     */
1979 5
    public function customSortKeys(callable $function): self
1980
    {
1981 5
        $this->generatorToArray();
1982
1983 5
        \uksort($this->array, $function);
1984
1985 5
        return $this;
1986
    }
1987
1988
    /**
1989
     * Custom sort by index via "uksort".
1990
     *
1991
     * @see          http://php.net/manual/en/function.uksort.php
1992
     *
1993
     * @param callable $function
1994
     *
1995
     * @throws \InvalidArgumentException
1996
     *
1997
     * @return $this
1998
     *               <p>(Immutable) Return this Arrayy object.</p>
1999
     *
2000
     * @phpstan-return static<TKey,T>
2001
     * @psalm-mutation-free
2002
     */
2003 1
    public function customSortKeysImmutable(callable $function): self
2004
    {
2005 1
        $that = clone $this;
2006
2007 1
        $that->generatorToArray();
2008
2009
        /**
2010
         * @psalm-suppress ImpureFunctionCall - object is already cloned
2011
         */
2012 1
        \uksort($that->array, $function);
2013
2014 1
        return $that;
2015
    }
2016
2017
    /**
2018
     * Custom sort by value via "usort".
2019
     *
2020
     * EXAMPLE: <code>
2021
     * $callable = function ($a, $b) {
2022
     *     if ($a == $b) {
2023
     *         return 0;
2024
     *     }
2025
     *     return ($a > $b) ? 1 : -1;
2026
     * };
2027
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
2028
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
2029
     * </code>
2030
     *
2031
     * @see          http://php.net/manual/en/function.usort.php
2032
     *
2033
     * @param callable $function
2034
     *
2035
     * @return $this
2036
     *               <p>(Mutable) Return this Arrayy object.</p>
2037
     *
2038
     * @phpstan-return static<TKey,T>
2039
     */
2040 10
    public function customSortValues(callable $function): self
2041
    {
2042 10
        $this->generatorToArray();
2043
2044 10
        \usort($this->array, $function);
2045
2046 10
        return $this;
2047
    }
2048
2049
    /**
2050
     * Custom sort by value via "usort".
2051
     *
2052
     * @see          http://php.net/manual/en/function.usort.php
2053
     *
2054
     * @param callable $function
2055
     *
2056
     * @throws \InvalidArgumentException
2057
     *
2058
     * @return $this
2059
     *               <p>(Immutable) Return this Arrayy object.</p>
2060
     *
2061
     * @phpstan-return static<TKey,T>
2062
     * @psalm-mutation-free
2063
     */
2064 4
    public function customSortValuesImmutable($function): self
2065
    {
2066 4
        $that = clone $this;
2067
2068
        /**
2069
         * @psalm-suppress ImpureMethodCall - object is already cloned
2070
         */
2071 4
        $that->customSortValues($function);
2072
2073 4
        return $that;
2074
    }
2075
2076
    /**
2077
     * Delete the given key or keys.
2078
     *
2079
     * @param int|int[]|string|string[] $keyOrKeys
2080
     *
2081
     * @return void
2082
     */
2083 9
    public function delete($keyOrKeys)
2084
    {
2085 9
        $keyOrKeys = (array) $keyOrKeys;
2086
2087 9
        foreach ($keyOrKeys as $key) {
2088 9
            $this->offsetUnset($key);
2089
        }
2090 9
    }
2091
2092
    /**
2093
     * Return elements where the values that are only in the current array.
2094
     *
2095
     * EXAMPLE: <code>
2096
     * a([1 => 1, 2 => 2])->diff([1 => 1]); // Arrayy[2 => 2]
2097
     * </code>
2098
     *
2099
     * @param array ...$array
2100
     *
2101
     * @return static
2102
     *                <p>(Immutable)</p>
2103
     *
2104
     * @phpstan-param  array<TKey,T> ...$array
2105
     * @phpstan-return static<TKey,T>
2106
     * @psalm-mutation-free
2107
     */
2108 13 View Code Duplication
    public function diff(array ...$array): 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...
2109
    {
2110 13
        if (\count($array) > 1) {
2111 1
            $array = \array_merge([], ...$array);
2112
        } else {
2113 13
            $array = $array[0];
2114
        }
2115
2116 13
        $generator = function () use ($array): \Generator {
2117 13
            foreach ($this->getGenerator() as $key => $value) {
2118 11
                if (\in_array($value, $array, true) === false) {
2119 5
                    yield $key => $value;
2120
                }
2121
            }
2122 13
        };
2123
2124 13
        return static::create(
2125 13
            $generator,
2126 13
            $this->iteratorClass,
2127 13
            false
2128
        );
2129
    }
2130
2131
    /**
2132
     * Return elements where the keys are only in the current array.
2133
     *
2134
     * @param array ...$array
2135
     *
2136
     * @return static
2137
     *                <p>(Immutable)</p>
2138
     *
2139
     * @phpstan-param  array<TKey,T> ...$array
2140
     * @phpstan-return static<TKey,T>
2141
     * @psalm-mutation-free
2142
     */
2143 9 View Code Duplication
    public function diffKey(array ...$array): 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...
2144
    {
2145 9
        if (\count($array) > 1) {
2146 1
            $array = \array_replace([], ...$array);
2147
        } else {
2148 8
            $array = $array[0];
2149
        }
2150
2151 9
        $generator = function () use ($array): \Generator {
2152 9
            foreach ($this->getGenerator() as $key => $value) {
2153 8
                if (\array_key_exists($key, $array) === false) {
2154 2
                    yield $key => $value;
2155
                }
2156
            }
2157 9
        };
2158
2159 9
        return static::create(
2160 9
            $generator,
2161 9
            $this->iteratorClass,
2162 9
            false
2163
        );
2164
    }
2165
2166
    /**
2167
     * Return elements where the values and keys are only in the current array.
2168
     *
2169
     * @param array ...$array
2170
     *
2171
     * @return static
2172
     *                <p>(Immutable)</p>
2173
     *
2174
     * @phpstan-param  array<TKey,T> $array
2175
     * @phpstan-return static<TKey,T>
2176
     * @psalm-mutation-free
2177
     */
2178 9
    public function diffKeyAndValue(array ...$array): self
2179
    {
2180 9
        if (\count($array) > 1) {
2181 1
            $array = \array_merge([], ...$array);
2182
        } else {
2183 8
            $array = $array[0];
2184
        }
2185
2186 9
        $generator = function () use ($array): \Generator {
2187 9
            foreach ($this->getGenerator() as $key => $value) {
2188 8
                $isset = isset($array[$key]);
2189
2190
                if (
2191 8
                    !$isset
2192
                    ||
2193 8
                    $array[$key] !== $value
2194
                ) {
2195 4
                    yield $key => $value;
2196
                }
2197
            }
2198 9
        };
2199
2200 9
        return static::create(
2201 9
            $generator,
2202 9
            $this->iteratorClass,
2203 9
            false
2204
        );
2205
    }
2206
2207
    /**
2208
     * Return elements where the values are only in the current multi-dimensional array.
2209
     *
2210
     * EXAMPLE: <code>
2211
     * a([1 => [1 => 1], 2 => [2 => 2]])->diffRecursive([1 => [1 => 1]]); // Arrayy[2 => [2 => 2]]
2212
     * </code>
2213
     *
2214
     * @param array                 $array
2215
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
2216
     *
2217
     * @return static
2218
     *                <p>(Immutable)</p>
2219
     *
2220
     * @phpstan-param  array<TKey,T> $array
2221
     * @phpstan-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2222
     * @phpstan-return static<TKey,T>
2223
     * @psalm-mutation-free
2224
     */
2225 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2226
    {
2227
        // init
2228 1
        $result = [];
2229
2230
        if (
2231 1
            $helperVariableForRecursion !== null
2232
            &&
2233 1
            \is_array($helperVariableForRecursion)
2234
        ) {
2235
            $arrayForTheLoop = $helperVariableForRecursion;
2236
        } else {
2237 1
            $arrayForTheLoop = $this->getGenerator();
2238
        }
2239
2240 1
        foreach ($arrayForTheLoop as $key => $value) {
2241 1
            if ($value instanceof self) {
2242 1
                $value = $value->toArray();
2243
            }
2244
2245 1
            if (\array_key_exists($key, $array)) {
2246 1
                if ($value !== $array[$key]) {
2247 1
                    $result[$key] = $value;
2248
                }
2249
            } else {
2250 1
                $result[$key] = $value;
2251
            }
2252
        }
2253
2254 1
        return static::create(
2255 1
            $result,
2256 1
            $this->iteratorClass,
2257 1
            false
2258
        );
2259
    }
2260
2261
    /**
2262
     * Return elements where the values that are only in the new $array.
2263
     *
2264
     * EXAMPLE: <code>
2265
     * a([1 => 1])->diffReverse([1 => 1, 2 => 2]); // Arrayy[2 => 2]
2266
     * </code>
2267
     *
2268
     * @param array $array
2269
     *
2270
     * @return static
2271
     *                <p>(Immutable)</p>
2272
     *
2273
     * @phpstan-param  array<TKey,T> $array
2274
     * @phpstan-return static<TKey,T>
2275
     * @psalm-mutation-free
2276
     */
2277 8
    public function diffReverse(array $array = []): self
2278
    {
2279 8
        return static::create(
2280 8
            \array_diff($array, $this->toArray()),
2281 8
            $this->iteratorClass,
2282 8
            false
2283
        );
2284
    }
2285
2286
    /**
2287
     * Divide an array into two arrays. One with keys and the other with values.
2288
     *
2289
     * EXAMPLE: <code>
2290
     * a(['a' => 1, 'b' => ''])->divide(); // Arrayy[Arrayy['a', 'b'], Arrayy[1, '']]
2291
     * </code>
2292
     *
2293
     * @return static
2294
     *                <p>(Immutable)</p>
2295
     *
2296
     * @phpstan-return static<TKey,T>
2297
     * @psalm-mutation-free
2298
     */
2299 1
    public function divide(): self
2300
    {
2301 1
        return static::create(
2302
            [
2303 1
                $this->keys(),
2304 1
                $this->values(),
2305
            ],
2306 1
            $this->iteratorClass,
2307 1
            false
2308
        );
2309
    }
2310
2311
    /**
2312
     * Iterate over the current array and modify the array's value.
2313
     *
2314
     * EXAMPLE: <code>
2315
     * $result = A::create();
2316
     * $closure = function ($value) {
2317
     *     return ':' . $value . ':';
2318
     * };
2319
     * a(['foo', 'bar' => 'bis'])->each($closure); // Arrayy[':foo:', 'bar' => ':bis:']
2320
     * </code>
2321
     *
2322
     * @param \Closure $closure
2323
     *
2324
     * @return static
2325
     *                <p>(Immutable)</p>
2326
     *
2327
     * @phpstan-param \Closure(T=):T|\Closure(T=,TKey=):T $closure
2328
     * @phpstan-return static<TKey,T>
2329
     * @psalm-mutation-free
2330
     */
2331 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...
2332
    {
2333
        // init
2334 5
        $array = [];
2335
2336 5
        foreach ($this->getGenerator() as $key => $value) {
2337 5
            $array[$key] = $closure($value, $key);
2338
        }
2339
2340 5
        return static::create(
2341 5
            $array,
2342 5
            $this->iteratorClass,
2343 5
            false
2344
        );
2345
    }
2346
2347
    /**
2348
     * Sets the internal iterator to the last element in the array and returns this element.
2349
     *
2350
     * @return false|mixed
2351
     *
2352
     * @phpstan-return T|false
2353
     */
2354
    public function end()
2355
    {
2356
        if ($this->generator) {
2357
            $count = $this->count();
2358
            if ($count === 0) {
2359
                return false;
2360
            }
2361
2362
            $counter = 0;
2363
            /** @noinspection PhpUnusedLocalVariableInspection */
2364
            foreach ($this->getIterator() as $item) {
2365
                if (++$counter === $count - 1) {
2366
                    break;
2367
                }
2368
            }
2369
        }
2370
2371
        return \end($this->array);
2372
    }
2373
2374
    /**
2375
     * Check if a value is in the current array using a closure.
2376
     *
2377
     * EXAMPLE: <code>
2378
     * $callable = function ($value, $key) {
2379
     *     return 2 === $key and 'two' === $value;
2380
     * };
2381
     * a(['foo', 2 => 'two'])->exists($callable); // true
2382
     * </code>
2383
     *
2384
     * @param \Closure $closure
2385
     *
2386
     * @return bool
2387
     *              <p>Returns true if the given value is found, false otherwise.</p>
2388
     *
2389
     * @phpstan-param \Closure(T=,TKey=):bool $closure
2390
     */
2391 4
    public function exists(\Closure $closure): bool
2392
    {
2393
        // init
2394 4
        $isExists = false;
2395
2396 4
        foreach ($this->getGenerator() as $key => $value) {
2397 3
            if ($closure($value, $key)) {
2398 1
                $isExists = true;
2399
2400 1
                break;
2401
            }
2402
        }
2403
2404 4
        return $isExists;
2405
    }
2406
2407
    /**
2408
     * Fill the array until "$num" with "$default" values.
2409
     *
2410
     * EXAMPLE: <code>
2411
     * a(['bar'])->fillWithDefaults(3, 'foo'); // Arrayy['bar', 'foo', 'foo']
2412
     * </code>
2413
     *
2414
     * @param int   $num
2415
     * @param mixed $default
2416
     *
2417
     * @return static
2418
     *                <p>(Immutable)</p>
2419
     *
2420
     * @phpstan-param T $default
2421
     * @phpstan-return static<TKey,T>
2422
     * @psalm-mutation-free
2423
     */
2424 8
    public function fillWithDefaults(int $num, $default = null): self
2425
    {
2426 8
        if ($num < 0) {
2427 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2428
        }
2429
2430 7
        $this->generatorToArray();
2431
2432 7
        $tmpArray = $this->array;
2433
2434 7
        $count = \count($tmpArray);
2435
2436 7
        while ($count < $num) {
2437 4
            $tmpArray[] = $default;
2438 4
            ++$count;
2439
        }
2440
2441 7
        return static::create(
2442 7
            $tmpArray,
2443 7
            $this->iteratorClass,
2444 7
            false
2445
        );
2446
    }
2447
2448
    /**
2449
     * Find all items in an array that pass the truth test.
2450
     *
2451
     * EXAMPLE: <code>
2452
     * $closure = function ($value) {
2453
     *     return $value % 2 !== 0;
2454
     * }
2455
     * a([1, 2, 3, 4])->filter($closure); // Arrayy[0 => 1, 2 => 3]
2456
     * </code>
2457
     *
2458
     * @param \Closure|null $closure [optional] <p>
2459
     *                               The callback function to use
2460
     *                               </p>
2461
     *                               <p>
2462
     *                               If no callback is supplied, all entries of
2463
     *                               input equal to false (see
2464
     *                               converting to
2465
     *                               boolean) will be removed.
2466
     *                               </p>
2467
     * @param int           $flag    [optional] <p>
2468
     *                               Flag determining what arguments are sent to <i>callback</i>:
2469
     *                               </p>
2470
     *                               <ul>
2471
     *                               <li>
2472
     *                               <b>ARRAY_FILTER_USE_KEY</b> (1) - pass key as the only argument
2473
     *                               to <i>callback</i> instead of the value
2474
     *                               </li>
2475
     *                               <li>
2476
     *                               <b>ARRAY_FILTER_USE_BOTH</b> (2) - pass both value and key as
2477
     *                               arguments to <i>callback</i> instead of the value
2478
     *                               </li>
2479
     *                               </ul>
2480
     *
2481
     * @return static
2482
     *                <p>(Immutable)</p>
2483
     *
2484
     * @phpstan-param null|(\Closure(T=,TKey=):bool)|(\Closure(T=):bool)|(\Closure(TKey=):bool) $closure
2485
     * @phpstan-return static<TKey,T>
2486
     * @psalm-mutation-free
2487
     */
2488 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2489
    {
2490 12
        if (!$closure) {
2491 1
            return $this->clean();
2492
        }
2493
2494 12
        if ($flag === \ARRAY_FILTER_USE_KEY) {
2495 1
            $generator = function () use ($closure) {
2496 1
                foreach ($this->getGenerator() as $key => $value) {
2497 1
                    if ($closure($key) === true) {
2498 1
                        yield $key => $value;
2499
                    }
2500
                }
2501 1
            };
2502 12
        } elseif ($flag === \ARRAY_FILTER_USE_BOTH) {
2503
            /** @noinspection PhpSillyAssignmentInspection - hack for phpstan - https://github.com/phpstan/phpstan/issues/4192 */
2504
            /** @phpstan-var \Closure(T=,TKey=):bool $closure */
2505 12
            $closure = $closure;
0 ignored issues
show
Bug introduced by
Why assign $closure to itself?

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

This assignement can be removed without consequences.

Loading history...
2506
2507 12
            $generator = function () use ($closure) {
2508 11
                foreach ($this->getGenerator() as $key => $value) {
2509 10
                    if ($closure($value, $key) === true) {
2510 9
                        yield $key => $value;
2511
                    }
2512
                }
2513 11
            };
2514
        } else {
2515 1
            $generator = function () use ($closure) {
2516 1
                foreach ($this->getGenerator() as $key => $value) {
2517 1
                    if ($closure($value) === true) {
2518 1
                        yield $key => $value;
2519
                    }
2520
                }
2521 1
            };
2522
        }
2523
2524 12
        return static::create(
2525 12
            $generator,
2526 12
            $this->iteratorClass,
2527 12
            false
2528
        );
2529
    }
2530
2531
    /**
2532
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2533
     * property within that.
2534
     *
2535
     * @param string      $property
2536
     * @param mixed       $value
2537
     * @param string|null $comparisonOp
2538
     *                                  <p>
2539
     *                                  'eq' (equals),<br />
2540
     *                                  'gt' (greater),<br />
2541
     *                                  'gte' || 'ge' (greater or equals),<br />
2542
     *                                  'lt' (less),<br />
2543
     *                                  'lte' || 'le' (less or equals),<br />
2544
     *                                  'ne' (not equals),<br />
2545
     *                                  'contains',<br />
2546
     *                                  'notContains',<br />
2547
     *                                  'newer' (via strtotime),<br />
2548
     *                                  'older' (via strtotime),<br />
2549
     *                                  </p>
2550
     *
2551
     * @return static
2552
     *                <p>(Immutable)</p>
2553
     *
2554
     * @phpstan-param array|T $value
2555
     * @phpstan-return static<TKey,T>
2556
     * @psalm-mutation-free
2557
     *
2558
     * @psalm-suppress MissingClosureReturnType
2559
     * @psalm-suppress MissingClosureParamType
2560
     */
2561 1
    public function filterBy(
2562
        string $property,
2563
        $value,
2564
        string $comparisonOp = null
2565
    ): self {
2566 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...
2567 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
2568
        }
2569
2570 1
        $ops = [
2571 1
            'eq' => static function ($item, $prop, $value): bool {
2572 1
                return $item[$prop] === $value;
2573 1
            },
2574 1
            'gt' => static function ($item, $prop, $value): bool {
2575
                return $item[$prop] > $value;
2576 1
            },
2577 1
            'ge' => static function ($item, $prop, $value): bool {
2578
                return $item[$prop] >= $value;
2579 1
            },
2580 1
            'gte' => static function ($item, $prop, $value): bool {
2581
                return $item[$prop] >= $value;
2582 1
            },
2583 1
            'lt' => static function ($item, $prop, $value): bool {
2584 1
                return $item[$prop] < $value;
2585 1
            },
2586 1
            'le' => static function ($item, $prop, $value): bool {
2587
                return $item[$prop] <= $value;
2588 1
            },
2589 1
            'lte' => static function ($item, $prop, $value): bool {
2590
                return $item[$prop] <= $value;
2591 1
            },
2592 1
            'ne' => static function ($item, $prop, $value): bool {
2593
                return $item[$prop] !== $value;
2594 1
            },
2595 1
            'contains' => static function ($item, $prop, $value): bool {
2596 1
                return \in_array($item[$prop], (array) $value, true);
2597 1
            },
2598 1
            'notContains' => static function ($item, $prop, $value): bool {
2599
                return !\in_array($item[$prop], (array) $value, true);
2600 1
            },
2601 1
            'newer' => static function ($item, $prop, $value): bool {
2602
                return \strtotime($item[$prop]) > \strtotime($value);
2603 1
            },
2604 1
            'older' => static function ($item, $prop, $value): bool {
2605
                return \strtotime($item[$prop]) < \strtotime($value);
2606 1
            },
2607
        ];
2608
2609 1
        $result = \array_values(
2610 1
            \array_filter(
2611 1
                $this->toArray(false, true),
2612 1
                static function ($item) use (
2613 1
                    $property,
2614 1
                    $value,
2615 1
                    $ops,
2616 1
                    $comparisonOp
2617
                ) {
2618 1
                    $item = (array) $item;
2619 1
                    $itemArrayy = static::create($item);
2620 1
                    $item[$property] = $itemArrayy->get($property, []);
2621
2622 1
                    return $ops[$comparisonOp]($item, $property, $value);
2623 1
                }
2624
            )
2625
        );
2626
2627 1
        return static::create(
2628 1
            $result,
2629 1
            $this->iteratorClass,
2630 1
            false
2631
        );
2632
    }
2633
2634
    /**
2635
     * Find the first item in an array that passes the truth test, otherwise return false.
2636
     *
2637
     * EXAMPLE: <code>
2638
     * $search = 'foo';
2639
     * $closure = function ($value, $key) use ($search) {
2640
     *     return $value === $search;
2641
     * };
2642
     * a(['foo', 'bar', 'lall'])->find($closure); // 'foo'
2643
     * </code>
2644
     *
2645
     * @param \Closure $closure
2646
     *
2647
     * @return false|mixed
2648
     *                     <p>Return false if we did not find the value.</p>
2649
     *
2650
     * @phpstan-param \Closure(T=,TKey=):bool $closure
2651
     * @phpstan-return T|false
2652
     */
2653 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...
2654
    {
2655 8
        foreach ($this->getGenerator() as $key => $value) {
2656 6
            if ($closure($value, $key)) {
2657 5
                return $value;
2658
            }
2659
        }
2660
2661 3
        return false;
2662
    }
2663
2664
    /**
2665
     * find by ...
2666
     *
2667
     * EXAMPLE: <code>
2668
     * $array = [
2669
     *     0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01'],
2670
     *     1 => ['id' => 456, 'name' => 'bar', 'group' => 'primary', 'value' => 1468, 'when' => '2014-07-15'],
2671
     * ];
2672
     * a($array)->filterBy('name', 'foo'); // Arrayy[0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01']]
2673
     * </code>
2674
     *
2675
     * @param string $property
2676
     * @param mixed  $value
2677
     * @param string $comparisonOp
2678
     *
2679
     * @return static
2680
     *                <p>(Immutable)</p>
2681
     *
2682
     * @phpstan-param array|T $value
2683
     * @phpstan-return static<TKey,T>
2684
     * @psalm-mutation-free
2685
     */
2686 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2687
    {
2688 1
        return $this->filterBy($property, $value, $comparisonOp);
2689
    }
2690
2691
    /**
2692
     * Get the first value from the current array.
2693
     *
2694
     * EXAMPLE: <code>
2695
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->first(); // 'foo'
2696
     * </code>
2697
     *
2698
     * @return mixed|null
2699
     *                    <p>Return null if there wasn't a element.</p>
2700
     *
2701
     * @phpstan-return T|null
2702
     * @psalm-mutation-free
2703
     */
2704 23
    public function first()
2705
    {
2706 23
        $key_first = $this->firstKey();
2707 23
        if ($key_first === null) {
2708 3
            return null;
2709
        }
2710
2711 20
        return $this->get($key_first);
2712
    }
2713
2714
    /**
2715
     * Get the first key from the current array.
2716
     *
2717
     * @return mixed|null
2718
     *                    <p>Return null if there wasn't a element.</p>
2719
     *
2720
     * @phpstan-return TKey|null
2721
     *
2722
     * @psalm-mutation-free
2723
     */
2724 30
    public function firstKey()
2725
    {
2726 30
        $this->generatorToArray();
2727
2728
        /** @phpstan-var TKey|null $return */
2729 30
        $return = \array_key_first($this->array);
2730
2731 30
        return $return;
2732
    }
2733
2734
    /**
2735
     * Get the first value(s) from the current array.
2736
     * And will return an empty array if there was no first entry.
2737
     *
2738
     * EXAMPLE: <code>
2739
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsImmutable(2); // Arrayy[0 => 'foo', 1 => 'bar']
2740
     * </code>
2741
     *
2742
     * @param int|null $number <p>How many values you will take?</p>
2743
     *
2744
     * @return static
2745
     *                <p>(Immutable)</p>
2746
     *
2747
     * @phpstan-return static<TKey,T>
2748
     * @psalm-mutation-free
2749
     */
2750 37
    public function firstsImmutable(int $number = null): self
2751
    {
2752 37
        $arrayTmp = $this->toArray();
2753
2754 37
        if ($number === null) {
2755 14
            $array = (array) \array_shift($arrayTmp);
2756
        } else {
2757 23
            $array = \array_splice($arrayTmp, 0, $number);
2758
        }
2759
2760 37
        return static::create(
2761 37
            $array,
2762 37
            $this->iteratorClass,
2763 37
            false
2764
        );
2765
    }
2766
2767
    /**
2768
     * Get the first value(s) from the current array.
2769
     * And will return an empty array if there was no first entry.
2770
     *
2771
     * @param int|null $number <p>How many values you will take?</p>
2772
     *
2773
     * @return static
2774
     *                <p>(Immutable)</p>
2775
     *
2776
     * @phpstan-return static<array-key,TKey>
2777
     * @psalm-mutation-free
2778
     */
2779 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...
2780
    {
2781 3
        $arrayTmp = $this->keys()->toArray();
2782
2783 3
        if ($number === null) {
2784
            $array = (array) \array_shift($arrayTmp);
2785
        } else {
2786 3
            $array = \array_splice($arrayTmp, 0, $number);
2787
        }
2788
2789 3
        return static::create(
2790 3
            $array,
2791 3
            $this->iteratorClass,
2792 3
            false
2793
        );
2794
    }
2795
2796
    /**
2797
     * Get and remove the first value(s) from the current array.
2798
     * And will return an empty array if there was no first entry.
2799
     *
2800
     * EXAMPLE: <code>
2801
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsMutable(); // 'foo'
2802
     * </code>
2803
     *
2804
     * @param int|null $number <p>How many values you will take?</p>
2805
     *
2806
     * @return $this
2807
     *               <p>(Mutable)</p>
2808
     *
2809
     * @phpstan-return static<TKey,T>
2810
     */
2811 34
    public function firstsMutable(int $number = null): self
2812
    {
2813 34
        $this->generatorToArray();
2814
2815 34
        if ($number === null) {
2816 19
            $this->array = (array) \array_shift($this->array);
2817
        } else {
2818 15
            $this->array = \array_splice($this->array, 0, $number);
2819
        }
2820
2821 34
        return $this;
2822
    }
2823
2824
    /**
2825
     * Exchanges all keys with their associated values in an array.
2826
     *
2827
     * EXAMPLE: <code>
2828
     * a([0 => 'foo', 1 => 'bar'])->flip(); // Arrayy['foo' => 0, 'bar' => 1]
2829
     * </code>
2830
     *
2831
     * @return static
2832
     *                <p>(Immutable)</p>
2833
     *
2834
     * @phpstan-return static<array-key,TKey>
2835
     * @psalm-mutation-free
2836
     */
2837 1
    public function flip(): self
2838
    {
2839 1
        $generator = function (): \Generator {
2840 1
            foreach ($this->getGenerator() as $key => $value) {
2841 1
                yield (string) $value => $key;
2842
            }
2843 1
        };
2844
2845 1
        return static::create(
2846 1
            $generator,
2847 1
            $this->iteratorClass,
2848 1
            false
2849
        );
2850
    }
2851
2852
    /**
2853
     * Get a value from an array (optional using dot-notation).
2854
     *
2855
     * EXAMPLE: <code>
2856
     * $arrayy = a(['user' => ['lastname' => 'Moelleken']]);
2857
     * $arrayy->get('user.lastname'); // 'Moelleken'
2858
     * // ---
2859
     * $arrayy = new A();
2860
     * $arrayy['user'] = ['lastname' => 'Moelleken'];
2861
     * $arrayy['user.firstname'] = 'Lars';
2862
     * $arrayy['user']['lastname']; // Moelleken
2863
     * $arrayy['user.lastname']; // Moelleken
2864
     * $arrayy['user.firstname']; // Lars
2865
     * </code>
2866
     *
2867
     * @param int|string $key
2868
     *                                   <p>The key to look for.</p>
2869
     * @param mixed      $fallback
2870
     *                                   <p>Value to fallback to.</p>
2871
     * @param array|null $array
2872
     *                                   <p>The array to get from, if it's set to "null" we use the current array from the
2873
     *                                   class.</p>
2874
     * @param bool       $useByReference
2875
     *
2876
     * @return mixed|static
2877
     *
2878
     * @phpstan-param array-key $key
2879
     * @phpstan-param array<array-key,mixed>|array<TKey,T> $array
2880
     * @psalm-mutation-free
2881
     */
2882 248
    public function get(
2883
        $key = null,
2884
        $fallback = null,
2885
        array $array = null,
2886
        bool $useByReference = false
2887
    ) {
2888 248
        if ($array === null && $key === null) {
2889 1
            if ($useByReference) {
2890
                return $this;
2891
            }
2892
2893 1
            return clone $this;
2894
        }
2895
2896 248
        if ($array !== null) {
2897 4
            if ($useByReference) {
2898
                $usedArray = &$array;
2899
            } else {
2900 4
                $usedArray = $array;
2901
            }
2902
        } else {
2903 245
            $this->generatorToArray();
2904
2905 245
            if ($useByReference) {
2906 133
                $usedArray = &$this->array;
2907
            } else {
2908 131
                $usedArray = $this->array;
2909
            }
2910
        }
2911
2912 248
        if ($key === null) {
2913 1
            return static::create(
2914 1
                [],
2915 1
                $this->iteratorClass,
2916 1
                false
2917 1
            )->createByReference($usedArray);
2918
        }
2919
2920
        // php cast "bool"-index into "int"-index
2921
        /** @phpstan-ignore-next-line | this is only a fallback */
2922 248
        if ((bool) $key === $key) {
2923 3
            $key = (int) $key;
2924
        }
2925
2926 248
        if (\array_key_exists($key, $usedArray) === true) {
2927 210
            if (\is_array($usedArray[$key])) {
2928 19
                return static::create(
2929 19
                    [],
2930 19
                    $this->iteratorClass,
2931 19
                    false
2932 19
                )->createByReference($usedArray[$key]);
2933
            }
2934
2935 196
            return $usedArray[$key];
2936
        }
2937
2938
        // crawl through array, get key according to object or not
2939 61
        $usePath = false;
2940
        if (
2941 61
            $this->pathSeparator
2942
            &&
2943 61
            (string) $key === $key
2944
            &&
2945 61
            \strpos($key, $this->pathSeparator) !== false
2946
        ) {
2947 31
            $segments = \explode($this->pathSeparator, (string) $key);
2948 31
            if ($segments !== false) {
2949 31
                $usePath = true;
2950 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2951
2952 31
                foreach ($segments as $segment) {
2953
                    if (
2954
                        (
2955 31
                            \is_array($usedArrayTmp)
2956
                            ||
2957 31
                            $usedArrayTmp instanceof \ArrayAccess
2958
                        )
2959
                        &&
2960 31
                        isset($usedArrayTmp[$segment])
2961
                    ) {
2962 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2963
2964 30
                        continue;
2965
                    }
2966
2967
                    if (
2968 14
                        \is_object($usedArrayTmp) === true
2969
                        &&
2970 14
                        \property_exists($usedArrayTmp, $segment)
2971
                    ) {
2972 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2973
2974 1
                        continue;
2975
                    }
2976
2977 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2978 1
                        $segmentsTmp = $segments;
2979 1
                        unset($segmentsTmp[0]);
2980 1
                        $keyTmp = \implode('.', $segmentsTmp);
2981 1
                        $returnTmp = static::create(
2982 1
                            [],
2983 1
                            $this->iteratorClass,
2984 1
                            false
2985
                        );
2986 1
                        foreach ($this->getAll() as $dataTmp) {
2987 1
                            if ($dataTmp instanceof self) {
2988
                                $returnTmp->add($dataTmp->get($keyTmp));
2989
2990
                                continue;
2991
                            }
2992
2993
                            if (
2994
                                (
2995 1
                                    \is_array($dataTmp)
2996
                                    ||
2997 1
                                    $dataTmp instanceof \ArrayAccess
2998
                                )
2999
                                &&
3000 1
                                isset($dataTmp[$keyTmp])
3001
                            ) {
3002
                                $returnTmp->add($dataTmp[$keyTmp]);
3003
3004
                                continue;
3005
                            }
3006
3007
                            if (
3008 1
                                \is_object($dataTmp) === true
3009
                                &&
3010 1
                                \property_exists($dataTmp, $keyTmp)
3011
                            ) {
3012 1
                                $returnTmp->add($dataTmp->{$keyTmp});
3013
3014 1
                                continue;
3015
                            }
3016
                        }
3017
3018 1
                        if ($returnTmp->count() > 0) {
3019 1
                            return $returnTmp;
3020
                        }
3021
                    }
3022
3023 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
3024
                }
3025
            }
3026
        }
3027
3028 58
        if (isset($usedArrayTmp)) {
3029 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...
3030
                return $fallback instanceof \Closure ? $fallback() : $fallback;
3031
            }
3032
3033 28
            if (\is_array($usedArrayTmp)) {
3034 6
                return static::create(
3035 6
                    [],
3036 6
                    $this->iteratorClass,
3037 6
                    false
3038 6
                )->createByReference($usedArrayTmp);
3039
            }
3040
3041 28
            return $usedArrayTmp;
3042
        }
3043
3044 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...
3045 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
3046
        }
3047
3048
        return static::create(
3049
            [],
3050
            $this->iteratorClass,
3051
            false
3052
        )->createByReference($usedArray);
3053
    }
3054
3055
    /**
3056
     * alias: for "Arrayy->toArray()"
3057
     *
3058
     * @return array
3059
     *
3060
     * @see          Arrayy::getArray()
3061
     *
3062
     * @phpstan-return array<TKey,T>
3063
     */
3064 15
    public function getAll(): array
3065
    {
3066 15
        return $this->toArray();
3067
    }
3068
3069
    /**
3070
     * Get the current array from the "Arrayy"-object.
3071
     *
3072
     * alias for "toArray()"
3073
     *
3074
     * @param bool $convertAllArrayyElements <p>
3075
     *                                       Convert all Child-"Arrayy" objects also to arrays.
3076
     *                                       </p>
3077
     * @param bool $preserveKeys             <p>
3078
     *                                       e.g.: A generator maybe return the same key more then once,
3079
     *                                       so maybe you will ignore the keys.
3080
     *                                       </p>
3081
     *
3082
     * @return array
3083
     *
3084
     * @phpstan-return array<TKey,T>
3085
     * @psalm-mutation-free
3086
     *
3087
     * @see Arrayy::toArray()
3088
     */
3089 514
    public function getArray(
3090
        bool $convertAllArrayyElements = false,
3091
        bool $preserveKeys = true
3092
    ): array {
3093 514
        return $this->toArray(
3094 514
            $convertAllArrayyElements,
3095
            $preserveKeys
3096
        );
3097
    }
3098
3099
    /**
3100
     * @param string $json
3101
     *
3102
     * @return $this
3103
     */
3104 3
    public static function createFromJsonMapper(string $json)
3105
    {
3106
        // init
3107 3
        $class = static::create();
3108
3109 3
        $jsonObject = \json_decode($json, false);
3110
3111 3
        $mapper = new \Arrayy\Mapper\Json();
3112 3 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...
3113
            if ($class->checkPropertiesMismatchInConstructor) {
3114
                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...
3115
            }
3116
        };
3117
3118 3
        return $mapper->map($jsonObject, $class);
3119
    }
3120
3121
    /**
3122
     * @return array<array-key,TypeCheckInterface>|TypeCheckArray<array-key,TypeCheckInterface>
0 ignored issues
show
Documentation introduced by
The doc-type array<array-key,TypeChec...key,TypeCheckInterface> could not be parsed: Unknown type name "array-key" 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...
3123
     *
3124
     * @internal
3125
     */
3126 6
    public function getPhpDocPropertiesFromClass()
3127
    {
3128 6
        if ($this->properties === []) {
3129 1
            $this->properties = $this->getPropertiesFromPhpDoc();
3130
        }
3131
3132 6
        return $this->properties;
3133
    }
3134
3135
    /**
3136
     * Get the current array from the "Arrayy"-object as list.
3137
     *
3138
     * alias for "toList()"
3139
     *
3140
     * @param bool $convertAllArrayyElements <p>
3141
     *                                       Convert all Child-"Arrayy" objects also to arrays.
3142
     *                                       </p>
3143
     *
3144
     * @return array
3145
     *
3146
     * @phpstan-return list<T>
3147
     * @psalm-mutation-free
3148
     *
3149
     * @see Arrayy::toList()
3150
     */
3151 1
    public function getList(bool $convertAllArrayyElements = false): array
3152
    {
3153 1
        return $this->toList($convertAllArrayyElements);
3154
    }
3155
3156
    /**
3157
     * Returns the values from a single column of the input array, identified by
3158
     * the $columnKey, can be used to extract data-columns from multi-arrays.
3159
     *
3160
     * EXAMPLE: <code>
3161
     * a([['foo' => 'bar', 'id' => 1], ['foo => 'lall', 'id' => 2]])->getColumn('foo', 'id'); // Arrayy[1 => 'bar', 2 => 'lall']
3162
     * </code>
3163
     *
3164
     * INFO: Optionally, you may provide an $indexKey to index the values in the returned
3165
     *       array by the values from the $indexKey column in the input array.
3166
     *
3167
     * @param int|string|null $columnKey
3168
     * @param int|string|null $indexKey
3169
     *
3170
     * @return static
3171
     *                <p>(Immutable)</p>
3172
     *
3173
     * @phpstan-return static<TKey,T>
3174
     * @psalm-mutation-free
3175
     */
3176 1
    public function getColumn($columnKey = null, $indexKey = null): self
3177
    {
3178 1
        if ($columnKey === null && $indexKey === null) {
3179 1
            $generator = function () {
3180 1
                foreach ($this->getGenerator() as $value) {
3181 1
                    yield $value;
3182
                }
3183 1
            };
3184
        } else {
3185 1
            $generator = function () use ($columnKey, $indexKey) {
3186 1
                foreach ($this->getGenerator() as $value) {
3187
                    // reset
3188 1
                    $newKey = null;
3189 1
                    $newValue = null;
3190 1
                    $newValueFound = false;
3191
3192 1
                    if ($indexKey !== null) {
3193 1
                        foreach ($value as $keyInner => $valueInner) {
3194 1
                            if ($indexKey === $keyInner) {
3195 1
                                $newKey = $valueInner;
3196
                            }
3197
3198 1
                            if ($columnKey === $keyInner) {
3199 1
                                $newValue = $valueInner;
3200 1
                                $newValueFound = true;
3201
                            }
3202
                        }
3203
                    } else {
3204 1
                        foreach ($value as $keyInner => $valueInner) {
3205 1
                            if ($columnKey === $keyInner) {
3206 1
                                $newValue = $valueInner;
3207 1
                                $newValueFound = true;
3208
                            }
3209
                        }
3210
                    }
3211
3212 1
                    if ($newValueFound === false) {
3213 1
                        if ($newKey !== null) {
3214 1
                            yield $newKey => $value;
3215
                        } else {
3216 1
                            yield $value;
3217
                        }
3218
                    } else {
3219
                        /** @noinspection NestedPositiveIfStatementsInspection */
3220 1
                        if ($newKey !== null) {
3221 1
                            yield $newKey => $newValue;
3222
                        } else {
3223 1
                            yield $newValue;
3224
                        }
3225
                    }
3226
                }
3227 1
            };
3228
        }
3229
3230 1
        return static::create(
3231 1
            $generator,
3232 1
            $this->iteratorClass,
3233 1
            false
3234
        );
3235
    }
3236
3237
    /**
3238
     * Get the current array from the "Arrayy"-object as generator by reference.
3239
     *
3240
     * @return \Generator
3241
     *
3242
     * @phpstan-return \Generator<mixed,T>|\Generator<TKey,T>
3243
     */
3244 75
    public function &getGeneratorByReference(): \Generator
3245
    {
3246 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3247 17
            foreach ($this->generator as $key => $value) {
3248 17
                yield $key => $value;
3249
            }
3250
3251 5
            return;
3252
        }
3253
3254
        // -> false-positive -> see "&$value"
3255
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3256 59
        foreach ($this->array as $key => &$value) {
3257 54
            yield $key => $value;
3258
        }
3259 35
    }
3260
3261
    /**
3262
     * Get the current array from the "Arrayy"-object as generator.
3263
     *
3264
     * @return \Generator
3265
     *
3266
     * @phpstan-return \Generator<mixed,T>|\Generator<TKey,T>
3267
     * @psalm-mutation-free
3268
     */
3269 1074
    public function getGenerator(): \Generator
3270
    {
3271 1074
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3272 77
            yield from $this->generator;
3273
3274 77
            return;
3275
        }
3276
3277 1072
        yield from $this->array;
3278 1031
    }
3279
3280
    /**
3281
     * alias: for "Arrayy->keys()"
3282
     *
3283
     * @return static
3284
     *                <p>(Immutable)</p>
3285
     *
3286
     * @see          Arrayy::keys()
3287
     *
3288
     * @phpstan-return static<int,TKey>
3289
     * @psalm-mutation-free
3290
     */
3291 2
    public function getKeys()
3292
    {
3293 2
        return $this->keys();
3294
    }
3295
3296
    /**
3297
     * Get the current array from the "Arrayy"-object as object.
3298
     *
3299
     * @return \stdClass
3300
     */
3301 4
    public function getObject(): \stdClass
3302
    {
3303 4
        return self::arrayToObject($this->toArray());
3304
    }
3305
3306
    /**
3307
     * alias: for "Arrayy->randomImmutable()"
3308
     *
3309
     * @return static
3310
     *                <p>(Immutable)</p>
3311
     *
3312
     * @see          Arrayy::randomImmutable()
3313
     *
3314
     * @phpstan-return static<array-key,T>
3315
     */
3316 4
    public function getRandom(): self
3317
    {
3318 4
        return $this->randomImmutable();
3319
    }
3320
3321
    /**
3322
     * alias: for "Arrayy->randomKey()"
3323
     *
3324
     * @return mixed|null
3325
     *                    <p>Get a key/index or null if there wasn't a key/index.</p>
3326
     *
3327
     * @phpstan-return null|TKey
3328
     *
3329
     * @see Arrayy::randomKey()
3330
     */
3331 3
    public function getRandomKey()
3332
    {
3333 3
        return $this->randomKey();
3334
    }
3335
3336
    /**
3337
     * alias: for "Arrayy->randomKeys()"
3338
     *
3339
     * @param int $number
3340
     *
3341
     * @return static
3342
     *                <p>(Immutable)</p>
3343
     *
3344
     * @see          Arrayy::randomKeys()
3345
     *
3346
     * @phpstan-return static<TKey,T>
3347
     */
3348 8
    public function getRandomKeys(int $number): self
3349
    {
3350 8
        return $this->randomKeys($number);
3351
    }
3352
3353
    /**
3354
     * alias: for "Arrayy->randomValue()"
3355
     *
3356
     * @return mixed|null
3357
     *                    <p>Get a random value or null if there wasn't a value.</p>
3358
     *
3359
     * @phpstan-return null|T
3360
     *
3361
     * @see Arrayy::randomValue()
3362
     */
3363 3
    public function getRandomValue()
3364
    {
3365 3
        return $this->randomValue();
3366
    }
3367
3368
    /**
3369
     * alias: for "Arrayy->randomValues()"
3370
     *
3371
     * @param int $number
3372
     *
3373
     * @return static
3374
     *                <p>(Immutable)</p>
3375
     *
3376
     * @see          Arrayy::randomValues()
3377
     *
3378
     * @phpstan-return static<TKey,T>
3379
     */
3380 6
    public function getRandomValues(int $number): self
3381
    {
3382 6
        return $this->randomValues($number);
3383
    }
3384
3385
    /**
3386
     * Gets all values.
3387
     *
3388
     * @return static
3389
     *                <p>The values of all elements in this array, in the order they
3390
     *                appear in the array.</p>
3391
     *
3392
     * @phpstan-return static<TKey,T>
3393
     */
3394 4
    public function getValues()
3395
    {
3396 4
        $this->generatorToArray(false);
3397
3398 4
        return static::create(
3399 4
            \array_values($this->array),
3400 4
            $this->iteratorClass,
3401 4
            false
3402
        );
3403
    }
3404
3405
    /**
3406
     * Gets all values via Generator.
3407
     *
3408
     * @return \Generator
3409
     *                    <p>The values of all elements in this array, in the order they
3410
     *                    appear in the array as Generator.</p>
3411
     *
3412
     * @phpstan-return \Generator<TKey,T>
3413
     */
3414 4
    public function getValuesYield(): \Generator
3415
    {
3416 4
        yield from $this->getGenerator();
3417 4
    }
3418
3419
    /**
3420
     * Group values from a array according to the results of a closure.
3421
     *
3422
     * @param callable|string $grouper  <p>A callable function name.</p>
3423
     * @param bool            $saveKeys
3424
     *
3425
     * @return static
3426
     *                <p>(Immutable)</p>
3427
     *
3428
     * @phpstan-return static<TKey,T>
3429
     * @psalm-mutation-free
3430
     */
3431 4
    public function group($grouper, bool $saveKeys = false): self
3432
    {
3433
        // init
3434 4
        $result = [];
3435
3436
        // Iterate over values, group by property/results from closure.
3437 4
        foreach ($this->getGenerator() as $key => $value) {
3438 4
            if (\is_callable($grouper) === true) {
3439 3
                $groupKey = $grouper($value, $key);
3440
            } else {
3441 1
                $groupKey = $this->get($grouper);
3442
            }
3443
3444 4
            $newValue = $this->get($groupKey, null, $result);
3445
3446 4
            if ($groupKey instanceof self) {
3447
                $groupKey = $groupKey->toArray();
3448
            }
3449
3450 4
            if ($newValue instanceof self) {
3451 4
                $newValue = $newValue->toArray();
3452
            }
3453
3454
            // Add to results.
3455 4
            if ($groupKey !== null) {
3456 3
                if ($saveKeys) {
3457 2
                    $result[$groupKey] = $newValue;
3458 2
                    $result[$groupKey][$key] = $value;
3459
                } else {
3460 1
                    $result[$groupKey] = $newValue;
3461 1
                    $result[$groupKey][] = $value;
3462
                }
3463
            }
3464
        }
3465
3466 4
        return static::create(
3467 4
            $result,
3468 4
            $this->iteratorClass,
3469 4
            false
3470
        );
3471
    }
3472
3473
    /**
3474
     * Check if an array has a given key.
3475
     *
3476
     * @param mixed $key
3477
     *
3478
     * @return bool
3479
     *
3480
     * @phpstan-param null|TKey|TKey[] $key
3481
     */
3482 30
    public function has($key): bool
3483
    {
3484 30
        static $UN_FOUND = null;
3485
3486 30
        if ($UN_FOUND === null) {
3487
            // Generate unique string to use as marker.
3488 1
            $UN_FOUND = 'arrayy--' . \uniqid('arrayy', true);
3489
        }
3490
3491 30
        if (\is_array($key)) {
3492 1
            if ($key === []) {
3493
                return false;
3494
            }
3495
3496 1
            foreach ($key as $keyTmp) {
3497 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3498 1
                if ($found === false) {
3499 1
                    return false;
3500
                }
3501
            }
3502
3503 1
            return true;
3504
        }
3505
3506 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3507
    }
3508
3509
    /**
3510
     * Check if an array has a given value.
3511
     *
3512
     * INFO: If you need to search recursive please use ```contains($value, true)```.
3513
     *
3514
     * @param mixed $value
3515
     *
3516
     * @return bool
3517
     *
3518
     * @phpstan-param T $value
3519
     */
3520 1
    public function hasValue($value): bool
3521
    {
3522 1
        return $this->contains($value);
3523
    }
3524
3525
    /**
3526
     * Implodes the values of this array.
3527
     *
3528
     * EXAMPLE: <code>
3529
     * a([0 => -9, 1, 2])->implode('|'); // '-9|1|2'
3530
     * </code>
3531
     *
3532
     * @param string $glue
3533
     * @param string $prefix
3534
     *
3535
     * @return string
3536
     * @psalm-mutation-free
3537
     */
3538 28
    public function implode(string $glue = '', string $prefix = ''): string
3539
    {
3540 28
        return $prefix . $this->implode_recursive($glue, $this->toArray(), false);
3541
    }
3542
3543
    /**
3544
     * Implodes the keys of this array.
3545
     *
3546
     * @param string $glue
3547
     *
3548
     * @return string
3549
     * @psalm-mutation-free
3550
     */
3551 8
    public function implodeKeys(string $glue = ''): string
3552
    {
3553 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3554
    }
3555
3556
    /**
3557
     * Given a list and an iterate-function that returns
3558
     * a key for each element in the list (or a property name),
3559
     * returns an object with an index of each item.
3560
     *
3561
     * @param int|string $key
3562
     *
3563
     * @return static
3564
     *                <p>(Immutable)</p>
3565
     *
3566
     * @phpstan-param array-key $key
3567
     * @phpstan-return static<TKey,T>
3568
     * @psalm-mutation-free
3569
     */
3570 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...
3571
    {
3572
        // init
3573 4
        $results = [];
3574
3575 4
        foreach ($this->getGenerator() as $a) {
3576 4
            if (\array_key_exists($key, $a) === true) {
3577 3
                $results[$a[$key]] = $a;
3578
            }
3579
        }
3580
3581 4
        return static::create(
3582 4
            $results,
3583 4
            $this->iteratorClass,
3584 4
            false
3585
        );
3586
    }
3587
3588
    /**
3589
     * alias: for "Arrayy->searchIndex()"
3590
     *
3591
     * @param mixed $value
3592
     *                     <p>The value to search for.</p>
3593
     *
3594
     * @return false|int|string
3595
     *
3596
     * @phpstan-param T $value
3597
     * @phpstan-return false|TKey
3598
     *
3599
     * @see Arrayy::searchIndex()
3600
     */
3601 4
    public function indexOf($value)
3602
    {
3603 4
        return $this->searchIndex($value);
3604
    }
3605
3606
    /**
3607
     * Get everything but the last..$to items.
3608
     *
3609
     * EXAMPLE: <code>
3610
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->initial(2); // Arrayy[0 => 'foo']
3611
     * </code>
3612
     *
3613
     * @param int $to
3614
     *
3615
     * @return static
3616
     *                <p>(Immutable)</p>
3617
     *
3618
     * @phpstan-return static<TKey,T>
3619
     * @psalm-mutation-free
3620
     */
3621 12
    public function initial(int $to = 1): self
3622
    {
3623 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3624
    }
3625
3626
    /**
3627
     * Return an array with all elements found in input array.
3628
     *
3629
     * EXAMPLE: <code>
3630
     * a(['foo', 'bar'])->intersection(['bar', 'baz']); // Arrayy['bar']
3631
     * </code>
3632
     *
3633
     * @param array $search
3634
     * @param bool  $keepKeys
3635
     *
3636
     * @return static
3637
     *                <p>(Immutable)</p>
3638
     *
3639
     * @phpstan-param  array<TKey,T> $search
3640
     * @phpstan-return static<TKey,T>
3641
     * @psalm-mutation-free
3642
     */
3643 4
    public function intersection(array $search, bool $keepKeys = false): self
3644
    {
3645 4
        if ($keepKeys) {
3646
            /**
3647
             * @psalm-suppress MissingClosureReturnType
3648
             * @psalm-suppress MissingClosureParamType
3649
             */
3650 1
            return static::create(
3651 1
                \array_uintersect(
3652 1
                    $this->toArray(),
3653 1
                    $search,
3654 1
                    static function ($a, $b) {
3655 1
                        return $a === $b ? 0 : -1;
3656 1
                    }
3657
                ),
3658 1
                $this->iteratorClass,
3659 1
                false
3660
            );
3661
        }
3662
3663 3
        return static::create(
3664 3
            \array_values(\array_intersect($this->toArray(), $search)),
3665 3
            $this->iteratorClass,
3666 3
            false
3667
        );
3668
    }
3669
3670
    /**
3671
     * Return an array with all elements found in input array.
3672
     *
3673
     * @param array ...$array
3674
     *
3675
     * @return static
3676
     *                <p>(Immutable)</p>
3677
     *
3678
     * @phpstan-param  array<array<TKey,T>> ...$array
3679
     * @phpstan-return static<TKey,T>
3680
     * @psalm-mutation-free
3681
     */
3682 1
    public function intersectionMulti(...$array): self
3683
    {
3684 1
        return static::create(
3685 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3686 1
            $this->iteratorClass,
3687 1
            false
3688
        );
3689
    }
3690
3691
    /**
3692
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3693
     *
3694
     * EXAMPLE: <code>
3695
     * a(['foo', 'bar'])->intersects(['föö', 'bär']); // false
3696
     * </code>
3697
     *
3698
     * @param array $search
3699
     *
3700
     * @return bool
3701
     *
3702
     * @phpstan-param array<TKey,T> $search
3703
     */
3704 1
    public function intersects(array $search): bool
3705
    {
3706 1
        return $this->intersection($search)->count() > 0;
3707
    }
3708
3709
    /**
3710
     * Invoke a function on all of an array's values.
3711
     *
3712
     * @param callable $callable
3713
     * @param mixed    $arguments
3714
     *
3715
     * @return static
3716
     *                <p>(Immutable)</p>
3717
     *
3718
     * @phpstan-param  callable(T=,mixed):mixed $callable
3719
     * @phpstan-return static<TKey,T>
3720
     * @psalm-mutation-free
3721
     */
3722 1
    public function invoke($callable, $arguments = []): self
3723
    {
3724
        // If one argument given for each iteration, create an array for it.
3725 1
        if (!\is_array($arguments)) {
3726 1
            $arguments = \array_fill(
3727 1
                0,
3728 1
                $this->count(),
3729 1
                $arguments
3730
            );
3731
        }
3732
3733
        // If the callable has arguments, pass them.
3734 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...
3735 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3736
        } else {
3737 1
            $array = $this->map($callable);
3738
        }
3739
3740 1
        return static::create(
3741 1
            $array,
3742 1
            $this->iteratorClass,
3743 1
            false
3744
        );
3745
    }
3746
3747
    /**
3748
     * Check whether array is associative or not.
3749
     *
3750
     * EXAMPLE: <code>
3751
     * a(['foo' => 'bar', 2, 3])->isAssoc(); // true
3752
     * </code>
3753
     *
3754
     * @param bool $recursive
3755
     *
3756
     * @return bool
3757
     *              <p>Returns true if associative, false otherwise.</p>
3758
     */
3759 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...
3760
    {
3761 15
        if ($this->isEmpty()) {
3762 3
            return false;
3763
        }
3764
3765
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3766 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3767 13
            if ((string) $key !== $key) {
3768 11
                return false;
3769
            }
3770
        }
3771
3772 3
        return true;
3773
    }
3774
3775
    /**
3776
     * Check if a given key or keys are empty.
3777
     *
3778
     * @param int|int[]|string|string[]|null $keys
3779
     *
3780
     * @return bool
3781
     *              <p>Returns true if empty, false otherwise.</p>
3782
     * @psalm-mutation-free
3783
     */
3784 45
    public function isEmpty($keys = null): bool
3785
    {
3786 45
        if ($this->generator) {
3787
            return $this->toArray() === [];
3788
        }
3789
3790 45
        if ($keys === null) {
3791 43
            return $this->array === [];
3792
        }
3793
3794 2
        foreach ((array) $keys as $key) {
3795 2
            if (!empty($this->get($key))) {
3796 2
                return false;
3797
            }
3798
        }
3799
3800 2
        return true;
3801
    }
3802
3803
    /**
3804
     * Check if the current array is equal to the given "$array" or not.
3805
     *
3806
     * EXAMPLE: <code>
3807
     * a(['💩'])->isEqual(['💩']); // true
3808
     * </code>
3809
     *
3810
     * @param array $array
3811
     *
3812
     * @return bool
3813
     *
3814
     * @phpstan-param array<array-key,mixed> $array
3815
     */
3816 1
    public function isEqual(array $array): bool
3817
    {
3818 1
        return $this->toArray() === $array;
3819
    }
3820
3821
    /**
3822
     * Check if the current array is a multi-array.
3823
     *
3824
     * EXAMPLE: <code>
3825
     * a(['foo' => [1, 2 , 3]])->isMultiArray(); // true
3826
     * </code>
3827
     *
3828
     * @return bool
3829
     */
3830 22
    public function isMultiArray(): bool
3831
    {
3832 22
        foreach ($this->getGenerator() as $value) {
3833 20
            if (\is_array($value)) {
3834 5
                return true;
3835
            }
3836
        }
3837
3838 18
        return false;
3839
    }
3840
3841
    /**
3842
     * Check whether array is numeric or not.
3843
     *
3844
     * @return bool
3845
     *              <p>Returns true if numeric, false otherwise.</p>
3846
     */
3847 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...
3848
    {
3849 5
        if ($this->isEmpty()) {
3850 2
            return false;
3851
        }
3852
3853
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3854 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3855 4
            if ((int) $key !== $key) {
3856 2
                return false;
3857
            }
3858
        }
3859
3860 2
        return true;
3861
    }
3862
3863
    /**
3864
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3865
     *
3866
     * EXAMPLE: <code>
3867
     * a([0 => 'foo', 1 => 'lall', 2 => 'foobar'])->isSequential(); // true
3868
     * </code>
3869
     *
3870
     * INFO: If the array is empty we count it as non-sequential.
3871
     *
3872
     * @param bool $recursive
3873
     *
3874
     * @return bool
3875
     * @psalm-mutation-free
3876
     */
3877 10
    public function isSequential(bool $recursive = false): bool
3878
    {
3879 10
        $i = 0;
3880 10
        foreach ($this->getGenerator() as $key => $value) {
3881
            if (
3882 9
                $recursive
3883
                &&
3884 9
                (\is_array($value) || $value instanceof \Traversable)
3885
                &&
3886 9
                self::create($value)->isSequential() === false
3887
            ) {
3888 1
                return false;
3889
            }
3890
3891 9
            if ($key !== $i) {
3892 3
                return false;
3893
            }
3894
3895 8
            ++$i;
3896
        }
3897
3898 9
        if ($i === 0) {
3899 3
            return false;
3900
        }
3901
3902 8
        return true;
3903
    }
3904
3905
    /**
3906
     * @return array
3907
     *
3908
     * @phpstan-return array<TKey,T>
3909
     */
3910 2
    public function jsonSerialize(): array
3911
    {
3912 2
        return $this->toArray();
3913
    }
3914
3915
    /**
3916
     * Gets the key/index of the element at the current internal iterator position.
3917
     *
3918
     * @return int|string|null
3919
     * @phpstan-return array-key|null
3920
     */
3921
    public function key()
3922
    {
3923
        if ($this->generator) {
3924
            return $this->generator->key();
3925
        }
3926
3927
        return \key($this->array);
3928
    }
3929
3930
    /**
3931
     * Checks if the given key exists in the provided array.
3932
     *
3933
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3934
     *       then you need to use "Arrayy->offsetExists()".
3935
     *
3936
     * @param int|string $key the key to look for
3937
     *
3938
     * @return bool
3939
     * @psalm-mutation-free
3940
     */
3941 174
    public function keyExists($key): bool
3942
    {
3943 174
        foreach ($this->getGenerator() as $keyTmp => $value) {
3944 169
            if ($key === $keyTmp) {
3945 154
                return true;
3946
            }
3947
        }
3948
3949 131
        return false;
3950
    }
3951
3952
    /**
3953
     * Get all keys from the current array.
3954
     *
3955
     * EXAMPLE: <code>
3956
     * a([1 => 'foo', 2 => 'foo2', 3 => 'bar'])->keys(); // Arrayy[1, 2, 3]
3957
     * </code>
3958
     *
3959
     * @param bool       $recursive
3960
     *                                  [optional] <p>
3961
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3962
     *                                  </p>
3963
     * @param mixed|null $search_values
3964
     *                                  [optional] <p>
3965
     *                                  If specified, then only keys containing these values are returned.
3966
     *                                  </p>
3967
     * @param bool       $strict
3968
     *                                  [optional] <p>
3969
     *                                  Determines if strict comparison (===) should be used during the search.
3970
     *                                  </p>
3971
     *
3972
     * @return static
3973
     *                <p>(Immutable) An array of all the keys in input.</p>
3974
     *
3975
     * @phpstan-param null|T|T[] $search_values
3976
     * @phpstan-return static<int,TKey>
3977
     *
3978
     * @psalm-mutation-free
3979
     */
3980 29
    public function keys(
3981
        bool $recursive = false,
3982
        $search_values = null,
3983
        bool $strict = true
3984
    ): self {
3985
3986
        // recursive
3987
3988 29
        if ($recursive === true) {
3989 4
            $array = $this->array_keys_recursive(
3990 4
                null,
3991
                $search_values,
3992
                $strict
3993
            );
3994
3995 4
            return static::create(
3996 4
                $array,
3997 4
                $this->iteratorClass,
3998 4
                false
3999
            );
4000
        }
4001
4002
        // non recursive
4003
4004 28
        if ($search_values === null) {
4005 28
            $arrayFunction = function (): \Generator {
4006 28
                foreach ($this->getGenerator() as $key => $value) {
4007 26
                    yield $key;
4008
                }
4009 16
            };
4010
        } else {
4011 1
            $arrayFunction = function () use ($search_values, $strict): \Generator {
4012 1
                $is_array_tmp = \is_array($search_values);
4013
4014
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4015 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
4016 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...
4017
                        (
4018 1
                            $is_array_tmp === false
4019
                            &&
4020 1
                            $strict === true
4021
                            &&
4022 1
                            $search_values === $value
4023
                        )
4024
                        ||
4025
                        (
4026 1
                            $is_array_tmp === false
4027
                            &&
4028 1
                            $strict === false
4029
                            &&
4030 1
                            $search_values == $value
4031
                        )
4032
                        ||
4033
                        (
4034 1
                            $is_array_tmp === true
4035
                            &&
4036 1
                            \in_array($value, $search_values, $strict)
4037
                        )
4038
                    ) {
4039 1
                        yield $key;
4040
                    }
4041
                }
4042 1
            };
4043
        }
4044
4045 28
        return static::create(
4046 28
            $arrayFunction,
4047 28
            $this->iteratorClass,
4048 28
            false
4049
        );
4050
    }
4051
4052
    /**
4053
     * Sort an array by key in reverse order.
4054
     *
4055
     * @param int $sort_flags [optional] <p>
4056
     *                        You may modify the behavior of the sort using the optional
4057
     *                        parameter sort_flags, for details
4058
     *                        see sort.
4059
     *                        </p>
4060
     *
4061
     * @return $this
4062
     *               <p>(Mutable) Return this Arrayy object.</p>
4063
     *
4064
     * @phpstan-return static<TKey,T>
4065
     */
4066 4
    public function krsort(int $sort_flags = 0): self
4067
    {
4068 4
        $this->generatorToArray();
4069
4070 4
        \krsort($this->array, $sort_flags);
4071
4072 4
        return $this;
4073
    }
4074
4075
    /**
4076
     * Sort an array by key in reverse order.
4077
     *
4078
     * @param int $sort_flags [optional] <p>
4079
     *                        You may modify the behavior of the sort using the optional
4080
     *                        parameter sort_flags, for details
4081
     *                        see sort.
4082
     *                        </p>
4083
     *
4084
     * @return $this
4085
     *               <p>(Immutable) Return this Arrayy object.</p>
4086
     *
4087
     * @phpstan-return static<TKey,T>
4088
     * @psalm-mutation-free
4089
     */
4090 4
    public function krsortImmutable(int $sort_flags = 0): self
4091
    {
4092 4
        $that = clone $this;
4093
4094
        /**
4095
         * @psalm-suppress ImpureMethodCall - object is already cloned
4096
         */
4097 4
        $that->krsort($sort_flags);
4098
4099 4
        return $that;
4100
    }
4101
4102
    /**
4103
     * Get the last value from the current array.
4104
     *
4105
     * EXAMPLE: <code>
4106
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->last(); // 'lall'
4107
     * </code>
4108
     *
4109
     * @return mixed|null
4110
     *                    <p>Return null if there wasn't a element.</p>
4111
     *
4112
     * @phpstan-return T|null
4113
     * @psalm-mutation-free
4114
     */
4115 17
    public function last()
4116
    {
4117 17
        $key_last = $this->lastKey();
4118 17
        if ($key_last === null) {
4119 2
            return null;
4120
        }
4121
4122 15
        return $this->get($key_last);
4123
    }
4124
4125
    /**
4126
     * Get the last key from the current array.
4127
     *
4128
     * @return mixed|null
4129
     *                    <p>Return null if there wasn't a element.</p>
4130
     * @psalm-mutation-free
4131
     */
4132 21
    public function lastKey()
4133
    {
4134 21
        $this->generatorToArray();
4135
4136 21
        return \array_key_last($this->array);
4137
    }
4138
4139
    /**
4140
     * Get the last value(s) from the current array.
4141
     *
4142
     * EXAMPLE: <code>
4143
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
4144
     * </code>
4145
     *
4146
     * @param int|null $number
4147
     *
4148
     * @return static
4149
     *                <p>(Immutable)</p>
4150
     *
4151
     * @phpstan-return static<TKey,T>
4152
     * @psalm-mutation-free
4153
     */
4154 13
    public function lastsImmutable(int $number = null): self
4155
    {
4156 13
        if ($this->isEmpty()) {
4157 1
            return static::create(
4158 1
                [],
4159 1
                $this->iteratorClass,
4160 1
                false
4161
            );
4162
        }
4163
4164 12
        if ($number === null) {
4165 8
            $poppedValue = $this->last();
4166
4167 8
            if ($poppedValue === null) {
4168 1
                $poppedValue = [$poppedValue];
4169
            } else {
4170 7
                $poppedValue = (array) $poppedValue;
4171
            }
4172
4173 8
            $arrayy = static::create(
4174 8
                $poppedValue,
4175 8
                $this->iteratorClass,
4176 8
                false
4177
            );
4178
        } else {
4179 4
            $arrayy = $this->rest(-$number);
4180
        }
4181
4182 12
        return $arrayy;
4183
    }
4184
4185
    /**
4186
     * Get the last value(s) from the current array.
4187
     *
4188
     * EXAMPLE: <code>
4189
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
4190
     * </code>
4191
     *
4192
     * @param int|null $number
4193
     *
4194
     * @return $this
4195
     *               <p>(Mutable)</p>
4196
     *
4197
     * @phpstan-return static<TKey,T>
4198
     */
4199 13
    public function lastsMutable(int $number = null): self
4200
    {
4201 13
        if ($this->isEmpty()) {
4202 1
            return $this;
4203
        }
4204
4205 12
        $this->array = $this->lastsImmutable($number)->toArray();
4206 12
        $this->generator = null;
4207
4208 12
        return $this;
4209
    }
4210
4211
    /**
4212
     * Count the values from the current array.
4213
     *
4214
     * alias: for "Arrayy->count()"
4215
     *
4216
     * @param int $mode
4217
     *
4218
     * @return int
4219
     *
4220
     * @see Arrayy::count()
4221
     */
4222 20
    public function length(int $mode = \COUNT_NORMAL): int
4223
    {
4224 20
        return $this->count($mode);
4225
    }
4226
4227
    /**
4228
     * Apply the given function to the every element of the array,
4229
     * collecting the results.
4230
     *
4231
     * EXAMPLE: <code>
4232
     * a(['foo', 'Foo'])->map('mb_strtoupper'); // Arrayy['FOO', 'FOO']
4233
     * </code>
4234
     *
4235
     * @param callable $callable
4236
     * @param bool     $useKeyAsSecondParameter
4237
     * @param mixed    ...$arguments
4238
     *
4239
     * @return static
4240
     *                <p>(Immutable) Arrayy object with modified elements.</p>
4241
     *
4242
     * @template T2
4243
     *              <p>The output value type.</p>
4244
     *
4245
     * @phpstan-param callable(T,TKey=,mixed=):T2 $callable
4246
     * @phpstan-return static<TKey,T2>
4247
     * @psalm-mutation-free
4248
     */
4249 6
    public function map(
4250
        callable $callable,
4251
        bool $useKeyAsSecondParameter = false,
4252
        ...$arguments
4253
    ) {
4254
        /**
4255
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
4256
         */
4257 6
        $useArguments = \func_num_args() > 2;
4258
4259 6
        return static::create(
4260 6
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
4261 6
                foreach ($this->getGenerator() as $key => $value) {
4262 5
                    if ($useArguments) {
4263 3
                        if ($useKeyAsSecondParameter) {
4264
                            yield $key => $callable($value, $key, ...$arguments);
4265
                        } else {
4266 3
                            yield $key => $callable($value, ...$arguments);
4267
                        }
4268
                    } else {
4269
                        /** @noinspection NestedPositiveIfStatementsInspection */
4270 5
                        if ($useKeyAsSecondParameter) {
4271
                            yield $key => $callable($value, $key);
4272
                        } else {
4273 5
                            yield $key => $callable($value);
4274
                        }
4275
                    }
4276
                }
4277 6
            },
4278 6
            $this->iteratorClass,
4279 6
            false
4280
        );
4281
    }
4282
4283
    /**
4284
     * Check if all items in current array match a truth test.
4285
     *
4286
     * EXAMPLE: <code>
4287
     * $closure = function ($value, $key) {
4288
     *     return ($value % 2 === 0);
4289
     * };
4290
     * a([2, 4, 8])->matches($closure); // true
4291
     * </code>
4292
     *
4293
     * @param \Closure $closure
4294
     *
4295
     * @return bool
4296
     *
4297
     * @phpstan-param \Closure(T=,TKey=):bool $closure
4298
     */
4299 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...
4300
    {
4301 15
        if ($this->count() === 0) {
4302 2
            return false;
4303
        }
4304
4305 13
        foreach ($this->getGenerator() as $key => $value) {
4306 13
            $value = $closure($value, $key);
4307
4308 13
            if ($value === false) {
4309 7
                return false;
4310
            }
4311
        }
4312
4313 7
        return true;
4314
    }
4315
4316
    /**
4317
     * Check if any item in the current array matches a truth test.
4318
     *
4319
     * EXAMPLE: <code>
4320
     * $closure = function ($value, $key) {
4321
     *     return ($value % 2 === 0);
4322
     * };
4323
     * a([1, 4, 7])->matches($closure); // true
4324
     * </code>
4325
     *
4326
     * @param \Closure $closure
4327
     *
4328
     * @return bool
4329
     *
4330
     * @phpstan-param \Closure(T=,TKey=):bool $closure
4331
     */
4332 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...
4333
    {
4334 14
        if ($this->count() === 0) {
4335 2
            return false;
4336
        }
4337
4338 12
        foreach ($this->getGenerator() as $key => $value) {
4339 12
            $value = $closure($value, $key);
4340
4341 12
            if ($value === true) {
4342 9
                return true;
4343
            }
4344
        }
4345
4346 4
        return false;
4347
    }
4348
4349
    /**
4350
     * Get the max value from an array.
4351
     *
4352
     * EXAMPLE: <code>
4353
     * a([-9, -8, -7, 1.32])->max(); // 1.32
4354
     * </code>
4355
     *
4356
     * @return false|float|int|string
4357
     *                                <p>Will return false if there are no values.</p>
4358
     */
4359 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...
4360
    {
4361 10
        if ($this->count() === 0) {
4362 1
            return false;
4363
        }
4364
4365 9
        $max = false;
4366
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4367 9
        foreach ($this->getGeneratorByReference() as &$value) {
4368
            if (
4369 9
                $max === false
4370
                ||
4371 9
                $value > $max
4372
            ) {
4373 9
                $max = $value;
4374
            }
4375
        }
4376
4377 9
        return $max;
4378
    }
4379
4380
    /**
4381
     * Merge the new $array into the current array.
4382
     *
4383
     * - keep key,value from the current array, also if the index is in the new $array
4384
     *
4385
     * EXAMPLE: <code>
4386
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4387
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4388
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[1 => 'one', 'foo' => 'bar2', 3 => 'three']
4389
     * // ---
4390
     * $array1 = [0 => 'one', 1 => 'foo'];
4391
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4392
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2']
4393
     * </code>
4394
     *
4395
     * @param array $array
4396
     * @param bool  $recursive
4397
     *
4398
     * @return static
4399
     *                <p>(Immutable)</p>
4400
     *
4401
     * @phpstan-param  array<int|TKey,T> $array
4402
     * @phpstan-return static<int|TKey,T>
4403
     * @psalm-mutation-free
4404
     */
4405 33 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...
4406
    {
4407 33
        if ($recursive === true) {
4408 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
4409 9
            $result = \array_replace_recursive($this->toArray(), $array);
4410
        } else {
4411 24
            $result = \array_replace($this->toArray(), $array);
4412
        }
4413
4414 33
        return static::create(
4415 33
            $result,
4416 33
            $this->iteratorClass,
4417 33
            false
4418
        );
4419
    }
4420
4421
    /**
4422
     * Merge the new $array into the current array.
4423
     *
4424
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
4425
     * - create new indexes
4426
     *
4427
     * EXAMPLE: <code>
4428
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4429
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4430
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 'foo' => 'bar2', 1 => 'three']
4431
     * // ---
4432
     * $array1 = [0 => 'one', 1 => 'foo'];
4433
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4434
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 1 => 'foo', 2 => 'foo', 3 => 'bar2']
4435
     * </code>
4436
     *
4437
     * @param array $array
4438
     * @param bool  $recursive
4439
     *
4440
     * @return static
4441
     *                <p>(Immutable)</p>
4442
     *
4443
     * @phpstan-param  array<TKey,T> $array
4444
     * @phpstan-return static<int,T>
4445
     * @psalm-mutation-free
4446
     */
4447 20 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...
4448
    {
4449 20
        if ($recursive === true) {
4450 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
4451 5
            $result = \array_merge_recursive($this->toArray(), $array);
4452
        } else {
4453 15
            $result = \array_merge($this->toArray(), $array);
4454
        }
4455
4456 20
        return static::create(
4457 20
            $result,
4458 20
            $this->iteratorClass,
4459 20
            false
4460
        );
4461
    }
4462
4463
    /**
4464
     * Merge the the current array into the $array.
4465
     *
4466
     * - use key,value from the new $array, also if the index is in the current array
4467
     *
4468
     * EXAMPLE: <code>
4469
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4470
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4471
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy['foo' => 'bar1', 3 => 'three', 1 => 'one']
4472
     * // ---
4473
     * $array1 = [0 => 'one', 1 => 'foo'];
4474
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4475
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy[0 => 'one', 1 => 'foo']
4476
     * </code>
4477
     *
4478
     * @param array $array
4479
     * @param bool  $recursive
4480
     *
4481
     * @return static
4482
     *                <p>(Immutable)</p>
4483
     *
4484
     * @phpstan-param  array<TKey,T> $array
4485
     * @phpstan-return static<TKey,T>
4486
     * @psalm-mutation-free
4487
     */
4488 17 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...
4489
    {
4490 17
        if ($recursive === true) {
4491 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
4492 4
            $result = \array_replace_recursive($array, $this->toArray());
4493
        } else {
4494 13
            $result = \array_replace($array, $this->toArray());
4495
        }
4496
4497 17
        return static::create(
4498 17
            $result,
4499 17
            $this->iteratorClass,
4500 17
            false
4501
        );
4502
    }
4503
4504
    /**
4505
     * Merge the current array into the new $array.
4506
     *
4507
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
4508
     * - create new indexes
4509
     *
4510
     * EXAMPLE: <code>
4511
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4512
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4513
     * a($array1)->mergePrependNewIndex($array2); // Arrayy['foo' => 'bar1', 0 => 'three', 1 => 'one']
4514
     * // ---
4515
     * $array1 = [0 => 'one', 1 => 'foo'];
4516
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4517
     * a($array1)->mergePrependNewIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2', 2 => 'one', 3 => 'foo']
4518
     * </code>
4519
     *
4520
     * @param array $array
4521
     * @param bool  $recursive
4522
     *
4523
     * @return static
4524
     *                <p>(Immutable)</p>
4525
     *
4526
     * @phpstan-param  array<TKey,T> $array
4527
     * @phpstan-return static<int,T>
4528
     * @psalm-mutation-free
4529
     */
4530 21 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...
4531
    {
4532 21
        if ($recursive === true) {
4533 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
4534 7
            $result = \array_merge_recursive($array, $this->toArray());
4535
        } else {
4536 14
            $result = \array_merge($array, $this->toArray());
4537
        }
4538
4539 21
        return static::create(
4540 21
            $result,
4541 21
            $this->iteratorClass,
4542 21
            false
4543
        );
4544
    }
4545
4546
    /**
4547
     * @return ArrayyMeta|mixed|static
4548
     */
4549 18
    public static function meta()
4550
    {
4551 18
        return (new ArrayyMeta())->getMetaObject(static::class);
4552
    }
4553
4554
    /**
4555
     * Get the min value from an array.
4556
     *
4557
     * EXAMPLE: <code>
4558
     * a([-9, -8, -7, 1.32])->min(); // -9
4559
     * </code>
4560
     *
4561
     * @return false|mixed
4562
     *                     <p>Will return false if there are no values.</p>
4563
     */
4564 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...
4565
    {
4566 10
        if ($this->count() === 0) {
4567 1
            return false;
4568
        }
4569
4570 9
        $min = false;
4571
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4572 9
        foreach ($this->getGeneratorByReference() as &$value) {
4573
            if (
4574 9
                $min === false
4575
                ||
4576 9
                $value < $min
4577
            ) {
4578 9
                $min = $value;
4579
            }
4580
        }
4581
4582 9
        return $min;
4583
    }
4584
4585
    /**
4586
     * Get the most used value from the array.
4587
     *
4588
     * @return mixed|null
4589
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4590
     *
4591
     * @phpstan-return T|null
4592
     * @psalm-mutation-free
4593
     */
4594 3
    public function mostUsedValue()
4595
    {
4596
        /** @phpstan-ignore-next-line | false-positive? maybe because we switch key-value via "countValues"? */
4597 3
        return $this->countValues()->arsortImmutable()->firstKey();
4598
    }
4599
4600
    /**
4601
     * Get the most used value from the array.
4602
     *
4603
     * @param int|null $number <p>How many values you will take?</p>
4604
     *
4605
     * @return static
4606
     *                <p>(Immutable)</p>
4607
     *
4608
     * @phpstan-return static<array-key,T>
4609
     * @psalm-mutation-free
4610
     */
4611 3
    public function mostUsedValues(int $number = null): self
4612
    {
4613 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4614
    }
4615
4616
    /**
4617
     * Move an array element to a new index.
4618
     *
4619
     * EXAMPLE: <code>
4620
     * $arr2 = new A(['A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e']);
4621
     * $newArr2 = $arr2->moveElement('D', 1); // Arrayy['A' => 'a', 'D' => 'd', 'B' => 'b', 'C' => 'c', 'E' => 'e']
4622
     * </code>
4623
     *
4624
     * @param int|string $from
4625
     * @param int        $to
4626
     *
4627
     * @return static
4628
     *                <p>(Immutable)</p>
4629
     *
4630
     * @phpstan-return static<TKey,T>
4631
     * @psalm-mutation-free
4632
     */
4633 1
    public function moveElement($from, $to): self
4634
    {
4635 1
        $array = $this->toArray();
4636
4637 1
        if ((int) $from === $from) {
4638 1
            $tmp = \array_splice($array, $from, 1);
4639 1
            \array_splice($array, (int) $to, 0, $tmp);
4640 1
            $output = $array;
4641 1
        } elseif ((string) $from === $from) {
4642 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4643 1
            $itemToMove = $array[$from];
4644 1
            if ($indexToMove !== false) {
4645 1
                \array_splice($array, $indexToMove, 1);
4646
            }
4647 1
            $i = 0;
4648 1
            $output = [];
4649 1
            foreach ($array as $key => $item) {
4650 1
                if ($i === $to) {
4651 1
                    $output[$from] = $itemToMove;
4652
                }
4653 1
                $output[$key] = $item;
4654 1
                ++$i;
4655
            }
4656
        } else {
4657
            $output = [];
4658
        }
4659
4660 1
        return static::create(
4661 1
            $output,
4662 1
            $this->iteratorClass,
4663 1
            false
4664
        );
4665
    }
4666
4667
    /**
4668
     * Move an array element to the first place.
4669
     *
4670
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4671
     *       loss the keys of an indexed array.
4672
     *
4673
     * @param int|string $key
4674
     *
4675
     * @return static
4676
     *                <p>(Immutable)</p>
4677
     *
4678
     * @phpstan-return static<TKey,T>
4679
     * @psalm-mutation-free
4680
     */
4681 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...
4682
    {
4683 1
        $array = $this->toArray();
4684
4685 1
        if ($this->offsetExists($key)) {
4686 1
            $tmpValue = $this->get($key);
4687 1
            unset($array[$key]);
4688 1
            $array = [$key => $tmpValue] + $array;
4689
        }
4690
4691 1
        return static::create(
4692 1
            $array,
4693 1
            $this->iteratorClass,
4694 1
            false
4695
        );
4696
    }
4697
4698
    /**
4699
     * Move an array element to the last place.
4700
     *
4701
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4702
     *       loss the keys of an indexed array.
4703
     *
4704
     * @param int|string $key
4705
     *
4706
     * @return static
4707
     *                <p>(Immutable)</p>
4708
     *
4709
     * @phpstan-return static<TKey,T>
4710
     * @psalm-mutation-free
4711
     */
4712 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...
4713
    {
4714 1
        $array = $this->toArray();
4715
4716 1
        if ($this->offsetExists($key)) {
4717 1
            $tmpValue = $this->get($key);
4718 1
            unset($array[$key]);
4719 1
            $array += [$key => $tmpValue];
4720
        }
4721
4722 1
        return static::create(
4723 1
            $array,
4724 1
            $this->iteratorClass,
4725 1
            false
4726
        );
4727
    }
4728
4729
    /**
4730
     * Moves the internal iterator position to the next element and returns this element.
4731
     *
4732
     * @return false|mixed
4733
     *                     <p>(Mutable) Will return false if there are no values.</p>
4734
     *
4735
     * @phpstan-return false|T
4736
     */
4737
    public function next()
4738
    {
4739
        if ($this->generator) {
4740
            $this->generator->next();
4741
4742
            return $this->generator->current() ?? false;
4743
        }
4744
4745
        return \next($this->array);
4746
    }
4747
4748
    /**
4749
     * Get the next nth keys and values from the array.
4750
     *
4751
     * @param int $step
4752
     * @param int $offset
4753
     *
4754
     * @return static
4755
     *                <p>(Immutable)</p>
4756
     *
4757
     * @phpstan-return static<TKey,T>
4758
     * @psalm-mutation-free
4759
     */
4760 1
    public function nth(int $step, int $offset = 0): self
4761
    {
4762 1
        $arrayFunction = function () use ($step, $offset): \Generator {
4763 1
            $position = 0;
4764 1
            foreach ($this->getGenerator() as $key => $value) {
4765 1
                if ($position++ % $step !== $offset) {
4766 1
                    continue;
4767
                }
4768
4769 1
                yield $key => $value;
4770
            }
4771 1
        };
4772
4773 1
        return static::create(
4774 1
            $arrayFunction,
4775 1
            $this->iteratorClass,
4776 1
            false
4777
        );
4778
    }
4779
4780
    /**
4781
     * Get a subset of the items from the given array.
4782
     *
4783
     * @param int[]|string[] $keys
4784
     *
4785
     * @return static
4786
     *                <p>(Immutable)</p>
4787
     *
4788
     * @phpstan-param array-key[] $keys
4789
     * @phpstan-return static<TKey,T>
4790
     * @psalm-mutation-free
4791
     */
4792 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...
4793
    {
4794 1
        $keys = \array_flip($keys);
4795
4796 1
        $generator = function () use ($keys): \Generator {
4797 1
            foreach ($this->getGenerator() as $key => $value) {
4798 1
                if (isset($keys[$key])) {
4799 1
                    yield $key => $value;
4800
                }
4801
            }
4802 1
        };
4803
4804 1
        return static::create(
4805 1
            $generator,
4806 1
            $this->iteratorClass,
4807 1
            false
4808
        );
4809
    }
4810
4811
    /**
4812
     * Pad array to the specified size with a given value.
4813
     *
4814
     * @param int   $size  <p>Size of the result array.</p>
4815
     * @param mixed $value <p>Empty value by default.</p>
4816
     *
4817
     * @return static
4818
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4819
     *
4820
     * @phpstan-return static<TKey,T>
4821
     * @psalm-mutation-free
4822
     */
4823 5
    public function pad(int $size, $value): self
4824
    {
4825 5
        return static::create(
4826 5
            \array_pad($this->toArray(), $size, $value),
4827 5
            $this->iteratorClass,
4828 5
            false
4829
        );
4830
    }
4831
4832
    /**
4833
     * Partitions this array in two array according to a predicate.
4834
     * Keys are preserved in the resulting array.
4835
     *
4836
     * @param \Closure $closure
4837
     *                          <p>The predicate on which to partition.</p>
4838
     *
4839
     * @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...
4840
     *                    <p>An array with two elements. The first element contains the array
4841
     *                    of elements where the predicate returned TRUE, the second element
4842
     *                    contains the array of elements where the predicate returned FALSE.</p>
4843
     *
4844
     * @phpstan-param \Closure(T=,TKey=):bool $closure
4845
     * @phpstan-return array<int, static<TKey,T>>
4846
     */
4847 1
    public function partition(\Closure $closure): array
4848
    {
4849
        // init
4850 1
        $matches = [];
4851 1
        $noMatches = [];
4852
4853 1
        foreach ($this->getGenerator() as $key => $value) {
4854 1
            if ($closure($value, $key)) {
4855 1
                $matches[$key] = $value;
4856
            } else {
4857 1
                $noMatches[$key] = $value;
4858
            }
4859
        }
4860
4861 1
        return [self::create($matches), self::create($noMatches)];
4862
    }
4863
4864
    /**
4865
     * Pop a specified value off the end of the current array.
4866
     *
4867
     * @return mixed|null
4868
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4869
     *
4870
     * @phpstan-return T|null
4871
     */
4872 5
    public function pop()
4873
    {
4874 5
        $this->generatorToArray();
4875
4876 5
        return \array_pop($this->array);
4877
    }
4878
4879
    /**
4880
     * Prepend a (key) + value to the current array.
4881
     *
4882
     * EXAMPLE: <code>
4883
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4884
     * </code>
4885
     *
4886
     * @param mixed $value
4887
     * @param mixed $key
4888
     *
4889
     * @return $this
4890
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4891
     *
4892
     * @phpstan-param T $value
4893
     * @phpstan-param TKey|null $key
4894
     * @phpstan-return static<TKey,T>
4895
     */
4896 11
    public function prepend($value, $key = null)
4897
    {
4898 11
        $this->generatorToArray();
4899
4900 11
        if ($this->properties !== []) {
4901 3
            $this->checkType($key, $value);
4902
        }
4903
4904 9
        if ($key === null) {
4905 8
            \array_unshift($this->array, $value);
4906
        } else {
4907 2
            $this->array = [$key => $value] + $this->array;
4908
        }
4909
4910 9
        return $this;
4911
    }
4912
4913
    /**
4914
     * Prepend a (key) + value to the current array.
4915
     *
4916
     * EXAMPLE: <code>
4917
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4918
     * </code>
4919
     *
4920
     * @param mixed $value
4921
     * @param mixed $key
4922
     *
4923
     * @return $this
4924
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4925
     *
4926
     * @phpstan-param T $value
4927
     * @phpstan-param TKey $key
4928
     * @phpstan-return static<TKey,T>
4929
     * @psalm-mutation-free
4930
     */
4931 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...
4932
    {
4933 1
        $generator = function () use ($key, $value): \Generator {
4934 1
            if ($this->properties !== []) {
4935
                $this->checkType($key, $value);
4936
            }
4937
4938 1
            if ($key !== null) {
4939
                yield $key => $value;
4940
            } else {
4941 1
                yield $value;
4942
            }
4943
4944 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
4945 1
                yield $keyOld => $itemOld;
4946
            }
4947 1
        };
4948
4949 1
        return static::create(
4950 1
            $generator,
4951 1
            $this->iteratorClass,
4952 1
            false
4953
        );
4954
    }
4955
4956
    /**
4957
     * Add a suffix to each key.
4958
     *
4959
     * @param float|int|string $suffix
4960
     *
4961
     * @return static
4962
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4963
     *
4964
     * @phpstan-return static<TKey,T>
4965
     * @psalm-mutation-free
4966
     */
4967 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...
4968
    {
4969
        // init
4970 10
        $result = [];
4971
4972 10
        foreach ($this->getGenerator() as $key => $item) {
4973 9
            if ($item instanceof self) {
4974
                $result[$key] = $item->prependToEachKey($suffix);
4975 9
            } elseif (\is_array($item)) {
4976
                $result[$key] = self::create(
4977
                    $item,
4978
                    $this->iteratorClass,
4979
                    false
4980
                )->prependToEachKey($suffix)
4981
                    ->toArray();
4982
            } else {
4983 9
                $result[$key . $suffix] = $item;
4984
            }
4985
        }
4986
4987 10
        return self::create(
4988 10
            $result,
4989 10
            $this->iteratorClass,
4990 10
            false
4991
        );
4992
    }
4993
4994
    /**
4995
     * Add a suffix to each value.
4996
     *
4997
     * @param float|int|string $suffix
4998
     *
4999
     * @return static
5000
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
5001
     *
5002
     * @phpstan-return static<TKey,T>
5003
     * @psalm-mutation-free
5004
     */
5005 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...
5006
    {
5007
        // init
5008 10
        $result = [];
5009
5010 10
        foreach ($this->getGenerator() as $key => $item) {
5011 9
            if ($item instanceof self) {
5012
                $result[$key] = $item->prependToEachValue($suffix);
5013 9
            } elseif (\is_array($item)) {
5014
                $result[$key] = self::create(
5015
                    $item,
5016
                    $this->iteratorClass,
5017
                    false
5018
                )->prependToEachValue($suffix)
5019
                    ->toArray();
5020 9
            } elseif (\is_object($item) === true) {
5021 1
                $result[$key] = $item;
5022
            } else {
5023 8
                $result[$key] = $item . $suffix;
5024
            }
5025
        }
5026
5027 10
        return self::create(
5028 10
            $result,
5029 10
            $this->iteratorClass,
5030 10
            false
5031
        );
5032
    }
5033
5034
    /**
5035
     * Return the value of a given key and
5036
     * delete the key.
5037
     *
5038
     * @param int|int[]|string|string[]|null $keyOrKeys
5039
     * @param mixed                          $fallback
5040
     *
5041
     * @return mixed
5042
     */
5043 6
    public function pull($keyOrKeys = null, $fallback = null)
5044
    {
5045 6
        if ($keyOrKeys === null) {
5046 1
            $array = $this->toArray();
5047 1
            $this->clear();
5048
5049 1
            return $array;
5050
        }
5051
5052 5
        if (\is_array($keyOrKeys)) {
5053 1
            $valueOrValues = [];
5054 1
            foreach ($keyOrKeys as $key) {
5055 1
                $valueOrValues[] = $this->get($key, $fallback);
5056 1
                $this->offsetUnset($key);
5057
            }
5058
        } else {
5059 5
            $valueOrValues = $this->get($keyOrKeys, $fallback);
5060 5
            $this->offsetUnset($keyOrKeys);
5061
        }
5062
5063 5
        return $valueOrValues;
5064
    }
5065
5066
    /**
5067
     * Push one or more values onto the end of array at once.
5068
     *
5069
     * @param mixed ...$args
5070
     *
5071
     * @return $this
5072
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
5073
     *
5074
     * @noinspection ReturnTypeCanBeDeclaredInspection
5075
     *
5076
     * @phpstan-param  array<TKey,T> ...$args
5077
     * @phpstan-return static<TKey,T>
5078
     */
5079 9 View Code Duplication
    public function push(...$args)
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...
5080
    {
5081 9
        $this->generatorToArray();
5082
5083
        if (
5084 9
            $this->checkPropertyTypes
5085
            &&
5086 9
            $this->properties !== []
5087
        ) {
5088 3
            foreach ($args as $key => $value) {
5089 3
                $this->checkType($key, $value);
5090
            }
5091
        }
5092
5093 8
        \array_push($this->array, ...$args);
5094
5095 8
        return $this;
5096
    }
5097
5098
    /**
5099
     * Get a random value from the current array.
5100
     *
5101
     * EXAMPLE: <code>
5102
     * a([1, 2, 3, 4])->randomImmutable(2); // e.g.: Arrayy[1, 4]
5103
     * </code>
5104
     *
5105
     * @param int|null $number <p>How many values you will take?</p>
5106
     *
5107
     * @return static
5108
     *                <p>(Immutable)</p>
5109
     *
5110
     * @phpstan-return static<array-key,T>
5111
     */
5112 19
    public function randomImmutable(int $number = null): self
5113
    {
5114 19
        $this->generatorToArray();
5115
5116 19
        if ($this->count() === 0) {
5117 1
            return static::create(
5118 1
                [],
5119 1
                $this->iteratorClass,
5120 1
                false
5121
            );
5122
        }
5123
5124 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...
5125 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
5126
5127 13
            return static::create(
5128 13
                $arrayRandValue,
5129 13
                $this->iteratorClass,
5130 13
                false
5131
            );
5132
        }
5133
5134 6
        $arrayTmp = $this->array;
5135 6
        \shuffle($arrayTmp);
5136
5137 6
        return static::create(
5138 6
            $arrayTmp,
5139 6
            $this->iteratorClass,
5140 6
            false
5141 6
        )->firstsImmutable($number);
5142
    }
5143
5144
    /**
5145
     * Pick a random key/index from the keys of this array.
5146
     *
5147
     * EXAMPLE: <code>
5148
     * $arrayy = A::create([1 => 'one', 2 => 'two']);
5149
     * $arrayy->randomKey(); // e.g. 2
5150
     * </code>
5151
     *
5152
     * @throws \RangeException If array is empty
5153
     *
5154
     * @return mixed|null
5155
     *                    <p>Get a key/index or null if there wasn't a key/index.</p>
5156
     *
5157
     * @phpstan-return null|TKey
5158
     */
5159 4
    public function randomKey()
5160
    {
5161 4
        $result = $this->randomKeys(1);
5162
5163 4
        if (!isset($result[0])) {
5164
            $result[0] = null;
5165
        }
5166
5167 4
        return $result[0];
5168
    }
5169
5170
    /**
5171
     * Pick a given number of random keys/indexes out of this array.
5172
     *
5173
     * EXAMPLE: <code>
5174
     * a([1 => 'one', 2 => 'two'])->randomKeys(); // e.g. Arrayy[1, 2]
5175
     * </code>
5176
     *
5177
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
5178
     *
5179
     * @throws \RangeException If array is empty
5180
     *
5181
     * @return static
5182
     *                <p>(Immutable)</p>
5183
     *
5184
     * @phpstan-return static<TKey,T>
5185
     */
5186 13
    public function randomKeys(int $number): self
5187
    {
5188 13
        $this->generatorToArray();
5189
5190 13
        $count = $this->count();
5191
5192
        if (
5193 13
            $number === 0
5194
            ||
5195 13
            $number > $count
5196
        ) {
5197 2
            throw new \RangeException(
5198 2
                \sprintf(
5199 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
5200 2
                    $number,
5201 2
                    $count
5202
                )
5203
            );
5204
        }
5205
5206 11
        $result = (array) \array_rand($this->array, $number);
5207
5208 11
        return static::create(
5209 11
            $result,
5210 11
            $this->iteratorClass,
5211 11
            false
5212
        );
5213
    }
5214
5215
    /**
5216
     * Get a random value from the current array.
5217
     *
5218
     * EXAMPLE: <code>
5219
     * a([1, 2, 3, 4])->randomMutable(2); // e.g.: Arrayy[1, 4]
5220
     * </code>
5221
     *
5222
     * @param int|null $number <p>How many values you will take?</p>
5223
     *
5224
     * @return $this
5225
     *               <p>(Mutable) Return this Arrayy object.</p>
5226
     *
5227
     * @phpstan-return static<TKey,T>
5228
     */
5229 17
    public function randomMutable(int $number = null): self
5230
    {
5231 17
        $this->generatorToArray();
5232
5233 17
        if ($this->count() === 0) {
5234
            return static::create(
5235
                [],
5236
                $this->iteratorClass,
5237
                false
5238
            );
5239
        }
5240
5241 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...
5242 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
5243 7
            $this->array = $arrayRandValue;
5244
5245 7
            return $this;
5246
        }
5247
5248 11
        \shuffle($this->array);
5249
5250 11
        return $this->firstsMutable($number);
5251
    }
5252
5253
    /**
5254
     * Pick a random value from the values of this array.
5255
     *
5256
     * EXAMPLE: <code>
5257
     * a([1 => 'one', 2 => 'two'])->randomValue(); // e.g. 'one'
5258
     * </code>
5259
     *
5260
     * @return mixed
5261
     *               <p>Get a random value or null if there wasn't a value.</p>
5262
     *
5263
     * @phpstan-return T|null
5264
     */
5265 4
    public function randomValue()
5266
    {
5267 4
        $result = $this->randomImmutable();
5268
5269 4
        if (!isset($result[0])) {
5270
            $result[0] = null;
5271
        }
5272
5273 4
        return $result[0];
5274
    }
5275
5276
    /**
5277
     * Pick a given number of random values out of this array.
5278
     *
5279
     * EXAMPLE: <code>
5280
     * a([1 => 'one', 2 => 'two'])->randomValues(); // e.g. Arrayy['one', 'two']
5281
     * </code>
5282
     *
5283
     * @param int $number
5284
     *
5285
     * @return static
5286
     *                <p>(Mutable)</p>
5287
     *
5288
     * @phpstan-return static<TKey,T>
5289
     */
5290 7
    public function randomValues(int $number): self
5291
    {
5292 7
        return $this->randomMutable($number);
5293
    }
5294
5295
    /**
5296
     * Get a random value from an array, with the ability to skew the results.
5297
     *
5298
     * EXAMPLE: <code>
5299
     * a([0 => 3, 1 => 4])->randomWeighted([1 => 4]); // e.g.: Arrayy[4] (has a 66% chance of returning 4)
5300
     * </code>
5301
     *
5302
     * @param array    $array
5303
     * @param int|null $number <p>How many values you will take?</p>
5304
     *
5305
     * @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...
5306
     *                           <p>(Immutable)</p>
5307
     *
5308
     * @phpstan-param  array<T,int> $array
5309
     * @phpstan-return static<array-key,T>
5310
     */
5311 9
    public function randomWeighted(array $array, int $number = null): self
5312
    {
5313
        // init
5314 9
        $options = [];
5315
5316 9
        foreach ($array as $option => $weight) {
5317 9
            if ($this->searchIndex($option) !== false) {
5318 2
                for ($i = 0; $i < $weight; ++$i) {
5319 1
                    $options[] = $option;
5320
                }
5321
            }
5322
        }
5323
5324 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
5325
    }
5326
5327
    /**
5328
     * Reduce the current array via callable e.g. anonymous-function and return the end result.
5329
     *
5330
     * EXAMPLE: <code>
5331
     * a([1, 2, 3, 4])->reduce(
5332
     *     function ($carry, $item) {
5333
     *         return $carry * $item;
5334
     *     },
5335
     *     1
5336
     * ); // Arrayy[24]
5337
     * </code>
5338
     *
5339
     * @param callable $callable
5340
     * @param mixed    $initial
5341
     *
5342
     * @return static
5343
     *                <p>(Immutable)</p>
5344
     *
5345
     * @template T2
5346
     *              <p>The output value type.</p>
5347
     *
5348
     * @phpstan-param callable(T2, T, TKey): T2 $callable
5349
     * @phpstan-param T2                  $initial
5350
     *
5351
     * @phpstan-return static<TKey,T2>
5352
     * @psalm-mutation-free
5353
     */
5354 18 View Code Duplication
    public function reduce($callable, $initial = []): 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...
5355
    {
5356 18
        foreach ($this->getGenerator() as $key => $value) {
5357 17
            $initial = $callable($initial, $value, $key);
5358
        }
5359
5360 18
        return static::create(
5361 18
            $initial,
5362 18
            $this->iteratorClass,
5363 18
            false
5364
        );
5365
    }
5366
5367
    /**
5368
     * @param bool $unique
5369
     *
5370
     * @return static
5371
     *                <p>(Immutable)</p>
5372
     *
5373
     * @phpstan-return static<int,mixed>
5374
     * @psalm-mutation-free
5375
     */
5376 14
    public function reduce_dimension(bool $unique = true): self
5377
    {
5378
        // init
5379 14
        $result = [];
5380
5381 14
        foreach ($this->getGenerator() as $val) {
5382 12
            if (\is_array($val)) {
5383 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
5384
            } else {
5385 12
                $result[] = [$val];
5386
            }
5387
        }
5388
5389 14
        $result = $result === [] ? [] : \array_merge(...$result);
5390
5391 14
        $resultArrayy = new static($result);
5392
5393
        /**
5394
         * @psalm-suppress ImpureMethodCall - object is already re-created
5395
         * @psalm-suppress InvalidReturnStatement - why?
5396
         */
5397 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
5398
    }
5399
5400
    /**
5401
     * Create a numerically re-indexed Arrayy object.
5402
     *
5403
     * EXAMPLE: <code>
5404
     * a([2 => 1, 3 => 2])->reindex(); // Arrayy[0 => 1, 1 => 2]
5405
     * </code>
5406
     *
5407
     * @return $this
5408
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
5409
     *
5410
     * @phpstan-return static<TKey,T>
5411
     */
5412 9
    public function reindex(): self
5413
    {
5414 9
        $this->generatorToArray(false);
5415
5416 9
        $this->array = \array_values($this->array);
5417
5418 9
        return $this;
5419
    }
5420
5421
    /**
5422
     * Return all items that fail the truth test.
5423
     *
5424
     * EXAMPLE: <code>
5425
     * $closure = function ($value) {
5426
     *     return $value % 2 !== 0;
5427
     * }
5428
     * a([1, 2, 3, 4])->reject($closure); // Arrayy[1 => 2, 3 => 4]
5429
     * </code>
5430
     *
5431
     * @param \Closure $closure
5432
     *
5433
     * @return static
5434
     *                <p>(Immutable)</p>
5435
     *
5436
     * @phpstan-param \Closure(T=,TKey=):bool  $closure
5437
     * @phpstan-return static<TKey,T>
5438
     * @psalm-mutation-free
5439
     */
5440 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...
5441
    {
5442
        // init
5443 1
        $filtered = [];
5444
5445 1
        foreach ($this->getGenerator() as $key => $value) {
5446 1
            if (!$closure($value, $key)) {
5447 1
                $filtered[$key] = $value;
5448
            }
5449
        }
5450
5451 1
        return static::create(
5452 1
            $filtered,
5453 1
            $this->iteratorClass,
5454 1
            false
5455
        );
5456
    }
5457
5458
    /**
5459
     * Remove a value from the current array (optional using dot-notation).
5460
     *
5461
     * EXAMPLE: <code>
5462
     * a([1 => 'bar', 'foo' => 'foo'])->remove(1); // Arrayy['foo' => 'foo']
5463
     * </code>
5464
     *
5465
     * @param mixed $key
5466
     *
5467
     * @return static
5468
     *                <p>(Mutable)</p>
5469
     *
5470
     * @phpstan-param  TKey|TKey[] $key
5471
     * @phpstan-return static<TKey,T>
5472
     */
5473 22
    public function remove($key)
5474
    {
5475
        // recursive call
5476 22
        if (\is_array($key)) {
5477 1
            foreach ($key as $k) {
5478 1
                $this->internalRemove($k);
5479
            }
5480
5481 1
            return static::create(
5482 1
                $this->toArray(),
5483 1
                $this->iteratorClass,
5484 1
                false
5485
            );
5486
        }
5487
5488 21
        $this->internalRemove($key);
5489
5490 21
        return static::create(
5491 21
            $this->toArray(),
5492 21
            $this->iteratorClass,
5493 21
            false
5494
        );
5495
    }
5496
5497
    /**
5498
     * alias: for "Arrayy->removeValue()"
5499
     *
5500
     * @param mixed $element
5501
     *
5502
     * @return static
5503
     *                <p>(Immutable)</p>
5504
     *
5505
     * @phpstan-param  T $element
5506
     * @phpstan-return static<TKey,T>
5507
     * @psalm-mutation-free
5508
     */
5509 8
    public function removeElement($element)
5510
    {
5511 8
        return $this->removeValue($element);
5512
    }
5513
5514
    /**
5515
     * Remove the first value from the current array.
5516
     *
5517
     * EXAMPLE: <code>
5518
     * a([1 => 'bar', 'foo' => 'foo'])->removeFirst(); // Arrayy['foo' => 'foo']
5519
     * </code>
5520
     *
5521
     * @return static
5522
     *                <p>(Immutable)</p>
5523
     *
5524
     * @phpstan-return static<TKey,T>
5525
     * @psalm-mutation-free
5526
     */
5527 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...
5528
    {
5529 7
        $tmpArray = $this->toArray();
5530
5531 7
        \array_shift($tmpArray);
5532
5533 7
        return static::create(
5534 7
            $tmpArray,
5535 7
            $this->iteratorClass,
5536 7
            false
5537
        );
5538
    }
5539
5540
    /**
5541
     * Remove the last value from the current array.
5542
     *
5543
     * EXAMPLE: <code>
5544
     * a([1 => 'bar', 'foo' => 'foo'])->removeLast(); // Arrayy[1 => 'bar']
5545
     * </code>
5546
     *
5547
     * @return static
5548
     *                <p>(Immutable)</p>
5549
     *
5550
     * @phpstan-return static<TKey,T>
5551
     * @psalm-mutation-free
5552
     */
5553 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...
5554
    {
5555 7
        $tmpArray = $this->toArray();
5556
5557 7
        \array_pop($tmpArray);
5558
5559 7
        return static::create(
5560 7
            $tmpArray,
5561 7
            $this->iteratorClass,
5562 7
            false
5563
        );
5564
    }
5565
5566
    /**
5567
     * Removes a particular value from an array (numeric or associative).
5568
     *
5569
     * EXAMPLE: <code>
5570
     * a([1 => 'bar', 'foo' => 'foo'])->removeValue('foo'); // Arrayy[1 => 'bar']
5571
     * </code>
5572
     *
5573
     * @param mixed $value
5574
     *
5575
     * @return static
5576
     *                <p>(Immutable)</p>
5577
     *
5578
     * @phpstan-param  T $value
5579
     * @phpstan-return static<TKey,T>
5580
     * @psalm-mutation-free
5581
     */
5582 8
    public function removeValue($value): self
5583
    {
5584 8
        $this->generatorToArray();
5585
5586
        // init
5587 8
        $isSequentialArray = $this->isSequential();
5588
5589 8
        foreach ($this->array as $key => $item) {
5590 7
            if ($item === $value) {
5591
                /** @phpstan-ignore-next-line | "Possibly invalid array key type int|string|TKey.", is this a bug in phpstan? */
5592 7
                unset($this->array[$key]);
5593
            }
5594
        }
5595
5596 8
        if ($isSequentialArray) {
5597 6
            $this->array = \array_values($this->array);
5598
        }
5599
5600 8
        return static::create(
5601 8
            $this->array,
5602 8
            $this->iteratorClass,
5603 8
            false
5604
        );
5605
    }
5606
5607
    /**
5608
     * Generate array of repeated arrays.
5609
     *
5610
     * @param int $times <p>How many times has to be repeated.</p>
5611
     *
5612
     * @return static
5613
     *                <p>(Immutable)</p>
5614
     *
5615
     * @phpstan-return static<TKey,T>
5616
     * @psalm-mutation-free
5617
     */
5618 1
    public function repeat($times): self
5619
    {
5620 1
        if ($times === 0) {
5621 1
            return static::create([], $this->iteratorClass);
5622
        }
5623
5624 1
        return static::create(
5625 1
            \array_fill(0, (int) $times, $this->toArray()),
5626 1
            $this->iteratorClass,
5627 1
            false
5628
        );
5629
    }
5630
5631
    /**
5632
     * Replace a key with a new key/value pair.
5633
     *
5634
     * EXAMPLE: <code>
5635
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
5636
     * $arrayy->replace(2, 'notfoo', 'notbar'); // Arrayy[1 => 'foo', 'notfoo' => 'notbar', 3 => 'bar']
5637
     * </code>
5638
     *
5639
     * @param mixed $oldKey
5640
     * @param mixed $newKey
5641
     * @param mixed $newValue
5642
     *
5643
     * @return static
5644
     *                <p>(Immutable)</p>
5645
     *
5646
     * @phpstan-return static<TKey,T>
5647
     * @psalm-mutation-free
5648
     */
5649 5
    public function replace($oldKey, $newKey, $newValue): self
5650
    {
5651 5
        $that = clone $this;
5652
5653
        /**
5654
         * @psalm-suppress ImpureMethodCall - object is already cloned
5655
         */
5656 5
        return $that->remove($oldKey)
5657 5
            ->set($newKey, $newValue);
5658
    }
5659
5660
    /**
5661
     * Create an array using the current array as values and the other array as keys.
5662
     *
5663
     * EXAMPLE: <code>
5664
     * $firstArray = [
5665
     *     1 => 'one',
5666
     *     2 => 'two',
5667
     *     3 => 'three',
5668
     * ];
5669
     * $secondArray = [
5670
     *     'one' => 1,
5671
     *     1     => 'one',
5672
     *     2     => 2,
5673
     * ];
5674
     * $arrayy = a($firstArray);
5675
     * $arrayy->replaceAllKeys($secondArray); // Arrayy[1 => "one", 'one' => "two", 2 => "three"]
5676
     * </code>
5677
     *
5678
     * @param int[]|string[] $keys <p>An array of keys.</p>
5679
     *
5680
     * @return static
5681
     *                <p>(Immutable) Arrayy object with keys from the other array, empty Arrayy object if the number of elements
5682
     *                for each array isn't equal or if the arrays are empty.
5683
     *                </p>
5684
     *
5685
     * @phpstan-param  array<array-key,TKey> $keys
5686
     * @phpstan-return static<TKey,T>
5687
     * @psalm-mutation-free
5688
     */
5689 2 View Code Duplication
    public function replaceAllKeys(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...
5690
    {
5691 2
        $data = \array_combine($keys, $this->toArray());
5692 2
        if ($data === false) {
5693
            $data = [];
5694
        }
5695
5696 2
        return static::create(
5697 2
            $data,
5698 2
            $this->iteratorClass,
5699 2
            false
5700
        );
5701
    }
5702
5703
    /**
5704
     * Create an array using the current array as keys and the other array as values.
5705
     *
5706
     * EXAMPLE: <code>
5707
     * $firstArray = [
5708
     *     1 => 'one',
5709
     *     2 => 'two',
5710
     *     3 => 'three',
5711
     * ];
5712
     * $secondArray = [
5713
     *     'one' => 1,
5714
     *     1     => 'one',
5715
     *     2     => 2,
5716
     * ];
5717
     * $arrayy = a($firstArray);
5718
     * $arrayy->replaceAllValues($secondArray); // Arrayy['one' => 1, 'two' => 'one', 'three' => 2]
5719
     * </code>
5720
     *
5721
     * @param array $array <p>An array of values.</p>
5722
     *
5723
     * @return static
5724
     *                <p>(Immutable) Arrayy object with values from the other array, empty Arrayy object if the number of elements
5725
     *                for each array isn't equal or if the arrays are empty.
5726
     *                </p>
5727
     *
5728
     * @phpstan-param  array<array-key,T> $array
5729
     * @phpstan-return static<TKey,T>
5730
     * @psalm-mutation-free
5731
     */
5732 2 View Code Duplication
    public function replaceAllValues(array $array): 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...
5733
    {
5734 2
        $data = \array_combine($this->toArray(), $array);
5735 2
        if ($data === false) {
5736
            $data = [];
5737
        }
5738
5739 2
        return static::create(
5740 2
            $data,
5741 2
            $this->iteratorClass,
5742 2
            false
5743
        );
5744
    }
5745
5746
    /**
5747
     * Replace the keys in an array with another set.
5748
     *
5749
     * EXAMPLE: <code>
5750
     * a([1 => 'bar', 'foo' => 'foo'])->replaceKeys([1 => 2, 'foo' => 'replaced']); // Arrayy[2 => 'bar', 'replaced' => 'foo']
5751
     * </code>
5752
     *
5753
     * @param array $keys <p>An array of keys matching the array's size.</p>
5754
     *
5755
     * @return static
5756
     *                <p>(Immutable)</p>
5757
     *
5758
     * @phpstan-param  array<array-key,TKey> $keys
5759
     * @phpstan-return static<TKey,T>
5760
     * @psalm-mutation-free
5761
     */
5762 1 View Code Duplication
    public function replaceKeys(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...
5763
    {
5764 1
        $values = \array_values($this->toArray());
5765 1
        $result = \array_combine($keys, $values);
5766 1
        if ($result === false) {
5767
            $result = [];
5768
        }
5769
5770 1
        return static::create(
5771 1
            $result,
5772 1
            $this->iteratorClass,
5773 1
            false
5774
        );
5775
    }
5776
5777
    /**
5778
     * Replace the first matched value in an array.
5779
     *
5780
     * EXAMPLE: <code>
5781
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5782
     * a($testArray)->replaceOneValue('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'foobar']
5783
     * </code>
5784
     *
5785
     * @param mixed $search      <p>The value to replace.</p>
5786
     * @param mixed $replacement <p>The value to replace.</p>
5787
     *
5788
     * @return static
5789
     *                <p>(Immutable)</p>
5790
     *
5791
     * @phpstan-return static<TKey,T>
5792
     * @psalm-mutation-free
5793
     */
5794 3
    public function replaceOneValue($search, $replacement = ''): self
5795
    {
5796 3
        $array = $this->toArray();
5797 3
        $key = \array_search($search, $array, true);
5798
5799 3
        if ($key !== false) {
5800 3
            $array[$key] = $replacement;
5801
        }
5802
5803 3
        return static::create(
5804 3
            $array,
5805 3
            $this->iteratorClass,
5806 3
            false
5807
        );
5808
    }
5809
5810
    /**
5811
     * Replace values in the current array.
5812
     *
5813
     * EXAMPLE: <code>
5814
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5815
     * a($testArray)->replaceValues('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'replacedbar']
5816
     * </code>
5817
     *
5818
     * @param string $search      <p>The value to replace.</p>
5819
     * @param string $replacement <p>What to replace it with.</p>
5820
     *
5821
     * @return static
5822
     *                <p>(Immutable)</p>
5823
     *
5824
     * @phpstan-return static<TKey,T>
5825
     * @psalm-mutation-free
5826
     */
5827 1
    public function replaceValues($search, $replacement = ''): self
5828
    {
5829 1
        $function = static function ($value) use ($search, $replacement) {
5830 1
            return \str_replace($search, $replacement, $value);
5831
        };
5832
5833
        /** @phpstan-ignore-next-line | ignore Closure with one or two parameters, is this a bug in phpstan? */
5834 1
        return $this->each($function);
5835
    }
5836
5837
    /**
5838
     * Get the last elements from index $from until the end of this array.
5839
     *
5840
     * EXAMPLE: <code>
5841
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->rest(2); // Arrayy[0 => 'lall']
5842
     * </code>
5843
     *
5844
     * @param int $from
5845
     *
5846
     * @return static
5847
     *                <p>(Immutable)</p>
5848
     *
5849
     * @phpstan-return static<TKey,T>
5850
     * @psalm-mutation-free
5851
     */
5852 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...
5853
    {
5854 15
        $tmpArray = $this->toArray();
5855
5856 15
        return static::create(
5857 15
            \array_splice($tmpArray, $from),
5858 15
            $this->iteratorClass,
5859 15
            false
5860
        );
5861
    }
5862
5863
    /**
5864
     * Return the array in the reverse order.
5865
     *
5866
     * EXAMPLE: <code>
5867
     * a([1, 2, 3])->reverse(); // self[3, 2, 1]
5868
     * </code>
5869
     *
5870
     * @return $this
5871
     *               <p>(Mutable) Return this Arrayy object.</p>
5872
     *
5873
     * @phpstan-return static<TKey,T>
5874
     */
5875 9
    public function reverse(): self
5876
    {
5877 9
        $this->generatorToArray();
5878
5879 9
        $this->array = \array_reverse($this->array);
5880
5881 9
        return $this;
5882
    }
5883
5884
    /**
5885
     * Sort an array in reverse order.
5886
     *
5887
     * @param int $sort_flags [optional] <p>
5888
     *                        You may modify the behavior of the sort using the optional
5889
     *                        parameter sort_flags, for details
5890
     *                        see sort.
5891
     *                        </p>
5892
     *
5893
     * @return $this
5894
     *               <p>(Mutable) Return this Arrayy object.</p>
5895
     *
5896
     * @phpstan-return static<TKey,T>
5897
     */
5898 4
    public function rsort(int $sort_flags = 0): self
5899
    {
5900 4
        $this->generatorToArray();
5901
5902 4
        \rsort($this->array, $sort_flags);
5903
5904 4
        return $this;
5905
    }
5906
5907
    /**
5908
     * Sort an array in reverse order.
5909
     *
5910
     * @param int $sort_flags [optional] <p>
5911
     *                        You may modify the behavior of the sort using the optional
5912
     *                        parameter sort_flags, for details
5913
     *                        see sort.
5914
     *                        </p>
5915
     *
5916
     * @return $this
5917
     *               <p>(Immutable) Return this Arrayy object.</p>
5918
     *
5919
     * @phpstan-return static<TKey,T>
5920
     * @psalm-mutation-free
5921
     */
5922 4
    public function rsortImmutable(int $sort_flags = 0): self
5923
    {
5924 4
        $that = clone $this;
5925
5926
        /**
5927
         * @psalm-suppress ImpureMethodCall - object is already cloned
5928
         */
5929 4
        $that->rsort($sort_flags);
5930
5931 4
        return $that;
5932
    }
5933
5934
    /**
5935
     * Search for the first index of the current array via $value.
5936
     *
5937
     * EXAMPLE: <code>
5938
     * a(['fòô' => 'bàř', 'lall' => 'bàř'])->searchIndex('bàř'); // Arrayy[0 => 'fòô']
5939
     * </code>
5940
     *
5941
     * @param mixed $value
5942
     *
5943
     * @return false|int|string
5944
     *                          <p>Will return <b>FALSE</b> if the value can't be found.</p>
5945
     *
5946
     * @phpstan-param T $value
5947
     * @phpstan-return false|TKey
5948
     *
5949
     * @psalm-mutation-free
5950
     */
5951 21
    public function searchIndex($value)
5952
    {
5953 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5954 20
            if ($value === $valueFromArray) {
5955 10
                return $keyFromArray;
5956
            }
5957
        }
5958
5959 11
        return false;
5960
    }
5961
5962
    /**
5963
     * Search for the value of the current array via $index.
5964
     *
5965
     * EXAMPLE: <code>
5966
     * a(['fòô' => 'bàř'])->searchValue('fòô'); // Arrayy[0 => 'bàř']
5967
     * </code>
5968
     *
5969
     * @param mixed $index
5970
     *
5971
     * @return static
5972
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5973
     *
5974
     * @phpstan-return static<TKey,T>
5975
     * @psalm-mutation-free
5976
     */
5977 9
    public function searchValue($index): self
5978
    {
5979 9
        $this->generatorToArray();
5980
5981
        // init
5982 9
        $return = [];
5983
5984 9
        if ($this->array === []) {
5985
            return static::create(
5986
                [],
5987
                $this->iteratorClass,
5988
                false
5989
            );
5990
        }
5991
5992
        // php cast "bool"-index into "int"-index
5993 9
        if ((bool) $index === $index) {
5994 1
            $index = (int) $index;
5995
        }
5996
5997 9
        if ($this->offsetExists($index)) {
5998 7
            $return = [$this->array[$index]];
5999
        }
6000
6001 9
        return static::create(
6002 9
            $return,
6003 9
            $this->iteratorClass,
6004 9
            false
6005
        );
6006
    }
6007
6008
    /**
6009
     * Set a value for the current array (optional using dot-notation).
6010
     *
6011
     * EXAMPLE: <code>
6012
     * $arrayy = a(['Lars' => ['lastname' => 'Moelleken']]);
6013
     * $arrayy->set('Lars.lastname', 'Müller'); // Arrayy['Lars', ['lastname' => 'Müller']]]
6014
     * </code>
6015
     *
6016
     * @param string $key   <p>The key to set.</p>
6017
     * @param mixed  $value <p>Its value.</p>
6018
     *
6019
     * @return $this
6020
     *               <p>(Mutable) Return this Arrayy object.</p>
6021
     *
6022
     * @phpstan-param  TKey $key
6023
     * @phpstan-param  T $value
6024
     * @phpstan-return static<TKey,T>
6025
     */
6026 28
    public function set($key, $value): self
6027
    {
6028 28
        $this->internalSet($key, $value);
6029
6030 27
        return $this;
6031
    }
6032
6033
    /**
6034
     * Get a value from a array and set it if it was not.
6035
     *
6036
     * WARNING: this method only set the value, if the $key is not already set
6037
     *
6038
     * EXAMPLE: <code>
6039
     * $arrayy = a([1 => 1, 2 => 2, 3 => 3]);
6040
     * $arrayy->setAndGet(1, 4); // 1
6041
     * $arrayy->setAndGet(0, 4); // 4
6042
     * </code>
6043
     *
6044
     * @param mixed $key      <p>The key</p>
6045
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
6046
     *
6047
     * @return mixed
6048
     *               <p>(Mutable)</p>
6049
     */
6050 11
    public function setAndGet($key, $fallback = null)
6051
    {
6052 11
        $this->generatorToArray();
6053
6054
        // If the key doesn't exist, set it.
6055 11
        if (!$this->has($key)) {
6056 4
            $this->array = $this->set($key, $fallback)->toArray();
6057
        }
6058
6059 11
        return $this->get($key);
6060
    }
6061
6062
    /**
6063
     * Shifts a specified value off the beginning of array.
6064
     *
6065
     * @return mixed
6066
     *               <p>(Mutable) A shifted element from the current array.</p>
6067
     */
6068 5
    public function shift()
6069
    {
6070 5
        $this->generatorToArray();
6071
6072 5
        return \array_shift($this->array);
6073
    }
6074
6075
    /**
6076
     * Shuffle the current array.
6077
     *
6078
     * EXAMPLE: <code>
6079
     * a([1 => 'bar', 'foo' => 'foo'])->shuffle(); // e.g.: Arrayy[['foo' => 'foo', 1 => 'bar']]
6080
     * </code>
6081
     *
6082
     * @param bool       $secure <p>using a CSPRNG | @see https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
6083
     * @param array|null $array  [optional]
6084
     *
6085
     * @return static
6086
     *                <p>(Immutable)</p>
6087
     *
6088
     * @phpstan-param  array<TKey,T> $array
6089
     * @phpstan-return static<TKey,T>
6090
     */
6091 2
    public function shuffle(bool $secure = false, array $array = null): self
6092
    {
6093 2
        if ($array === null) {
6094 2
            $array = $this->toArray(false);
6095
        }
6096
6097 2
        if ($secure !== true) {
6098 2
            \shuffle($array);
6099
        } else {
6100 1
            $size = \count($array, \COUNT_NORMAL);
6101 1
            $keys = \array_keys($array);
6102 1
            for ($i = $size - 1; $i > 0; --$i) {
6103
                try {
6104 1
                    $r = \random_int(0, $i);
6105
                } catch (\Exception $e) {
6106
                    $r = \mt_rand(0, $i);
6107
                }
6108 1
                if ($r !== $i) {
6109
                    $temp = $array[$keys[$r]];
6110
                    $array[$keys[$r]] = $array[$keys[$i]];
6111
                    $array[$keys[$i]] = $temp;
6112
                }
6113
            }
6114
        }
6115
6116 2
        foreach ($array as $key => $value) {
6117
            // check if recursive is needed
6118 2
            if (\is_array($value)) {
6119
                /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */
6120
                /** @phpstan-var array<TKey,T> $value */
6121
                $value = $value;
0 ignored issues
show
Bug introduced by
Why assign $value to itself?

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

This assignement can be removed without consequences.

Loading history...
6122
6123
                $array[$key] = $this->shuffle($secure, $value);
6124
            }
6125
        }
6126
6127 2
        return static::create(
6128 2
            $array,
6129 2
            $this->iteratorClass,
6130 2
            false
6131
        );
6132
    }
6133
6134
    /**
6135
     * Count the values from the current array.
6136
     *
6137
     * alias: for "Arrayy->count()"
6138
     *
6139
     * @param int $mode
6140
     *
6141
     * @return int
6142
     */
6143 20
    public function size(int $mode = \COUNT_NORMAL): int
6144
    {
6145 20
        return $this->count($mode);
6146
    }
6147
6148
    /**
6149
     * Checks whether array has exactly $size items.
6150
     *
6151
     * @param int $size
6152
     *
6153
     * @return bool
6154
     */
6155 1
    public function sizeIs(int $size): bool
6156
    {
6157
        // init
6158 1
        $itemsTempCount = 0;
6159
6160
        /** @noinspection PhpUnusedLocalVariableInspection */
6161
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
6162 1
        foreach ($this->getGeneratorByReference() as &$value) {
6163 1
            ++$itemsTempCount;
6164 1
            if ($itemsTempCount > $size) {
6165 1
                return false;
6166
            }
6167
        }
6168
6169 1
        return $itemsTempCount === $size;
6170
    }
6171
6172
    /**
6173
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
6174
     * smaller than $fromSize.
6175
     *
6176
     * @param int $fromSize
6177
     * @param int $toSize
6178
     *
6179
     * @return bool
6180
     */
6181 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
6182
    {
6183 1
        if ($fromSize > $toSize) {
6184 1
            $tmp = $toSize;
6185 1
            $toSize = $fromSize;
6186 1
            $fromSize = $tmp;
6187
        }
6188
6189
        // init
6190 1
        $itemsTempCount = 0;
6191
6192
        /** @noinspection PhpUnusedLocalVariableInspection */
6193 1
        foreach ($this->getGenerator() as $value) {
6194 1
            ++$itemsTempCount;
6195 1
            if ($itemsTempCount > $toSize) {
6196 1
                return false;
6197
            }
6198
        }
6199
6200 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
6201
    }
6202
6203
    /**
6204
     * Checks whether array has more than $size items.
6205
     *
6206
     * @param int $size
6207
     *
6208
     * @return bool
6209
     */
6210 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...
6211
    {
6212
        // init
6213 1
        $itemsTempCount = 0;
6214
6215
        /** @noinspection PhpUnusedLocalVariableInspection */
6216 1
        foreach ($this->getGenerator() as $value) {
6217 1
            ++$itemsTempCount;
6218 1
            if ($itemsTempCount > $size) {
6219 1
                return true;
6220
            }
6221
        }
6222
6223 1
        return $itemsTempCount > $size;
6224
    }
6225
6226
    /**
6227
     * Checks whether array has less than $size items.
6228
     *
6229
     * @param int $size
6230
     *
6231
     * @return bool
6232
     */
6233 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...
6234
    {
6235
        // init
6236 1
        $itemsTempCount = 0;
6237
6238
        /** @noinspection PhpUnusedLocalVariableInspection */
6239 1
        foreach ($this->getGenerator() as $value) {
6240 1
            ++$itemsTempCount;
6241 1
            if ($itemsTempCount > $size) {
6242 1
                return false;
6243
            }
6244
        }
6245
6246 1
        return $itemsTempCount < $size;
6247
    }
6248
6249
    /**
6250
     * Counts all elements in an array, or something in an object.
6251
     *
6252
     * <p>
6253
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
6254
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
6255
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
6256
     * implemented and used in PHP.
6257
     * </p>
6258
     *
6259
     * @return int
6260
     *             <p>
6261
     *             The number of elements in var, which is
6262
     *             typically an array, since anything else will have one
6263
     *             element.
6264
     *             </p>
6265
     *             <p>
6266
     *             If var is not an array or an object with
6267
     *             implemented Countable interface,
6268
     *             1 will be returned.
6269
     *             There is one exception, if var is &null;,
6270
     *             0 will be returned.
6271
     *             </p>
6272
     *             <p>
6273
     *             Caution: count may return 0 for a variable that isn't set,
6274
     *             but it may also return 0 for a variable that has been initialized with an
6275
     *             empty array. Use isset to test if a variable is set.
6276
     *             </p>
6277
     */
6278 10
    public function sizeRecursive(): int
6279
    {
6280 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
6281
    }
6282
6283
    /**
6284
     * Extract a slice of the array.
6285
     *
6286
     * @param int      $offset       <p>Slice begin index.</p>
6287
     * @param int|null $length       <p>Length of the slice.</p>
6288
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
6289
     *
6290
     * @return static
6291
     *                <p>(Immutable) A slice of the original array with length $length.</p>
6292
     *
6293
     * @phpstan-return static<TKey,T>
6294
     * @psalm-mutation-free
6295
     */
6296 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
6297
    {
6298 5
        return static::create(
6299 5
            \array_slice(
6300 5
                $this->toArray(),
6301 5
                $offset,
6302 5
                $length,
6303 5
                $preserveKeys
6304
            ),
6305 5
            $this->iteratorClass,
6306 5
            false
6307
        );
6308
    }
6309
6310
    /**
6311
     * Sort the current array and optional you can keep the keys.
6312
     *
6313
     * EXAMPLE: <code>
6314
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sort(SORT_ASC, SORT_NATURAL, false); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6315
     * </code>
6316
     *
6317
     * @param int|string $direction
6318
     *                              <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6319
     * @param int        $strategy
6320
     *                              <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6321
     *                              <strong>SORT_NATURAL</strong></p>
6322
     * @param bool       $keepKeys
6323
     *
6324
     * @return static
6325
     *                <p>(Mutable) Return this Arrayy object.</p>
6326
     *
6327
     * @phpstan-return static<int|TKey,T>
6328
     */
6329 20
    public function sort(
6330
        $direction = \SORT_ASC,
6331
        int $strategy = \SORT_REGULAR,
6332
        bool $keepKeys = false
6333
    ): self {
6334 20
        $this->generatorToArray();
6335
6336 20
        return $this->sorting(
6337 20
            $this->array,
6338
            $direction,
6339
            $strategy,
6340
            $keepKeys
6341
        );
6342
    }
6343
6344
    /**
6345
     * Sort the current array and optional you can keep the keys.
6346
     *
6347
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6348
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6349
     *                              <strong>SORT_NATURAL</strong></p>
6350
     * @param bool       $keepKeys
6351
     *
6352
     * @return static
6353
     *                <p>(Immutable) Return this Arrayy object.</p>
6354
     *
6355
     * @phpstan-return static<int|TKey,T>
6356
     */
6357 12
    public function sortImmutable(
6358
        $direction = \SORT_ASC,
6359
        int $strategy = \SORT_REGULAR,
6360
        bool $keepKeys = false
6361
    ): self {
6362 12
        $that = clone $this;
6363
6364 12
        $that->generatorToArray();
6365
6366 12
        return $that->sorting(
6367 12
            $that->array,
6368
            $direction,
6369
            $strategy,
6370
            $keepKeys
6371
        );
6372
    }
6373
6374
    /**
6375
     * Sort the current array by key.
6376
     *
6377
     * EXAMPLE: <code>
6378
     * a([1 => 2, 0 => 1])->sortKeys(\SORT_ASC); // Arrayy[0 => 1, 1 => 2]
6379
     * </code>
6380
     *
6381
     * @see http://php.net/manual/en/function.ksort.php
6382
     * @see http://php.net/manual/en/function.krsort.php
6383
     *
6384
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6385
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6386
     *                              <strong>SORT_NATURAL</strong></p>
6387
     *
6388
     * @return $this
6389
     *               <p>(Mutable) Return this Arrayy object.</p>
6390
     *
6391
     * @phpstan-return static<TKey,T>
6392
     */
6393 18
    public function sortKeys(
6394
        $direction = \SORT_ASC,
6395
        int $strategy = \SORT_REGULAR
6396
    ): self {
6397 18
        $this->generatorToArray();
6398
6399 18
        $this->sorterKeys($this->array, $direction, $strategy);
6400
6401 18
        return $this;
6402
    }
6403
6404
    /**
6405
     * Sort the current array by key.
6406
     *
6407
     * @see          http://php.net/manual/en/function.ksort.php
6408
     * @see          http://php.net/manual/en/function.krsort.php
6409
     *
6410
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6411
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6412
     *                              <strong>SORT_NATURAL</strong></p>
6413
     *
6414
     * @return $this
6415
     *               <p>(Immutable) Return this Arrayy object.</p>
6416
     *
6417
     * @phpstan-return static<TKey,T>
6418
     * @psalm-mutation-free
6419
     */
6420 8
    public function sortKeysImmutable(
6421
        $direction = \SORT_ASC,
6422
        int $strategy = \SORT_REGULAR
6423
    ): self {
6424 8
        $that = clone $this;
6425
6426
        /**
6427
         * @psalm-suppress ImpureMethodCall - object is already cloned
6428
         */
6429 8
        $that->sortKeys($direction, $strategy);
6430
6431 8
        return $that;
6432
    }
6433
6434
    /**
6435
     * Sort the current array by value.
6436
     *
6437
     * EXAMPLE: <code>
6438
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueKeepIndex(SORT_ASC, SORT_REGULAR); // Arrayy[0 => 'a', 3 => 'd', 2 => 'f']
6439
     * </code>
6440
     *
6441
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6442
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6443
     *                              <strong>SORT_NATURAL</strong></p>
6444
     *
6445
     * @return static
6446
     *                <p>(Mutable)</p>
6447
     *
6448
     * @phpstan-return static<int|TKey,T>
6449
     */
6450 1
    public function sortValueKeepIndex(
6451
        $direction = \SORT_ASC,
6452
        int $strategy = \SORT_REGULAR
6453
    ): self {
6454 1
        return $this->sort($direction, $strategy, true);
6455
    }
6456
6457
    /**
6458
     * Sort the current array by value.
6459
     *
6460
     * EXAMPLE: <code>
6461
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueNewIndex(SORT_ASC, SORT_NATURAL); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6462
     * </code>
6463
     *
6464
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6465
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6466
     *                              <strong>SORT_NATURAL</strong></p>
6467
     *
6468
     * @return static
6469
     *                <p>(Mutable)</p>
6470
     *
6471
     * @phpstan-return static<int|TKey,T>
6472
     */
6473 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6474
    {
6475 1
        return $this->sort($direction, $strategy, false);
6476
    }
6477
6478
    /**
6479
     * Sort a array by value or by a closure.
6480
     *
6481
     * - If the sorter is null, the array is sorted naturally.
6482
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
6483
     *
6484
     * EXAMPLE: <code>
6485
     * $testArray = range(1, 5);
6486
     * $under = a($testArray)->sorter(
6487
     *     function ($value) {
6488
     *         return $value % 2 === 0;
6489
     *     }
6490
     * );
6491
     * var_dump($under); // Arrayy[1, 3, 5, 2, 4]
6492
     * </code>
6493
     *
6494
     * @param callable|mixed|null $sorter
6495
     * @param int|string          $direction <p>use <strong>SORT_ASC</strong> (default) or
6496
     *                                       <strong>SORT_DESC</strong></p>
6497
     * @param int                 $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6498
     *                                       <strong>SORT_NATURAL</strong></p>
6499
     *
6500
     * @return static
6501
     *                <p>(Immutable)</p>
6502
     *
6503
     * @pslam-param callable|T|null $sorter
6504
     * @phpstan-return static<TKey,T>
6505
     * @psalm-mutation-free
6506
     */
6507 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6508
    {
6509 1
        $array = $this->toArray();
6510 1
        $direction = $this->getDirection($direction);
6511
6512
        // Transform all values into their results.
6513 1
        if ($sorter) {
6514 1
            $arrayy = static::create(
6515 1
                $array,
6516 1
                $this->iteratorClass,
6517 1
                false
6518
            );
6519
6520
            /**
6521
             * @psalm-suppress MissingClosureReturnType
6522
             * @psalm-suppress MissingClosureParamType
6523
             */
6524 1
            $results = $arrayy->each(
6525 1
                static function ($value) use ($sorter) {
6526 1
                    if (\is_callable($sorter) === true) {
6527 1
                        return $sorter($value);
6528
                    }
6529
6530 1
                    return $sorter === $value;
6531 1
                }
6532
            );
6533
6534 1
            $results = $results->toArray();
6535
        } else {
6536 1
            $results = $array;
6537
        }
6538
6539
        // Sort by the results and replace by original values
6540 1
        \array_multisort($results, $direction, $strategy, $array);
6541
6542 1
        return static::create(
6543 1
            $array,
6544 1
            $this->iteratorClass,
6545 1
            false
6546
        );
6547
    }
6548
6549
    /**
6550
     * @param int      $offset
6551
     * @param int|null $length
6552
     * @param array    $replacement
6553
     *
6554
     * @return static
6555
     *                <p>(Immutable)</p>
6556
     *
6557
     * @phpstan-param  array<array-key,T> $replacement
6558
     * @phpstan-return static<TKey,T>
6559
     * @psalm-mutation-free
6560
     */
6561 1
    public function splice(int $offset, int $length = null, $replacement = []): self
6562
    {
6563 1
        $tmpArray = $this->toArray();
6564
6565 1
        \array_splice(
6566 1
            $tmpArray,
6567 1
            $offset,
6568 1
            $length ?? $this->count(),
6569 1
            $replacement
6570
        );
6571
6572 1
        return static::create(
6573 1
            $tmpArray,
6574 1
            $this->iteratorClass,
6575 1
            false
6576
        );
6577
    }
6578
6579
    /**
6580
     * Split an array in the given amount of pieces.
6581
     *
6582
     * EXAMPLE: <code>
6583
     * a(['a' => 1, 'b' => 2])->split(2, true); // Arrayy[['a' => 1], ['b' => 2]]
6584
     * </code>
6585
     *
6586
     * @param int  $numberOfPieces
6587
     * @param bool $keepKeys
6588
     *
6589
     * @return static
6590
     *                <p>(Immutable)</p>
6591
     *
6592
     * @phpstan-return static<TKey,T>
6593
     * @psalm-mutation-free
6594
     */
6595 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
6596
    {
6597 1
        if ($keepKeys) {
6598 1
            $generator = function () use ($numberOfPieces) {
6599 1
                $carry = [];
6600 1
                $i = 1;
6601 1
                foreach ($this->getGenerator() as $key => $value) {
6602 1
                    $carry[$key] = $value;
6603
6604 1
                    if ($i % $numberOfPieces !== 0) {
6605 1
                        ++$i;
6606
6607 1
                        continue;
6608
                    }
6609
6610 1
                    yield $carry;
6611
6612 1
                    $carry = [];
6613 1
                    $i = 1;
6614
                }
6615
6616 1
                if ($carry !== []) {
6617 1
                    yield $carry;
6618
                }
6619 1
            };
6620 View Code Duplication
        } else {
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...
6621 1
            $generator = function () use ($numberOfPieces) {
6622 1
                $carry = [];
6623 1
                $i = 1;
6624 1
                foreach ($this->getGenerator() as $value) {
6625 1
                    $carry[] = $value;
6626
6627 1
                    if ($i % $numberOfPieces !== 0) {
6628 1
                        ++$i;
6629
6630 1
                        continue;
6631
                    }
6632
6633 1
                    yield $carry;
6634
6635 1
                    $carry = [];
6636 1
                    $i = 1;
6637
                }
6638
6639 1
                if ($carry !== []) {
6640 1
                    yield $carry;
6641
                }
6642 1
            };
6643
        }
6644
6645 1
        return static::create(
6646 1
            $generator,
6647 1
            $this->iteratorClass,
6648 1
            false
6649
        );
6650
    }
6651
6652
    /**
6653
     * Strip all empty items from the current array.
6654
     *
6655
     * EXAMPLE: <code>
6656
     * a(['a' => 1, 'b' => ''])->stripEmpty(); // Arrayy[['a' => 1]]
6657
     * </code>
6658
     *
6659
     * @return static
6660
     *                <p>(Immutable)</p>
6661
     *
6662
     * @phpstan-return static<TKey,T>
6663
     * @psalm-mutation-free
6664
     */
6665 1
    public function stripEmpty(): self
6666
    {
6667 1
        return $this->filter(
6668 1
            static function ($item) {
6669 1
                if ($item === null) {
6670 1
                    return false;
6671
                }
6672
6673 1
                return (bool) \trim((string) $item);
6674 1
            }
6675
        );
6676
    }
6677
6678
    /**
6679
     * Swap two values between positions by key.
6680
     *
6681
     * EXAMPLE: <code>
6682
     * a(['a' => 1, 'b' => ''])->swap('a', 'b'); // Arrayy[['a' => '', 'b' => 1]]
6683
     * </code>
6684
     *
6685
     * @param int|string $swapA <p>a key in the array</p>
6686
     * @param int|string $swapB <p>a key in the array</p>
6687
     *
6688
     * @return static
6689
     *                <p>(Immutable)</p>
6690
     *
6691
     * @phpstan-return static<TKey,T>
6692
     * @psalm-mutation-free
6693
     */
6694 1
    public function swap($swapA, $swapB): self
6695
    {
6696 1
        $array = $this->toArray();
6697
6698 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
6699
6700 1
        return static::create(
6701 1
            $array,
6702 1
            $this->iteratorClass,
6703 1
            false
6704
        );
6705
    }
6706
6707
    /**
6708
     * Get the current array from the "Arrayy"-object.
6709
     * alias for "getArray()"
6710
     *
6711
     * @param bool $convertAllArrayyElements <p>
6712
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6713
     *                                       </p>
6714
     * @param bool $preserveKeys             <p>
6715
     *                                       e.g.: A generator maybe return the same key more then once,
6716
     *                                       so maybe you will ignore the keys.
6717
     *                                       </p>
6718
     *
6719
     * @return array
6720
     *
6721
     * @phpstan-return array<TKey,T>
6722
     * @psalm-mutation-free
6723
     */
6724 943
    public function toArray(
6725
        bool $convertAllArrayyElements = false,
6726
        bool $preserveKeys = true
6727
    ): array {
6728
        // init
6729 943
        $array = [];
6730
6731 943
        if ($convertAllArrayyElements) {
6732 3
            foreach ($this->getGenerator() as $key => $value) {
6733 3
                if ($value instanceof self) {
6734 2
                    $value = $value->toArray(
6735 2
                        $convertAllArrayyElements,
6736
                        $preserveKeys
6737
                    );
6738
                }
6739
6740 3
                if ($preserveKeys) {
6741 2
                    $array[$key] = $value;
6742
                } else {
6743 1
                    $array[] = $value;
6744
                }
6745
            }
6746
        } else {
6747 942
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
6748
        }
6749
6750
        /** @phpstan-ignore-next-line - depends on the $convertAllArrayyElements parameter :/ */
6751 943
        return $array;
6752
    }
6753
6754
    /**
6755
     * Get the current array from the "Arrayy"-object as list.
6756
     *
6757
     * @param bool $convertAllArrayyElements <p>
6758
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6759
     *                                       </p>
6760
     *
6761
     * @return array
6762
     *
6763
     * @phpstan-return list<T>
6764
     * @psalm-mutation-free
6765
     */
6766 1
    public function toList(bool $convertAllArrayyElements = false): array
6767
    {
6768
        /** @var list<T> - currently phpstan can't return different types depending on the phpdocs params */
6769 1
        return $this->toArray(
6770 1
            $convertAllArrayyElements,
6771 1
            false
6772
        );
6773
    }
6774
6775
    /**
6776
     * Convert the current array to JSON.
6777
     *
6778
     * EXAMPLE: <code>
6779
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6780
     * </code>
6781
     *
6782
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6783
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6784
     *
6785
     * @return string
6786
     */
6787 13
    public function toJson(int $options = 0, int $depth = 512): string
6788
    {
6789 13
        $return = \json_encode($this->toArray(), $options, $depth);
6790 13
        if ($return === false) {
6791
            return '';
6792
        }
6793
6794 13
        return $return;
6795
    }
6796
6797
    /**
6798
     * @param string[]|null $items  [optional]
6799
     * @param string[]      $helper [optional]
6800
     *
6801
     * @return static|static[]
6802
     *
6803
     * @phpstan-return static<int, static<TKey,T>>
6804
     */
6805 1
    public function toPermutation(array $items = null, array $helper = []): self
6806
    {
6807
        // init
6808 1
        $return = [];
6809
6810 1
        if ($items === null) {
6811 1
            $items = $this->toArray();
6812
        }
6813
6814 1
        if (empty($items)) {
6815 1
            $return[] = $helper;
6816
        } else {
6817 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6818 1
                $new_items = $items;
6819 1
                $new_helper = $helper;
6820 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6821
                /** @noinspection PhpSillyAssignmentInspection */
6822
                /** @var string[] $new_items */
6823 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...
6824 1
                \array_unshift($new_helper, $tmp_helper);
6825 1
                $return = \array_merge(
6826 1
                    $return,
6827 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6828
                );
6829
            }
6830
        }
6831
6832 1
        return static::create(
6833 1
            $return,
6834 1
            $this->iteratorClass,
6835 1
            false
6836
        );
6837
    }
6838
6839
    /**
6840
     * Implodes array to a string with specified separator.
6841
     *
6842
     * @param string $separator [optional] <p>The element's separator.</p>
6843
     *
6844
     * @return string
6845
     *                <p>The string representation of array, separated by ",".</p>
6846
     */
6847 19
    public function toString(string $separator = ','): string
6848
    {
6849 19
        return $this->implode($separator);
6850
    }
6851
6852
    /**
6853
     * Return a duplicate free copy of the current array.
6854
     *
6855
     * EXAMPLE: <code>
6856
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6857
     * </code>
6858
     *
6859
     * @return $this
6860
     *               <p>(Mutable)</p>
6861
     *
6862
     * @phpstan-return static<int,T>
6863
     */
6864 13
    public function uniqueNewIndex(): self
6865
    {
6866
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6867
6868 13
        $this->array = $this->reduce(
6869 13
            static function ($resultArray, $value, $key) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
6870 12
                if (!\in_array($value, $resultArray, true)) {
6871 12
                    $resultArray[] = $value;
6872
                }
6873
6874 12
                return $resultArray;
6875 13
            },
6876 13
            []
6877 13
        )->toArray();
6878 13
        $this->generator = null;
6879
6880 13
        return $this;
6881
    }
6882
6883
    /**
6884
     * Return a duplicate free copy of the current array. (with the old keys)
6885
     *
6886
     * EXAMPLE: <code>
6887
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6888
     * </code>
6889
     *
6890
     * @return $this
6891
     *               <p>(Mutable)</p>
6892
     *
6893
     * @phpstan-return static<TKey,T>
6894
     */
6895 11
    public function uniqueKeepIndex(): self
6896
    {
6897
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6898
6899
        // init
6900 11
        $array = $this->toArray();
6901
6902
        /**
6903
         * @psalm-suppress MissingClosureReturnType
6904
         * @psalm-suppress MissingClosureParamType
6905
         */
6906 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...
6907 11
            \array_keys($array),
6908 11
            static function ($resultArray, $key) use ($array) {
6909 10
                if (!\in_array($array[$key], $resultArray, true)) {
6910 10
                    $resultArray[$key] = $array[$key];
6911
                }
6912
6913 10
                return $resultArray;
6914 11
            },
6915 11
            []
6916
        );
6917 11
        $this->generator = null;
6918
6919 11
        return $this;
6920
    }
6921
6922
    /**
6923
     * alias: for "Arrayy->uniqueNewIndex()"
6924
     *
6925
     * @return static
6926
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6927
     *
6928
     * @see          Arrayy::unique()
6929
     *
6930
     * @phpstan-return static<int,T>
6931
     */
6932 13
    public function unique(): self
6933
    {
6934 13
        return $this->uniqueNewIndex();
6935
    }
6936
6937
    /**
6938
     * Prepends one or more values to the beginning of array at once.
6939
     *
6940
     * @param mixed ...$args
6941
     *
6942
     * @return $this
6943
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6944
     *
6945
     * @phpstan-param  array<TKey,T> ...$args
6946
     * @phpstan-return static<TKey,T>
6947
     */
6948 6 View Code Duplication
    public function unshift(...$args): 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...
6949
    {
6950 6
        $this->generatorToArray();
6951
6952
        if (
6953 6
            $this->checkPropertyTypes
6954
            &&
6955 6
            $this->properties !== []
6956
        ) {
6957 2
            foreach ($args as $key => $value) {
6958 2
                $this->checkType($key, $value);
6959
            }
6960
        }
6961
6962 5
        \array_unshift($this->array, ...$args);
6963
6964 5
        return $this;
6965
    }
6966
6967
    /**
6968
     * Tests whether the given closure return something valid for all elements of this array.
6969
     *
6970
     * @param \Closure $closure the predicate
6971
     *
6972
     * @return bool
6973
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6974
     *
6975
     * @phpstan-param \Closure(T=,TKey=):bool $closure
6976
     */
6977 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...
6978
    {
6979 1
        foreach ($this->getGenerator() as $key => $value) {
6980 1
            if (!$closure($value, $key)) {
6981 1
                return false;
6982
            }
6983
        }
6984
6985 1
        return true;
6986
    }
6987
6988
    /**
6989
     * Get all values from a array.
6990
     *
6991
     * EXAMPLE: <code>
6992
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
6993
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
6994
     * </code>
6995
     *
6996
     * @return static
6997
     *                <p>(Immutable)</p>
6998
     *
6999
     * @phpstan-return static<TKey,T>
7000
     * @psalm-mutation-free
7001
     */
7002 2
    public function values(): self
7003
    {
7004 2
        return static::create(
7005 2
            function () {
7006 2
                foreach ($this->getGenerator() as $value) {
7007 2
                    yield $value;
7008
                }
7009 2
            },
7010 2
            $this->iteratorClass,
7011 2
            false
7012
        );
7013
    }
7014
7015
    /**
7016
     * Apply the given function to every element in the array, discarding the results.
7017
     *
7018
     * EXAMPLE: <code>
7019
     * $callable = function (&$value, $key) {
7020
     *     $value = $key;
7021
     * };
7022
     * $arrayy = a([1, 2, 3]);
7023
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
7024
     * </code>
7025
     *
7026
     * @param callable $callable
7027
     * @param bool     $recursive
7028
     *                            [optional] <p>Whether array will be walked recursively or no</p>
7029
     * @param mixed    $userData
7030
     *                            [optional] <p>
7031
     *                            If the optional $userData parameter is supplied,
7032
     *                            it will be passed as the third parameter to the $callable.
7033
     *                            </p>
7034
     *
7035
     * @return $this
7036
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
7037
     *
7038
     * @phpstan-return static<TKey,T>
7039
     */
7040 12
    public function walk(
7041
        $callable,
7042
        bool $recursive = false,
7043
        $userData = self::ARRAYY_HELPER_WALK
7044
    ): self {
7045 12
        $this->generatorToArray();
7046
7047 12
        if ($this->array !== []) {
7048 10
            if ($recursive === true) {
7049 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
7050
                    \array_walk_recursive($this->array, $callable, $userData);
7051
                } else {
7052 5
                    \array_walk_recursive($this->array, $callable);
7053
                }
7054
            } else {
7055
                /** @noinspection NestedPositiveIfStatementsInspection */
7056 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
7057
                    \array_walk($this->array, $callable, $userData);
7058
                } else {
7059 5
                    \array_walk($this->array, $callable);
7060
                }
7061
            }
7062
        }
7063
7064 12
        return $this;
7065
    }
7066
7067
    /**
7068
     * Returns a collection of matching items.
7069
     *
7070
     * @param string $keyOrPropertyOrMethod
7071
     *                                      <p>The property or method to evaluate.</p>
7072
     * @param mixed  $value
7073
     *                                      <p>The value to match.</p>
7074
     *
7075
     * @throws \InvalidArgumentException if property or method is not defined
7076
     *
7077
     * @return static
7078
     *
7079
     * @phpstan-return static<TKey,T>
7080
     */
7081 1
    public function where(string $keyOrPropertyOrMethod, $value): self
7082
    {
7083 1
        return $this->filter(
7084 1
            function ($item) use ($keyOrPropertyOrMethod, $value) {
7085
                $accessorValue = $this->extractValue(
7086
                    $item,
7087
                    $keyOrPropertyOrMethod
7088
                );
7089
7090
                return $accessorValue === $value;
7091 1
            }
7092
        );
7093
    }
7094
7095
    /**
7096
     * Convert an array into a object.
7097
     *
7098
     * @param array $array
7099
     *
7100
     * @return \stdClass
7101
     *
7102
     * @phpstan-param array<int|string,mixed> $array
7103
     */
7104 4
    final protected static function arrayToObject(array $array = []): \stdClass
7105
    {
7106
        // init
7107 4
        $object = new \stdClass();
7108
7109 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
7110 1
            return $object;
7111
        }
7112
7113 3
        foreach ($array as $name => $value) {
7114 3
            if (\is_array($value)) {
7115 1
                $object->{$name} = static::arrayToObject($value);
7116
            } else {
7117 3
                $object->{$name} = $value;
7118
            }
7119
        }
7120
7121 3
        return $object;
7122
    }
7123
7124
    /**
7125
     * @param array|\Generator|null $input         <p>
7126
     *                                             An array containing keys to return.
7127
     *                                             </p>
7128
     * @param mixed|null            $search_values [optional] <p>
7129
     *                                             If specified, then only keys containing these values are returned.
7130
     *                                             </p>
7131
     * @param bool                  $strict        [optional] <p>
7132
     *                                             Determines if strict comparison (===) should be used during the
7133
     *                                             search.
7134
     *                                             </p>
7135
     *
7136
     * @return array
7137
     *               <p>An array of all the keys in input.</p>
7138
     *
7139
     * @template TInput
7140
     *
7141
     * @phpstan-param  array<array-key,TInput>|\Generator<array-key,TInput>|null $input
7142
     * @phpstan-return array<array-key>|array<TKey>
7143
     *
7144
     * @psalm-mutation-free
7145
     */
7146 11
    protected function array_keys_recursive(
7147
        $input = null,
7148
        $search_values = null,
7149
        bool $strict = true
7150
    ): array {
7151
        // init
7152 11
        $keys = [];
7153 11
        $keysTmp = [];
7154
7155 11
        if ($input === null) {
7156 4
            $input = $this->getGenerator();
7157
        }
7158
7159 11
        if ($search_values === null) {
7160 11
            foreach ($input as $key => $value) {
7161 11
                $keys[] = $key;
7162
7163
                // check if recursive is needed
7164 11
                if (\is_array($value)) {
7165 4
                    $keysTmp[] = $this->array_keys_recursive($value);
7166
                }
7167
            }
7168
        } else {
7169 1
            $is_array_tmp = \is_array($search_values);
7170
7171 1
            foreach ($input as $key => $value) {
7172 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...
7173
                    (
7174 1
                        $is_array_tmp === false
7175
                        &&
7176 1
                        $strict === true
7177
                        &&
7178 1
                        $search_values === $value
7179
                    )
7180
                    ||
7181
                    (
7182 1
                        $is_array_tmp === false
7183
                        &&
7184 1
                        $strict === false
7185
                        &&
7186 1
                        $search_values == $value
7187
                    )
7188
                    ||
7189
                    (
7190 1
                        $is_array_tmp === true
7191
                        &&
7192 1
                        \in_array($value, $search_values, $strict)
7193
                    )
7194
                ) {
7195 1
                    $keys[] = $key;
7196
                }
7197
7198
                // check if recursive is needed
7199 1
                if (\is_array($value)) {
7200 1
                    $keysTmp[] = $this->array_keys_recursive($value);
7201
                }
7202
            }
7203
        }
7204
7205 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
7206
    }
7207
7208
    /**
7209
     * @param string     $path
7210
     * @param callable   $callable
7211
     * @param array|null $currentOffset
7212
     *
7213
     * @return void
7214
     *
7215
     * @phpstan-param array<TKey,T>|null $currentOffset
7216
     * @psalm-mutation-free
7217
     */
7218 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
7219
    {
7220 10
        $this->generatorToArray();
7221
7222 10
        if ($currentOffset === null) {
7223 10
            $currentOffset = &$this->array;
7224
        }
7225
7226 10
        $explodedPath = \explode($this->pathSeparator, $path);
7227 10
        if ($explodedPath === false) {
7228
            return;
7229
        }
7230
7231 10
        $nextPath = \array_shift($explodedPath);
7232
7233 10
        if (!isset($currentOffset[$nextPath])) {
7234 1
            return;
7235
        }
7236
7237 9
        if (!empty($explodedPath)) {
7238 1
            $this->callAtPath(
7239 1
                \implode($this->pathSeparator, $explodedPath),
7240
                $callable,
7241 1
                $currentOffset[$nextPath]
7242
            );
7243
        } else {
7244 9
            $callable($currentOffset[$nextPath]);
7245
        }
7246 9
    }
7247
7248
    /**
7249
     * Extracts the value of the given property or method from the object.
7250
     *
7251
     * @param static<T> $object
0 ignored issues
show
Documentation introduced by
The doc-type static<T> 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...
7252
     *                                         <p>The object to extract the value from.</p>
7253
     * @param string    $keyOrPropertyOrMethod
7254
     *                                         <p>The property or method for which the
7255
     *                                         value should be extracted.</p>
7256
     *
7257
     * @throws \InvalidArgumentException if the method or property is not defined
7258
     *
7259
     * @return mixed
7260
     *               <p>The value extracted from the specified property or method.</p>
7261
     *
7262
     * @phpstan-param self<TKey,T> $object
7263
     */
7264 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
7265
    {
7266 1
        if (isset($object[$keyOrPropertyOrMethod])) {
7267 1
            $return = $object->get($keyOrPropertyOrMethod);
7268
7269 1
            if ($return instanceof self) {
7270
                return $return->toArray();
7271
            }
7272
7273 1
            return $return;
7274
        }
7275
7276
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
7277
            return $object->{$keyOrPropertyOrMethod};
7278
        }
7279
7280
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
7281
            return $object->{$keyOrPropertyOrMethod}();
7282
        }
7283
7284
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
7285
    }
7286
7287
    /**
7288
     * create a fallback for array
7289
     *
7290
     * 1. use the current array, if it's a array
7291
     * 2. fallback to empty array, if there is nothing
7292
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
7293
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
7294
     * 5. call "__toArray()" on object, if the method exists
7295
     * 6. cast a string or object with "__toString()" into an array
7296
     * 7. throw a "InvalidArgumentException"-Exception
7297
     *
7298
     * @param mixed $data
7299
     *
7300
     * @throws \InvalidArgumentException
7301
     *
7302
     * @return array
7303
     *
7304
     * @phpstan-return array<mixed>|array<TKey,T>
7305
     */
7306 1214
    protected function fallbackForArray(&$data): array
7307
    {
7308 1214
        $data = $this->internalGetArray($data);
7309
7310 1214
        if ($data === null) {
7311 2
            throw new \InvalidArgumentException('Passed value should be a array');
7312
        }
7313
7314 1212
        return $data;
7315
    }
7316
7317
    /**
7318
     * @param bool $preserveKeys <p>
7319
     *                           e.g.: A generator maybe return the same key more then once,
7320
     *                           so maybe you will ignore the keys.
7321
     *                           </p>
7322
     *
7323
     * @return bool
7324
     *
7325
     * @noinspection ReturnTypeCanBeDeclaredInspection
7326
     * @psalm-mutation-free :/
7327
     */
7328 1123
    protected function generatorToArray(bool $preserveKeys = true)
7329
    {
7330 1123
        if ($this->generator) {
7331 2
            $this->array = $this->toArray(false, $preserveKeys);
7332 2
            $this->generator = null;
7333
7334 2
            return true;
7335
        }
7336
7337 1123
        return false;
7338
    }
7339
7340
    /**
7341
     * Get correct PHP constant for direction.
7342
     *
7343
     * @param int|string $direction
7344
     *
7345
     * @return int
7346
     * @psalm-mutation-free
7347
     */
7348 43
    protected function getDirection($direction): int
7349
    {
7350 43
        if ((string) $direction === $direction) {
7351 10
            $direction = \strtolower($direction);
7352
7353 10
            if ($direction === 'desc') {
7354 2
                $direction = \SORT_DESC;
7355
            } else {
7356 9
                $direction = \SORT_ASC;
7357
            }
7358
        }
7359
7360
        if (
7361 43
            $direction !== \SORT_DESC
7362
            &&
7363 43
            $direction !== \SORT_ASC
7364
        ) {
7365
            $direction = \SORT_ASC;
7366
        }
7367
7368 43
        return $direction;
7369
    }
7370
7371
    /**
7372
     * @return TypeCheckInterface[]
7373
     *
7374
     * @noinspection ReturnTypeCanBeDeclaredInspection
7375
     */
7376 24
    protected function getPropertiesFromPhpDoc()
7377
    {
7378 24
        static $PROPERTY_CACHE = [];
7379 24
        $cacheKey = 'Class::' . static::class;
7380
7381 24
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7382 22
            return $PROPERTY_CACHE[$cacheKey];
7383
        }
7384
7385
        // init
7386 4
        $properties = [];
7387
7388 4
        $reflector = new \ReflectionClass($this);
7389 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7390 4
        $docComment = $reflector->getDocComment();
7391 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...
7392 4
            $docblock = $factory->create($docComment);
7393
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7394 4
            foreach ($docblock->getTagsByName('property') as $tag) {
7395 3
                $typeName = $tag->getVariableName();
7396
                /** @var string|null $typeName */
7397 3
                if ($typeName !== null) {
7398 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7399 3
                    if ($typeCheckPhpDoc !== null) {
7400 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7401
                    }
7402
                }
7403
            }
7404
        }
7405
7406
        /** @noinspection PhpAssignmentInConditionInspection */
7407 4
        while ($reflector = $reflector->getParentClass()) {
7408 4
            $docComment = $reflector->getDocComment();
7409 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...
7410 4
                $docblock = $factory->create($docComment);
7411
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7412 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7413 1
                    $typeName = $tag->getVariableName();
7414
                    /** @var string|null $typeName */
7415 1
                    if ($typeName !== null) {
7416 1
                        if (isset($properties[$typeName])) {
7417 1
                            continue;
7418
                        }
7419
7420 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7421 1
                        if ($typeCheckPhpDoc !== null) {
7422 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7423
                        }
7424
                    }
7425
                }
7426
            }
7427
        }
7428
7429 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7430
    }
7431
7432
    /**
7433
     * @param string $glue
7434
     * @param mixed  $pieces
7435
     * @param bool   $useKeys
7436
     *
7437
     * @return string
7438
     *
7439
     * @phpstan-param scalar|object|self<TKey|T>|array<TKey,T> $pieces
7440
     * @psalm-mutation-free
7441
     */
7442 36
    protected function implode_recursive(
7443
        $glue = '',
7444
        $pieces = [],
7445
        bool $useKeys = false
7446
    ): string {
7447 36
        if ($pieces instanceof self) {
7448 1
            $pieces = $pieces->toArray();
7449
        }
7450
7451 36
        if (\is_array($pieces)) {
7452
            /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */
7453
            /** @phpstan-var array<TKey,T> $pieces */
7454 36
            $pieces = $pieces;
0 ignored issues
show
Bug introduced by
Why assign $pieces to itself?

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

This assignement can be removed without consequences.

Loading history...
7455
7456 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7457 36
            $pieces_count_not_zero = $pieces_count > 0;
7458
7459 36
            return \implode(
7460 36
                $glue,
7461 36
                \array_map(
7462 36
                    [$this, 'implode_recursive'],
7463 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7464 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7465
                )
7466
            );
7467
        }
7468
7469
        if (
7470 36
            \is_scalar($pieces) === true
7471
            ||
7472 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7473
        ) {
7474 32
            return (string) $pieces;
7475
        }
7476
7477 8
        return '';
7478
    }
7479
7480
    /**
7481
     * @param mixed                 $needle   <p>
7482
     *                                        The searched value.
7483
     *                                        </p>
7484
     *                                        <p>
7485
     *                                        If needle is a string, the comparison is done
7486
     *                                        in a case-sensitive manner.
7487
     *                                        </p>
7488
     * @param array|\Generator|null $haystack <p>
7489
     *                                        The array.
7490
     *                                        </p>
7491
     * @param bool                  $strict   [optional] <p>
7492
     *                                        If the third parameter strict is set to true
7493
     *                                        then the in_array function will also check the
7494
     *                                        types of the
7495
     *                                        needle in the haystack.
7496
     *                                        </p>
7497
     *
7498
     * @return bool
7499
     *              <p>true if needle is found in the array, false otherwise</p>
7500
     *
7501
     * @phpstan-param (array&T)|array<TKey,T>|\Generator<TKey,T>|null $haystack
7502
     *
7503
     * @psalm-mutation-free
7504
     */
7505 19
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7506
    {
7507 19
        if ($haystack === null) {
7508
            $haystack = $this->getGenerator();
7509
        }
7510
7511 19
        foreach ($haystack as $item) {
7512 15
            if (\is_array($item)) {
7513 4
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7514
            } else {
7515
                /** @noinspection NestedPositiveIfStatementsInspection */
7516 15
                if ($strict === true) {
7517 15
                    $returnTmp = $item === $needle;
7518
                } else {
7519
                    $returnTmp = $item == $needle;
7520
                }
7521
            }
7522
7523 15
            if ($returnTmp === true) {
7524 11
                return true;
7525
            }
7526
        }
7527
7528 8
        return false;
7529
    }
7530
7531
    /**
7532
     * @param mixed $data
7533
     *
7534
     * @return array<mixed>|null
7535
     */
7536 1214
    protected function internalGetArray(&$data)
7537
    {
7538 1214
        if (\is_array($data)) {
7539 1208
            return $data;
7540
        }
7541
7542 110
        if (!$data) {
7543 7
            return [];
7544
        }
7545
7546 109
        if (\is_object($data) === true) {
7547 102
            if ($data instanceof \ArrayObject) {
7548 5
                return $data->getArrayCopy();
7549
            }
7550
7551 98
            if ($data instanceof \Generator) {
7552
                return static::createFromGeneratorImmutable($data)->toArray();
7553
            }
7554
7555 98
            if ($data instanceof \Traversable) {
7556
                return static::createFromObject($data)->toArray();
7557
            }
7558
7559 98
            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...
7560
                return (array) $data->jsonSerialize();
7561
            }
7562
7563 98
            if (\method_exists($data, '__toArray')) {
7564
                return (array) $data->__toArray();
7565
            }
7566
7567 98
            if (\method_exists($data, '__toString')) {
7568
                return [(string) $data];
7569
            }
7570
        }
7571
7572 105
        if (\is_callable($data)) {
7573
            /**
7574
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7575
             */
7576 96
            $this->generator = new ArrayyRewindableGenerator($data);
7577
7578 96
            return [];
7579
        }
7580
7581 11
        if (\is_scalar($data)) {
7582 9
            return [$data];
7583
        }
7584
7585 2
        return null;
7586
    }
7587
7588
    /**
7589
     * Internal mechanics of remove method.
7590
     *
7591
     * @param float|int|string $key
7592
     *
7593
     * @return bool
7594
     */
7595 22
    protected function internalRemove($key): bool
7596
    {
7597 22
        $this->generatorToArray();
7598
7599
        if (
7600 22
            $this->pathSeparator
7601
            &&
7602 22
            (string) $key === $key
7603
            &&
7604 22
            \strpos($key, $this->pathSeparator) !== false
7605
        ) {
7606
            $path = \explode($this->pathSeparator, (string) $key);
7607
7608
            if ($path !== false) {
7609
                // crawl though the keys
7610
                while (\count($path, \COUNT_NORMAL) > 1) {
7611
                    $key = \array_shift($path);
7612
7613
                    if (!$this->has($key)) {
7614
                        return false;
7615
                    }
7616
7617
                    $this->array = &$this->array[$key];
7618
                }
7619
7620
                $key = \array_shift($path);
7621
            }
7622
        }
7623
7624 22
        unset($this->array[$key]);
7625
7626 22
        return true;
7627
    }
7628
7629
    /**
7630
     * Internal mechanic of set method.
7631
     *
7632
     * @param int|string|null $key
7633
     * @param mixed           $value
7634
     * @param bool            $checkProperties
7635
     *
7636
     * @return bool
7637
     */
7638 1064
    protected function internalSet(
7639
        $key,
7640
        &$value,
7641
        bool $checkProperties = true
7642
    ): bool {
7643
        if (
7644 1064
            $checkProperties === true
7645
            &&
7646 1064
            $this->properties !== []
7647
        ) {
7648 117
            $this->checkType($key, $value);
7649
        }
7650
7651 1062
        if ($key === null) {
7652
            return false;
7653
        }
7654
7655 1062
        $this->generatorToArray();
7656
7657 1062
        $array = &$this->array;
7658
7659
        /**
7660
         * https://github.com/vimeo/psalm/issues/2536
7661
         *
7662
         * @psalm-suppress PossiblyInvalidArgument
7663
         * @psalm-suppress InvalidScalarArgument
7664
         */
7665
        if (
7666 1062
            $this->pathSeparator
7667
            &&
7668 1062
            (string) $key === $key
7669
            &&
7670 1062
            \strpos($key, $this->pathSeparator) !== false
7671
        ) {
7672 9
            $path = \explode($this->pathSeparator, (string) $key);
7673
7674 9
            if ($path !== false) {
7675
                // crawl through the keys
7676 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7677 9
                    $key = \array_shift($path);
7678
7679 9
                    $array = &$array[$key];
7680
                }
7681
7682 9
                $key = \array_shift($path);
7683
            }
7684
        }
7685
7686 1062
        if ($array === null) {
7687 4
            $array = [];
7688 1059
        } elseif (!\is_array($array)) {
7689 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7690
        }
7691
7692 1062
        $array[$key] = $value;
7693
7694 1062
        return true;
7695
    }
7696
7697
    /**
7698
     * Convert a object into an array.
7699
     *
7700
     * @param mixed|object $object
7701
     *
7702
     * @return array|mixed
7703
     *
7704
     * @psalm-mutation-free
7705
     */
7706 5
    protected static function objectToArray($object)
7707
    {
7708 5
        if (!\is_object($object)) {
7709 4
            return $object;
7710
        }
7711
7712 5
        $object = \get_object_vars($object);
7713
7714
        /**
7715
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7716
         */
7717 5
        return \array_map(['static', 'objectToArray'], $object);
7718
    }
7719
7720
    /**
7721
     * @param array $data
7722
     * @param bool  $checkPropertiesInConstructor
7723
     *
7724
     * @return void
7725
     *
7726
     * @phpstan-param array<mixed,T> $data
7727
     */
7728 1212
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7729
    {
7730 1212
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7731
                                        &&
7732 119
                                        $checkPropertiesInConstructor === true;
7733
7734 1212
        if ($this->properties !== []) {
7735 104
            foreach ($data as $key => &$valueInner) {
7736 104
                $this->internalSet(
7737 104
                    $key,
7738
                    $valueInner,
7739
                    $checkPropertiesInConstructor
7740
                );
7741
            }
7742
        } else {
7743
            if (
7744 1127
                $this->checkPropertyTypes === true
7745
                ||
7746 1127
                $checkPropertiesInConstructor === true
7747
            ) {
7748 23
                $this->properties = $this->getPropertiesFromPhpDoc();
7749
            }
7750
7751
            /** @var TypeCheckInterface[] $properties */
7752 1127
            $properties = $this->properties;
7753
7754
            if (
7755 1127
                $this->checkPropertiesMismatchInConstructor === true
7756
                &&
7757 1127
                \count($data) !== 0
7758
                &&
7759 1127
                \count(\array_diff_key($properties, $data)) > 0
7760
            ) {
7761 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...
7762
            }
7763
7764 1126
            foreach ($data as $key => &$valueInner) {
7765 955
                $this->internalSet(
7766 955
                    $key,
7767
                    $valueInner,
7768
                    $checkPropertiesInConstructor
7769
                );
7770
            }
7771
        }
7772 1204
    }
7773
7774
    /**
7775
     * sorting keys
7776
     *
7777
     * @param array      $elements
7778
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7779
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7780
     *                              <strong>SORT_NATURAL</strong></p>
7781
     *
7782
     * @return $this
7783
     *               <p>(Mutable) Return this Arrayy object.</p>
7784
     *
7785
     * @phpstan-param  array<mixed|TKey,T> $elements
7786
     * @phpstan-return static<TKey,T>
7787
     */
7788 18
    protected function sorterKeys(
7789
        array &$elements,
7790
        $direction = \SORT_ASC,
7791
        int $strategy = \SORT_REGULAR
7792
    ): self {
7793 18
        $direction = $this->getDirection($direction);
7794
7795 18
        switch ($direction) {
7796 18
            case 'desc':
7797
            case \SORT_DESC:
7798 6
                \krsort($elements, $strategy);
7799
7800 6
                break;
7801 13
            case 'asc':
7802 13
            case \SORT_ASC:
7803
            default:
7804 13
                \ksort($elements, $strategy);
7805
        }
7806
7807 18
        return $this;
7808
    }
7809
7810
    /**
7811
     * @param array      $elements  <p>Warning: used as reference</p>
7812
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7813
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7814
     *                              <strong>SORT_NATURAL</strong></p>
7815
     * @param bool       $keepKeys
7816
     *
7817
     * @return $this
7818
     *               <p>(Mutable) Return this Arrayy object.</p>
7819
     *
7820
     * @phpstan-param array<mixed|TKey,T> $elements
7821
     * @phpstan-return static<int|TKey,T>
7822
     */
7823 24
    protected function sorting(
7824
        array &$elements,
7825
        $direction = \SORT_ASC,
7826
        int $strategy = \SORT_REGULAR,
7827
        bool $keepKeys = false
7828
    ): self {
7829 24
        $direction = $this->getDirection($direction);
7830
7831 24
        if (!$strategy) {
7832 24
            $strategy = \SORT_REGULAR;
7833
        }
7834
7835 24
        switch ($direction) {
7836 24
            case 'desc':
7837
            case \SORT_DESC:
7838 13
                if ($keepKeys) {
7839 9
                    \arsort($elements, $strategy);
7840
                } else {
7841 4
                    \rsort($elements, $strategy);
7842
                }
7843
7844 13
                break;
7845 11
            case 'asc':
7846 11
            case \SORT_ASC:
7847
            default:
7848 11
                if ($keepKeys) {
7849 4
                    \asort($elements, $strategy);
7850
                } else {
7851 7
                    \sort($elements, $strategy);
7852
                }
7853
        }
7854
7855 24
        return $this;
7856
    }
7857
7858
    /**
7859
     * @param array $array
7860
     *
7861
     * @return array
7862
     *
7863
     * @psalm-mutation-free
7864
     */
7865 25
    private function getArrayRecursiveHelperArrayy(array $array)
7866
    {
7867 25
        if ($array === []) {
7868
            return [];
7869
        }
7870
7871 25
        \array_walk_recursive(
7872 25
            $array,
7873
            /**
7874
             * @param array|self $item
7875
             *
7876
             * @return void
7877
             */
7878 25
            static function (&$item) {
7879 25
                if ($item instanceof self) {
7880 1
                    $item = $item->getArray();
7881
                }
7882 25
            }
7883
        );
7884
7885 25
        return $array;
7886
    }
7887
7888
    /**
7889
     * @param int|string|null $key
7890
     * @param mixed           $value
7891
     *
7892
     * @return void
7893
     */
7894 117
    private function checkType($key, $value)
7895
    {
7896
        if (
7897 117
            $key !== null
7898
            &&
7899 117
            isset($this->properties[$key]) === false
7900
            &&
7901 117
            $this->checkPropertiesMismatch === true
7902
        ) {
7903
            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...
7904
        }
7905
7906 117
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7907 102
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7908 24
        } elseif ($key !== null && isset($this->properties[$key])) {
7909 24
            $this->properties[$key]->checkType($value);
7910
        }
7911 115
    }
7912
}
7913