Completed
Push — master ( 8c6794...616477 )
by Lars
17:50 queued 16:10
created

Arrayy::swap()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 12
ccs 7
cts 7
cp 1
crap 1
rs 9.8666
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
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 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1420
    {
1421 23
        if ($recursive === true) {
1422 18
            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<T,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 731
    public static function create(
1665
        $data = [],
1666
        string $iteratorClass = ArrayyIterator::class,
1667
        bool $checkPropertiesInConstructor = true
1668
    ) {
1669 731
        return new static(
1670 731
            $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 26
    public function createByReference(array &$array = []): self
1730
    {
1731 26
        $this->array = &$array;
1732 26
        $this->generator = null;
1733
1734 26
        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 12
            };
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
        $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 22
    public function first()
2705
    {
2706 22
        $key_first = $this->firstKey();
2707 22
        if ($key_first === null) {
2708 3
            return null;
2709
        }
2710
2711 19
        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 29
    public function firstKey()
2725
    {
2726 29
        $this->generatorToArray();
2727
2728
        /** @phpstan-var TKey|null $return */
2729 29
        $return = \array_key_first($this->array);
2730
2731 29
        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 130
                $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 18
                return static::create(
2929 18
                    [],
2930 18
                    $this->iteratorClass,
2931 18
                    false
2932 18
                )->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 null|mixed
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 null|mixed
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|array-key $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 28
            };
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 3
        return $this->countValues()->arsortImmutable()->firstKey();
4597
    }
4598
4599
    /**
4600
     * Get the most used value from the array.
4601
     *
4602
     * @param int|null $number <p>How many values you will take?</p>
4603
     *
4604
     * @return static
4605
     *                <p>(Immutable)</p>
4606
     *
4607
     * @phpstan-return static<array-key,T>
4608
     * @psalm-mutation-free
4609
     */
4610 3
    public function mostUsedValues(int $number = null): self
4611
    {
4612 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4613
    }
4614
4615
    /**
4616
     * Move an array element to a new index.
4617
     *
4618
     * EXAMPLE: <code>
4619
     * $arr2 = new A(['A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e']);
4620
     * $newArr2 = $arr2->moveElement('D', 1); // Arrayy['A' => 'a', 'D' => 'd', 'B' => 'b', 'C' => 'c', 'E' => 'e']
4621
     * </code>
4622
     *
4623
     * @param int|string $from
4624
     * @param int        $to
4625
     *
4626
     * @return static
4627
     *                <p>(Immutable)</p>
4628
     *
4629
     * @phpstan-return static<TKey,T>
4630
     * @psalm-mutation-free
4631
     */
4632 1
    public function moveElement($from, $to): self
4633
    {
4634 1
        $array = $this->toArray();
4635
4636 1
        if ((int) $from === $from) {
4637 1
            $tmp = \array_splice($array, $from, 1);
4638 1
            \array_splice($array, (int) $to, 0, $tmp);
4639 1
            $output = $array;
4640 1
        } elseif ((string) $from === $from) {
4641 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4642 1
            $itemToMove = $array[$from];
4643 1
            if ($indexToMove !== false) {
4644 1
                \array_splice($array, $indexToMove, 1);
4645
            }
4646 1
            $i = 0;
4647 1
            $output = [];
4648 1
            foreach ($array as $key => $item) {
4649 1
                if ($i === $to) {
4650 1
                    $output[$from] = $itemToMove;
4651
                }
4652 1
                $output[$key] = $item;
4653 1
                ++$i;
4654
            }
4655
        } else {
4656
            $output = [];
4657
        }
4658
4659 1
        return static::create(
4660 1
            $output,
4661 1
            $this->iteratorClass,
4662 1
            false
4663
        );
4664
    }
4665
4666
    /**
4667
     * Move an array element to the first place.
4668
     *
4669
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4670
     *       loss the keys of an indexed array.
4671
     *
4672
     * @param int|string $key
4673
     *
4674
     * @return static
4675
     *                <p>(Immutable)</p>
4676
     *
4677
     * @phpstan-return static<TKey,T>
4678
     * @psalm-mutation-free
4679
     */
4680 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...
4681
    {
4682 1
        $array = $this->toArray();
4683
4684 1
        if ($this->offsetExists($key)) {
4685 1
            $tmpValue = $this->get($key);
4686 1
            unset($array[$key]);
4687 1
            $array = [$key => $tmpValue] + $array;
4688
        }
4689
4690 1
        return static::create(
4691 1
            $array,
4692 1
            $this->iteratorClass,
4693 1
            false
4694
        );
4695
    }
4696
4697
    /**
4698
     * Move an array element to the last place.
4699
     *
4700
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4701
     *       loss the keys of an indexed array.
4702
     *
4703
     * @param int|string $key
4704
     *
4705
     * @return static
4706
     *                <p>(Immutable)</p>
4707
     *
4708
     * @phpstan-return static<TKey,T>
4709
     * @psalm-mutation-free
4710
     */
4711 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...
4712
    {
4713 1
        $array = $this->toArray();
4714
4715 1
        if ($this->offsetExists($key)) {
4716 1
            $tmpValue = $this->get($key);
4717 1
            unset($array[$key]);
4718 1
            $array += [$key => $tmpValue];
4719
        }
4720
4721 1
        return static::create(
4722 1
            $array,
4723 1
            $this->iteratorClass,
4724 1
            false
4725
        );
4726
    }
4727
4728
    /**
4729
     * Moves the internal iterator position to the next element and returns this element.
4730
     *
4731
     * @return false|mixed
4732
     *                     <p>(Mutable) Will return false if there are no values.</p>
4733
     *
4734
     * @phpstan-return false|T
4735
     */
4736
    public function next()
4737
    {
4738
        if ($this->generator) {
4739
            $this->generator->next();
4740
4741
            return $this->generator->current() ?? false;
4742
        }
4743
4744
        return \next($this->array);
4745
    }
4746
4747
    /**
4748
     * Get the next nth keys and values from the array.
4749
     *
4750
     * @param int $step
4751
     * @param int $offset
4752
     *
4753
     * @return static
4754
     *                <p>(Immutable)</p>
4755
     *
4756
     * @phpstan-return static<TKey,T>
4757
     * @psalm-mutation-free
4758
     */
4759 1
    public function nth(int $step, int $offset = 0): self
4760
    {
4761 1
        $arrayFunction = function () use ($step, $offset): \Generator {
4762 1
            $position = 0;
4763 1
            foreach ($this->getGenerator() as $key => $value) {
4764 1
                if ($position++ % $step !== $offset) {
4765 1
                    continue;
4766
                }
4767
4768 1
                yield $key => $value;
4769
            }
4770 1
        };
4771
4772 1
        return static::create(
4773 1
            $arrayFunction,
4774 1
            $this->iteratorClass,
4775 1
            false
4776
        );
4777
    }
4778
4779
    /**
4780
     * Get a subset of the items from the given array.
4781
     *
4782
     * @param int[]|string[] $keys
4783
     *
4784
     * @return static
4785
     *                <p>(Immutable)</p>
4786
     *
4787
     * @phpstan-param array-key[] $keys
4788
     * @phpstan-return static<TKey,T>
4789
     * @psalm-mutation-free
4790
     */
4791 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...
4792
    {
4793 1
        $keys = \array_flip($keys);
4794
4795 1
        $generator = function () use ($keys): \Generator {
4796 1
            foreach ($this->getGenerator() as $key => $value) {
4797 1
                if (isset($keys[$key])) {
4798 1
                    yield $key => $value;
4799
                }
4800
            }
4801 1
        };
4802
4803 1
        return static::create(
4804 1
            $generator,
4805 1
            $this->iteratorClass,
4806 1
            false
4807
        );
4808
    }
4809
4810
    /**
4811
     * Pad array to the specified size with a given value.
4812
     *
4813
     * @param int   $size  <p>Size of the result array.</p>
4814
     * @param mixed $value <p>Empty value by default.</p>
4815
     *
4816
     * @return static
4817
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4818
     *
4819
     * @phpstan-return static<TKey,T>
4820
     * @psalm-mutation-free
4821
     */
4822 5
    public function pad(int $size, $value): self
4823
    {
4824 5
        return static::create(
4825 5
            \array_pad($this->toArray(), $size, $value),
4826 5
            $this->iteratorClass,
4827 5
            false
4828
        );
4829
    }
4830
4831
    /**
4832
     * Partitions this array in two array according to a predicate.
4833
     * Keys are preserved in the resulting array.
4834
     *
4835
     * @param \Closure $closure
4836
     *                          <p>The predicate on which to partition.</p>
4837
     *
4838
     * @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...
4839
     *                    <p>An array with two elements. The first element contains the array
4840
     *                    of elements where the predicate returned TRUE, the second element
4841
     *                    contains the array of elements where the predicate returned FALSE.</p>
4842
     *
4843
     * @phpstan-param \Closure(T=,TKey=):bool $closure
4844
     * @phpstan-return array<int, static<TKey,T>>
4845
     */
4846 1
    public function partition(\Closure $closure): array
4847
    {
4848
        // init
4849 1
        $matches = [];
4850 1
        $noMatches = [];
4851
4852 1
        foreach ($this->getGenerator() as $key => $value) {
4853 1
            if ($closure($value, $key)) {
4854 1
                $matches[$key] = $value;
4855
            } else {
4856 1
                $noMatches[$key] = $value;
4857
            }
4858
        }
4859
4860 1
        return [self::create($matches), self::create($noMatches)];
4861
    }
4862
4863
    /**
4864
     * Pop a specified value off the end of the current array.
4865
     *
4866
     * @return mixed|null
4867
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4868
     *
4869
     * @phpstan-return T|null
4870
     */
4871 5
    public function pop()
4872
    {
4873 5
        $this->generatorToArray();
4874
4875 5
        return \array_pop($this->array);
4876
    }
4877
4878
    /**
4879
     * Prepend a (key) + value to the current array.
4880
     *
4881
     * EXAMPLE: <code>
4882
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4883
     * </code>
4884
     *
4885
     * @param mixed $value
4886
     * @param mixed $key
4887
     *
4888
     * @return $this
4889
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4890
     *
4891
     * @phpstan-param T $value
4892
     * @phpstan-param TKey|null $key
4893
     * @phpstan-return static<TKey,T>
4894
     */
4895 11
    public function prepend($value, $key = null)
4896
    {
4897 11
        $this->generatorToArray();
4898
4899 11
        if ($this->properties !== []) {
4900 3
            $this->checkType($key, $value);
4901
        }
4902
4903 9
        if ($key === null) {
4904 8
            \array_unshift($this->array, $value);
4905
        } else {
4906 2
            $this->array = [$key => $value] + $this->array;
4907
        }
4908
4909 9
        return $this;
4910
    }
4911
4912
    /**
4913
     * Prepend a (key) + value to the current array.
4914
     *
4915
     * EXAMPLE: <code>
4916
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4917
     * </code>
4918
     *
4919
     * @param mixed $value
4920
     * @param mixed $key
4921
     *
4922
     * @return $this
4923
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4924
     *
4925
     * @phpstan-param T $value
4926
     * @phpstan-param TKey $key
4927
     * @phpstan-return static<TKey,T>
4928
     * @psalm-mutation-free
4929
     */
4930 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...
4931
    {
4932 1
        $generator = function () use ($key, $value): \Generator {
4933 1
            if ($this->properties !== []) {
4934
                $this->checkType($key, $value);
4935
            }
4936
4937 1
            if ($key !== null) {
4938
                yield $key => $value;
4939
            } else {
4940 1
                yield $value;
4941
            }
4942
4943 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
4944 1
                yield $keyOld => $itemOld;
4945
            }
4946 1
        };
4947
4948 1
        return static::create(
4949 1
            $generator,
4950 1
            $this->iteratorClass,
4951 1
            false
4952
        );
4953
    }
4954
4955
    /**
4956
     * Add a suffix to each key.
4957
     *
4958
     * @param float|int|string $suffix
4959
     *
4960
     * @return static
4961
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4962
     *
4963
     * @phpstan-return static<TKey,T>
4964
     * @psalm-mutation-free
4965
     */
4966 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...
4967
    {
4968
        // init
4969 10
        $result = [];
4970
4971 10
        foreach ($this->getGenerator() as $key => $item) {
4972 9
            if ($item instanceof self) {
4973
                $result[$key] = $item->prependToEachKey($suffix);
4974 9
            } elseif (\is_array($item)) {
4975
                $result[$key] = self::create(
4976
                    $item,
4977
                    $this->iteratorClass,
4978
                    false
4979
                )->prependToEachKey($suffix)
4980
                    ->toArray();
4981
            } else {
4982 9
                $result[$key . $suffix] = $item;
4983
            }
4984
        }
4985
4986 10
        return self::create(
4987 10
            $result,
4988 10
            $this->iteratorClass,
4989 10
            false
4990
        );
4991
    }
4992
4993
    /**
4994
     * Add a suffix to each value.
4995
     *
4996
     * @param float|int|string $suffix
4997
     *
4998
     * @return static
4999
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
5000
     *
5001
     * @phpstan-return static<TKey,T>
5002
     * @psalm-mutation-free
5003
     */
5004 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...
5005
    {
5006
        // init
5007 10
        $result = [];
5008
5009 10
        foreach ($this->getGenerator() as $key => $item) {
5010 9
            if ($item instanceof self) {
5011
                $result[$key] = $item->prependToEachValue($suffix);
5012 9
            } elseif (\is_array($item)) {
5013
                $result[$key] = self::create(
5014
                    $item,
5015
                    $this->iteratorClass,
5016
                    false
5017
                )->prependToEachValue($suffix)
5018
                    ->toArray();
5019 9
            } elseif (\is_object($item) === true) {
5020 1
                $result[$key] = $item;
5021
            } else {
5022 8
                $result[$key] = $item . $suffix;
5023
            }
5024
        }
5025
5026 10
        return self::create(
5027 10
            $result,
5028 10
            $this->iteratorClass,
5029 10
            false
5030
        );
5031
    }
5032
5033
    /**
5034
     * Return the value of a given key and
5035
     * delete the key.
5036
     *
5037
     * @param int|int[]|string|string[]|null $keyOrKeys
5038
     * @param mixed                          $fallback
5039
     *
5040
     * @return mixed
5041
     */
5042 6
    public function pull($keyOrKeys = null, $fallback = null)
5043
    {
5044 6
        if ($keyOrKeys === null) {
5045 1
            $array = $this->toArray();
5046 1
            $this->clear();
5047
5048 1
            return $array;
5049
        }
5050
5051 5
        if (\is_array($keyOrKeys)) {
5052 1
            $valueOrValues = [];
5053 1
            foreach ($keyOrKeys as $key) {
5054 1
                $valueOrValues[] = $this->get($key, $fallback);
5055 1
                $this->offsetUnset($key);
5056
            }
5057
        } else {
5058 5
            $valueOrValues = $this->get($keyOrKeys, $fallback);
5059 5
            $this->offsetUnset($keyOrKeys);
5060
        }
5061
5062 5
        return $valueOrValues;
5063
    }
5064
5065
    /**
5066
     * Push one or more values onto the end of array at once.
5067
     *
5068
     * @param mixed ...$args
5069
     *
5070
     * @return $this
5071
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
5072
     *
5073
     * @noinspection ReturnTypeCanBeDeclaredInspection
5074
     *
5075
     * @phpstan-param  array<TKey,T> ...$args
5076
     * @phpstan-return static<TKey,T>
5077
     */
5078 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...
5079
    {
5080 9
        $this->generatorToArray();
5081
5082
        if (
5083 9
            $this->checkPropertyTypes
5084
            &&
5085 9
            $this->properties !== []
5086
        ) {
5087 3
            foreach ($args as $key => $value) {
5088 3
                $this->checkType($key, $value);
5089
            }
5090
        }
5091
5092 8
        \array_push($this->array, ...$args);
5093
5094 8
        return $this;
5095
    }
5096
5097
    /**
5098
     * Get a random value from the current array.
5099
     *
5100
     * EXAMPLE: <code>
5101
     * a([1, 2, 3, 4])->randomImmutable(2); // e.g.: Arrayy[1, 4]
5102
     * </code>
5103
     *
5104
     * @param int|null $number <p>How many values you will take?</p>
5105
     *
5106
     * @return static
5107
     *                <p>(Immutable)</p>
5108
     *
5109
     * @phpstan-return static<array-key,T>
5110
     */
5111 19
    public function randomImmutable(int $number = null): self
5112
    {
5113 19
        $this->generatorToArray();
5114
5115 19
        if ($this->count() === 0) {
5116 1
            return static::create(
5117 1
                [],
5118 1
                $this->iteratorClass,
5119 1
                false
5120
            );
5121
        }
5122
5123 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...
5124 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
5125
5126 13
            return static::create(
5127 13
                $arrayRandValue,
5128 13
                $this->iteratorClass,
5129 13
                false
5130
            );
5131
        }
5132
5133 6
        $arrayTmp = $this->array;
5134 6
        \shuffle($arrayTmp);
5135
5136 6
        return static::create(
5137 6
            $arrayTmp,
5138 6
            $this->iteratorClass,
5139 6
            false
5140 6
        )->firstsImmutable($number);
5141
    }
5142
5143
    /**
5144
     * Pick a random key/index from the keys of this array.
5145
     *
5146
     * EXAMPLE: <code>
5147
     * $arrayy = A::create([1 => 'one', 2 => 'two']);
5148
     * $arrayy->randomKey(); // e.g. 2
5149
     * </code>
5150
     *
5151
     * @throws \RangeException If array is empty
5152
     *
5153
     * @return null|mixed
5154
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
5155
     *
5156
     * @phpstan-return null|TKey
5157
     */
5158 4
    public function randomKey()
5159
    {
5160 4
        $result = $this->randomKeys(1);
5161
5162 4
        if (!isset($result[0])) {
5163
            $result[0] = null;
5164
        }
5165
5166 4
        return $result[0];
5167
    }
5168
5169
    /**
5170
     * Pick a given number of random keys/indexes out of this array.
5171
     *
5172
     * EXAMPLE: <code>
5173
     * a([1 => 'one', 2 => 'two'])->randomKeys(); // e.g. Arrayy[1, 2]
5174
     * </code>
5175
     *
5176
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
5177
     *
5178
     * @throws \RangeException If array is empty
5179
     *
5180
     * @return static
5181
     *                <p>(Immutable)</p>
5182
     *
5183
     * @phpstan-return static<TKey,T>
5184
     */
5185 13
    public function randomKeys(int $number): self
5186
    {
5187 13
        $this->generatorToArray();
5188
5189 13
        $count = $this->count();
5190
5191
        if (
5192 13
            $number === 0
5193
            ||
5194 13
            $number > $count
5195
        ) {
5196 2
            throw new \RangeException(
5197 2
                \sprintf(
5198 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
5199 2
                    $number,
5200 2
                    $count
5201
                )
5202
            );
5203
        }
5204
5205 11
        $result = (array) \array_rand($this->array, $number);
5206
5207 11
        return static::create(
5208 11
            $result,
5209 11
            $this->iteratorClass,
5210 11
            false
5211
        );
5212
    }
5213
5214
    /**
5215
     * Get a random value from the current array.
5216
     *
5217
     * EXAMPLE: <code>
5218
     * a([1, 2, 3, 4])->randomMutable(2); // e.g.: Arrayy[1, 4]
5219
     * </code>
5220
     *
5221
     * @param int|null $number <p>How many values you will take?</p>
5222
     *
5223
     * @return $this
5224
     *               <p>(Mutable) Return this Arrayy object.</p>
5225
     *
5226
     * @phpstan-return static<TKey,T>
5227
     */
5228 17
    public function randomMutable(int $number = null): self
5229
    {
5230 17
        $this->generatorToArray();
5231
5232 17
        if ($this->count() === 0) {
5233
            return static::create(
5234
                [],
5235
                $this->iteratorClass,
5236
                false
5237
            );
5238
        }
5239
5240 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...
5241 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
5242 7
            $this->array = $arrayRandValue;
5243
5244 7
            return $this;
5245
        }
5246
5247 11
        \shuffle($this->array);
5248
5249 11
        return $this->firstsMutable($number);
5250
    }
5251
5252
    /**
5253
     * Pick a random value from the values of this array.
5254
     *
5255
     * EXAMPLE: <code>
5256
     * a([1 => 'one', 2 => 'two'])->randomValue(); // e.g. 'one'
5257
     * </code>
5258
     *
5259
     * @return mixed
5260
     *               <p>Get a random value or null if there wasn't a value.</p>
5261
     *
5262
     * @phpstan-return T|null
5263
     */
5264 4
    public function randomValue()
5265
    {
5266 4
        $result = $this->randomImmutable();
5267
5268 4
        if (!isset($result[0])) {
5269
            $result[0] = null;
5270
        }
5271
5272 4
        return $result[0];
5273
    }
5274
5275
    /**
5276
     * Pick a given number of random values out of this array.
5277
     *
5278
     * EXAMPLE: <code>
5279
     * a([1 => 'one', 2 => 'two'])->randomValues(); // e.g. Arrayy['one', 'two']
5280
     * </code>
5281
     *
5282
     * @param int $number
5283
     *
5284
     * @return static
5285
     *                <p>(Mutable)</p>
5286
     *
5287
     * @phpstan-return static<TKey,T>
5288
     */
5289 7
    public function randomValues(int $number): self
5290
    {
5291 7
        return $this->randomMutable($number);
5292
    }
5293
5294
    /**
5295
     * Get a random value from an array, with the ability to skew the results.
5296
     *
5297
     * EXAMPLE: <code>
5298
     * a([0 => 3, 1 => 4])->randomWeighted([1 => 4]); // e.g.: Arrayy[4] (has a 66% chance of returning 4)
5299
     * </code>
5300
     *
5301
     * @param array    $array
5302
     * @param int|null $number <p>How many values you will take?</p>
5303
     *
5304
     * @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...
5305
     *                           <p>(Immutable)</p>
5306
     *
5307
     * @phpstan-param  array<T,int> $array
5308
     * @phpstan-return static<array-key,T>
5309
     */
5310 9
    public function randomWeighted(array $array, int $number = null): self
5311
    {
5312
        // init
5313 9
        $options = [];
5314
5315 9
        foreach ($array as $option => $weight) {
5316 9
            if ($this->searchIndex($option) !== false) {
5317 2
                for ($i = 0; $i < $weight; ++$i) {
5318 1
                    $options[] = $option;
5319
                }
5320
            }
5321
        }
5322
5323 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
5324
    }
5325
5326
    /**
5327
     * Reduce the current array via callable e.g. anonymous-function and return the end result.
5328
     *
5329
     * EXAMPLE: <code>
5330
     * a([1, 2, 3, 4])->reduce(
5331
     *     function ($carry, $item) {
5332
     *         return $carry * $item;
5333
     *     },
5334
     *     1
5335
     * ); // Arrayy[24]
5336
     * </code>
5337
     *
5338
     * @param callable $callable
5339
     * @param mixed    $initial
5340
     *
5341
     * @return static
5342
     *                <p>(Immutable)</p>
5343
     *
5344
     * @template T2
5345
     *              <p>The output value type.</p>
5346
     *
5347
     * @phpstan-param callable(T2, T, TKey): T2 $callable
5348
     * @phpstan-param T2                  $initial
5349
     *
5350
     * @phpstan-return static<TKey,T2>
5351
     * @psalm-mutation-free
5352
     */
5353 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...
5354
    {
5355 18
        foreach ($this->getGenerator() as $key => $value) {
5356 17
            $initial = $callable($initial, $value, $key);
5357
        }
5358
5359 18
        return static::create(
5360 18
            $initial,
5361 18
            $this->iteratorClass,
5362 18
            false
5363
        );
5364
    }
5365
5366
    /**
5367
     * @param bool $unique
5368
     *
5369
     * @return static
5370
     *                <p>(Immutable)</p>
5371
     *
5372
     * @phpstan-return static<int,mixed>
5373
     * @psalm-mutation-free
5374
     */
5375 14
    public function reduce_dimension(bool $unique = true): self
5376
    {
5377
        // init
5378 14
        $result = [];
5379
5380 14
        foreach ($this->getGenerator() as $val) {
5381 12
            if (\is_array($val)) {
5382 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
5383
            } else {
5384 12
                $result[] = [$val];
5385
            }
5386
        }
5387
5388 14
        $result = $result === [] ? [] : \array_merge(...$result);
5389
5390 14
        $resultArrayy = new static($result);
5391
5392
        /**
5393
         * @psalm-suppress ImpureMethodCall - object is already re-created
5394
         * @psalm-suppress InvalidReturnStatement - why?
5395
         */
5396 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
5397
    }
5398
5399
    /**
5400
     * Create a numerically re-indexed Arrayy object.
5401
     *
5402
     * EXAMPLE: <code>
5403
     * a([2 => 1, 3 => 2])->reindex(); // Arrayy[0 => 1, 1 => 2]
5404
     * </code>
5405
     *
5406
     * @return $this
5407
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
5408
     *
5409
     * @phpstan-return static<TKey,T>
5410
     */
5411 9
    public function reindex(): self
5412
    {
5413 9
        $this->generatorToArray(false);
5414
5415 9
        $this->array = \array_values($this->array);
5416
5417 9
        return $this;
5418
    }
5419
5420
    /**
5421
     * Return all items that fail the truth test.
5422
     *
5423
     * EXAMPLE: <code>
5424
     * $closure = function ($value) {
5425
     *     return $value % 2 !== 0;
5426
     * }
5427
     * a([1, 2, 3, 4])->reject($closure); // Arrayy[1 => 2, 3 => 4]
5428
     * </code>
5429
     *
5430
     * @param \Closure $closure
5431
     *
5432
     * @return static
5433
     *                <p>(Immutable)</p>
5434
     *
5435
     * @phpstan-param \Closure(T=,TKey=):bool  $closure
5436
     * @phpstan-return static<TKey,T>
5437
     * @psalm-mutation-free
5438
     */
5439 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...
5440
    {
5441
        // init
5442 1
        $filtered = [];
5443
5444 1
        foreach ($this->getGenerator() as $key => $value) {
5445 1
            if (!$closure($value, $key)) {
5446 1
                $filtered[$key] = $value;
5447
            }
5448
        }
5449
5450 1
        return static::create(
5451 1
            $filtered,
5452 1
            $this->iteratorClass,
5453 1
            false
5454
        );
5455
    }
5456
5457
    /**
5458
     * Remove a value from the current array (optional using dot-notation).
5459
     *
5460
     * EXAMPLE: <code>
5461
     * a([1 => 'bar', 'foo' => 'foo'])->remove(1); // Arrayy['foo' => 'foo']
5462
     * </code>
5463
     *
5464
     * @param mixed $key
5465
     *
5466
     * @return static
5467
     *                <p>(Mutable)</p>
5468
     *
5469
     * @phpstan-param  TKey $key
5470
     * @phpstan-return static<TKey,T>
5471
     */
5472 22
    public function remove($key)
5473
    {
5474
        // recursive call
5475 22
        if (\is_array($key)) {
5476 1
            foreach ($key as $k) {
5477 1
                $this->internalRemove($k);
5478
            }
5479
5480 1
            return static::create(
5481 1
                $this->toArray(),
5482 1
                $this->iteratorClass,
5483 1
                false
5484
            );
5485
        }
5486
5487 21
        $this->internalRemove($key);
5488
5489 21
        return static::create(
5490 21
            $this->toArray(),
5491 21
            $this->iteratorClass,
5492 21
            false
5493
        );
5494
    }
5495
5496
    /**
5497
     * alias: for "Arrayy->removeValue()"
5498
     *
5499
     * @param mixed $element
5500
     *
5501
     * @return static
5502
     *                <p>(Immutable)</p>
5503
     *
5504
     * @phpstan-param  T $element
5505
     * @phpstan-return static<TKey,T>
5506
     * @psalm-mutation-free
5507
     */
5508 8
    public function removeElement($element)
5509
    {
5510 8
        return $this->removeValue($element);
5511
    }
5512
5513
    /**
5514
     * Remove the first value from the current array.
5515
     *
5516
     * EXAMPLE: <code>
5517
     * a([1 => 'bar', 'foo' => 'foo'])->removeFirst(); // Arrayy['foo' => 'foo']
5518
     * </code>
5519
     *
5520
     * @return static
5521
     *                <p>(Immutable)</p>
5522
     *
5523
     * @phpstan-return static<TKey,T>
5524
     * @psalm-mutation-free
5525
     */
5526 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...
5527
    {
5528 7
        $tmpArray = $this->toArray();
5529
5530 7
        \array_shift($tmpArray);
5531
5532 7
        return static::create(
5533 7
            $tmpArray,
5534 7
            $this->iteratorClass,
5535 7
            false
5536
        );
5537
    }
5538
5539
    /**
5540
     * Remove the last value from the current array.
5541
     *
5542
     * EXAMPLE: <code>
5543
     * a([1 => 'bar', 'foo' => 'foo'])->removeLast(); // Arrayy[1 => 'bar']
5544
     * </code>
5545
     *
5546
     * @return static
5547
     *                <p>(Immutable)</p>
5548
     *
5549
     * @phpstan-return static<TKey,T>
5550
     * @psalm-mutation-free
5551
     */
5552 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...
5553
    {
5554 7
        $tmpArray = $this->toArray();
5555
5556 7
        \array_pop($tmpArray);
5557
5558 7
        return static::create(
5559 7
            $tmpArray,
5560 7
            $this->iteratorClass,
5561 7
            false
5562
        );
5563
    }
5564
5565
    /**
5566
     * Removes a particular value from an array (numeric or associative).
5567
     *
5568
     * EXAMPLE: <code>
5569
     * a([1 => 'bar', 'foo' => 'foo'])->removeValue('foo'); // Arrayy[1 => 'bar']
5570
     * </code>
5571
     *
5572
     * @param mixed $value
5573
     *
5574
     * @return static
5575
     *                <p>(Immutable)</p>
5576
     *
5577
     * @phpstan-param  T $value
5578
     * @phpstan-return static<TKey,T>
5579
     * @psalm-mutation-free
5580
     */
5581 8
    public function removeValue($value): self
5582
    {
5583 8
        $this->generatorToArray();
5584
5585
        // init
5586 8
        $isSequentialArray = $this->isSequential();
5587
5588 8
        foreach ($this->array as $key => $item) {
5589 7
            if ($item === $value) {
5590
                /** @phpstan-ignore-next-line | "Possibly invalid array key type int|string|TKey.", is this a bug in phpstan? */
5591 7
                unset($this->array[$key]);
5592
            }
5593
        }
5594
5595 8
        if ($isSequentialArray) {
5596 6
            $this->array = \array_values($this->array);
5597
        }
5598
5599 8
        return static::create(
5600 8
            $this->array,
5601 8
            $this->iteratorClass,
5602 8
            false
5603
        );
5604
    }
5605
5606
    /**
5607
     * Generate array of repeated arrays.
5608
     *
5609
     * @param int $times <p>How many times has to be repeated.</p>
5610
     *
5611
     * @return static
5612
     *                <p>(Immutable)</p>
5613
     *
5614
     * @phpstan-return static<TKey,T>
5615
     * @psalm-mutation-free
5616
     */
5617 1
    public function repeat($times): self
5618
    {
5619 1
        if ($times === 0) {
5620 1
            return static::create([], $this->iteratorClass);
5621
        }
5622
5623 1
        return static::create(
5624 1
            \array_fill(0, (int) $times, $this->toArray()),
5625 1
            $this->iteratorClass,
5626 1
            false
5627
        );
5628
    }
5629
5630
    /**
5631
     * Replace a key with a new key/value pair.
5632
     *
5633
     * EXAMPLE: <code>
5634
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
5635
     * $arrayy->replace(2, 'notfoo', 'notbar'); // Arrayy[1 => 'foo', 'notfoo' => 'notbar', 3 => 'bar']
5636
     * </code>
5637
     *
5638
     * @param mixed $oldKey
5639
     * @param mixed $newKey
5640
     * @param mixed $newValue
5641
     *
5642
     * @return static
5643
     *                <p>(Immutable)</p>
5644
     *
5645
     * @phpstan-return static<TKey,T>
5646
     * @psalm-mutation-free
5647
     */
5648 5
    public function replace($oldKey, $newKey, $newValue): self
5649
    {
5650 5
        $that = clone $this;
5651
5652
        /**
5653
         * @psalm-suppress ImpureMethodCall - object is already cloned
5654
         */
5655 5
        return $that->remove($oldKey)
5656 5
            ->set($newKey, $newValue);
5657
    }
5658
5659
    /**
5660
     * Create an array using the current array as values and the other array as keys.
5661
     *
5662
     * EXAMPLE: <code>
5663
     * $firstArray = [
5664
     *     1 => 'one',
5665
     *     2 => 'two',
5666
     *     3 => 'three',
5667
     * ];
5668
     * $secondArray = [
5669
     *     'one' => 1,
5670
     *     1     => 'one',
5671
     *     2     => 2,
5672
     * ];
5673
     * $arrayy = a($firstArray);
5674
     * $arrayy->replaceAllKeys($secondArray); // Arrayy[1 => "one", 'one' => "two", 2 => "three"]
5675
     * </code>
5676
     *
5677
     * @param int[]|string[] $keys <p>An array of keys.</p>
5678
     *
5679
     * @return static
5680
     *                <p>(Immutable) Arrayy object with keys from the other array, empty Arrayy object if the number of elements
5681
     *                for each array isn't equal or if the arrays are empty.
5682
     *                </p>
5683
     *
5684
     * @phpstan-param  array<array-key,TKey> $keys
5685
     * @phpstan-return static<TKey,T>
5686
     * @psalm-mutation-free
5687
     */
5688 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...
5689
    {
5690 2
        $data = \array_combine($keys, $this->toArray());
5691 2
        if ($data === false) {
5692
            $data = [];
5693
        }
5694
5695 2
        return static::create(
5696 2
            $data,
5697 2
            $this->iteratorClass,
5698 2
            false
5699
        );
5700
    }
5701
5702
    /**
5703
     * Create an array using the current array as keys and the other array as values.
5704
     *
5705
     * EXAMPLE: <code>
5706
     * $firstArray = [
5707
     *     1 => 'one',
5708
     *     2 => 'two',
5709
     *     3 => 'three',
5710
     * ];
5711
     * $secondArray = [
5712
     *     'one' => 1,
5713
     *     1     => 'one',
5714
     *     2     => 2,
5715
     * ];
5716
     * $arrayy = a($firstArray);
5717
     * $arrayy->replaceAllValues($secondArray); // Arrayy['one' => 1, 'two' => 'one', 'three' => 2]
5718
     * </code>
5719
     *
5720
     * @param array $array <p>An array of values.</p>
5721
     *
5722
     * @return static
5723
     *                <p>(Immutable) Arrayy object with values from the other array, empty Arrayy object if the number of elements
5724
     *                for each array isn't equal or if the arrays are empty.
5725
     *                </p>
5726
     *
5727
     * @phpstan-param  array<array-key,T> $array
5728
     * @phpstan-return static<TKey,T>
5729
     * @psalm-mutation-free
5730
     */
5731 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...
5732
    {
5733 2
        $data = \array_combine($this->toArray(), $array);
5734 2
        if ($data === false) {
5735
            $data = [];
5736
        }
5737
5738 2
        return static::create(
5739 2
            $data,
5740 2
            $this->iteratorClass,
5741 2
            false
5742
        );
5743
    }
5744
5745
    /**
5746
     * Replace the keys in an array with another set.
5747
     *
5748
     * EXAMPLE: <code>
5749
     * a([1 => 'bar', 'foo' => 'foo'])->replaceKeys([1 => 2, 'foo' => 'replaced']); // Arrayy[2 => 'bar', 'replaced' => 'foo']
5750
     * </code>
5751
     *
5752
     * @param array $keys <p>An array of keys matching the array's size.</p>
5753
     *
5754
     * @return static
5755
     *                <p>(Immutable)</p>
5756
     *
5757
     * @phpstan-param  array<array-key,TKey> $keys
5758
     * @phpstan-return static<TKey,T>
5759
     * @psalm-mutation-free
5760
     */
5761 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...
5762
    {
5763 1
        $values = \array_values($this->toArray());
5764 1
        $result = \array_combine($keys, $values);
5765 1
        if ($result === false) {
5766
            $result = [];
5767
        }
5768
5769 1
        return static::create(
5770 1
            $result,
5771 1
            $this->iteratorClass,
5772 1
            false
5773
        );
5774
    }
5775
5776
    /**
5777
     * Replace the first matched value in an array.
5778
     *
5779
     * EXAMPLE: <code>
5780
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5781
     * a($testArray)->replaceOneValue('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'foobar']
5782
     * </code>
5783
     *
5784
     * @param mixed $search      <p>The value to replace.</p>
5785
     * @param mixed $replacement <p>The value to replace.</p>
5786
     *
5787
     * @return static
5788
     *                <p>(Immutable)</p>
5789
     *
5790
     * @phpstan-return static<TKey,T>
5791
     * @psalm-mutation-free
5792
     */
5793 3
    public function replaceOneValue($search, $replacement = ''): self
5794
    {
5795 3
        $array = $this->toArray();
5796 3
        $key = \array_search($search, $array, true);
5797
5798 3
        if ($key !== false) {
5799 3
            $array[$key] = $replacement;
5800
        }
5801
5802 3
        return static::create(
5803 3
            $array,
5804 3
            $this->iteratorClass,
5805 3
            false
5806
        );
5807
    }
5808
5809
    /**
5810
     * Replace values in the current array.
5811
     *
5812
     * EXAMPLE: <code>
5813
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5814
     * a($testArray)->replaceValues('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'replacedbar']
5815
     * </code>
5816
     *
5817
     * @param string $search      <p>The value to replace.</p>
5818
     * @param string $replacement <p>What to replace it with.</p>
5819
     *
5820
     * @return static
5821
     *                <p>(Immutable)</p>
5822
     *
5823
     * @phpstan-return static<TKey,T>
5824
     * @psalm-mutation-free
5825
     */
5826 1
    public function replaceValues($search, $replacement = ''): self
5827
    {
5828 1
        $function = static function ($value) use ($search, $replacement) {
5829 1
            return \str_replace($search, $replacement, $value);
5830 1
        };
5831
5832
        /** @phpstan-ignore-next-line | ignore Closure with one or two parameters, is this a bug in phpstan? */
5833 1
        return $this->each($function);
5834
    }
5835
5836
    /**
5837
     * Get the last elements from index $from until the end of this array.
5838
     *
5839
     * EXAMPLE: <code>
5840
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->rest(2); // Arrayy[0 => 'lall']
5841
     * </code>
5842
     *
5843
     * @param int $from
5844
     *
5845
     * @return static
5846
     *                <p>(Immutable)</p>
5847
     *
5848
     * @phpstan-return static<TKey,T>
5849
     * @psalm-mutation-free
5850
     */
5851 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...
5852
    {
5853 15
        $tmpArray = $this->toArray();
5854
5855 15
        return static::create(
5856 15
            \array_splice($tmpArray, $from),
5857 15
            $this->iteratorClass,
5858 15
            false
5859
        );
5860
    }
5861
5862
    /**
5863
     * Return the array in the reverse order.
5864
     *
5865
     * EXAMPLE: <code>
5866
     * a([1, 2, 3])->reverse(); // self[3, 2, 1]
5867
     * </code>
5868
     *
5869
     * @return $this
5870
     *               <p>(Mutable) Return this Arrayy object.</p>
5871
     *
5872
     * @phpstan-return static<TKey,T>
5873
     */
5874 9
    public function reverse(): self
5875
    {
5876 9
        $this->generatorToArray();
5877
5878 9
        $this->array = \array_reverse($this->array);
5879
5880 9
        return $this;
5881
    }
5882
5883
    /**
5884
     * Sort an array in reverse order.
5885
     *
5886
     * @param int $sort_flags [optional] <p>
5887
     *                        You may modify the behavior of the sort using the optional
5888
     *                        parameter sort_flags, for details
5889
     *                        see sort.
5890
     *                        </p>
5891
     *
5892
     * @return $this
5893
     *               <p>(Mutable) Return this Arrayy object.</p>
5894
     *
5895
     * @phpstan-return static<TKey,T>
5896
     */
5897 4
    public function rsort(int $sort_flags = 0): self
5898
    {
5899 4
        $this->generatorToArray();
5900
5901 4
        \rsort($this->array, $sort_flags);
5902
5903 4
        return $this;
5904
    }
5905
5906
    /**
5907
     * Sort an array in reverse order.
5908
     *
5909
     * @param int $sort_flags [optional] <p>
5910
     *                        You may modify the behavior of the sort using the optional
5911
     *                        parameter sort_flags, for details
5912
     *                        see sort.
5913
     *                        </p>
5914
     *
5915
     * @return $this
5916
     *               <p>(Immutable) Return this Arrayy object.</p>
5917
     *
5918
     * @phpstan-return static<TKey,T>
5919
     * @psalm-mutation-free
5920
     */
5921 4
    public function rsortImmutable(int $sort_flags = 0): self
5922
    {
5923 4
        $that = clone $this;
5924
5925
        /**
5926
         * @psalm-suppress ImpureMethodCall - object is already cloned
5927
         */
5928 4
        $that->rsort($sort_flags);
5929
5930 4
        return $that;
5931
    }
5932
5933
    /**
5934
     * Search for the first index of the current array via $value.
5935
     *
5936
     * EXAMPLE: <code>
5937
     * a(['fòô' => 'bàř', 'lall' => 'bàř'])->searchIndex('bàř'); // Arrayy[0 => 'fòô']
5938
     * </code>
5939
     *
5940
     * @param mixed $value
5941
     *
5942
     * @return false|int|string
5943
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5944
     *
5945
     * @phpstan-param T $value
5946
     * @phpstan-return false|TKey
5947
     *
5948
     * @psalm-mutation-free
5949
     */
5950 21
    public function searchIndex($value)
5951
    {
5952 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5953 20
            if ($value === $valueFromArray) {
5954 10
                return $keyFromArray;
5955
            }
5956
        }
5957
5958 11
        return false;
5959
    }
5960
5961
    /**
5962
     * Search for the value of the current array via $index.
5963
     *
5964
     * EXAMPLE: <code>
5965
     * a(['fòô' => 'bàř'])->searchValue('fòô'); // Arrayy[0 => 'bàř']
5966
     * </code>
5967
     *
5968
     * @param mixed $index
5969
     *
5970
     * @return static
5971
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5972
     *
5973
     * @phpstan-return static<TKey,T>
5974
     * @psalm-mutation-free
5975
     */
5976 9
    public function searchValue($index): self
5977
    {
5978 9
        $this->generatorToArray();
5979
5980
        // init
5981 9
        $return = [];
5982
5983 9
        if ($this->array === []) {
5984
            return static::create(
5985
                [],
5986
                $this->iteratorClass,
5987
                false
5988
            );
5989
        }
5990
5991
        // php cast "bool"-index into "int"-index
5992 9
        if ((bool) $index === $index) {
5993 1
            $index = (int) $index;
5994
        }
5995
5996 9
        if ($this->offsetExists($index)) {
5997 7
            $return = [$this->array[$index]];
5998
        }
5999
6000 9
        return static::create(
6001 9
            $return,
6002 9
            $this->iteratorClass,
6003 9
            false
6004
        );
6005
    }
6006
6007
    /**
6008
     * Set a value for the current array (optional using dot-notation).
6009
     *
6010
     * EXAMPLE: <code>
6011
     * $arrayy = a(['Lars' => ['lastname' => 'Moelleken']]);
6012
     * $arrayy->set('Lars.lastname', 'Müller'); // Arrayy['Lars', ['lastname' => 'Müller']]]
6013
     * </code>
6014
     *
6015
     * @param string $key   <p>The key to set.</p>
6016
     * @param mixed  $value <p>Its value.</p>
6017
     *
6018
     * @return $this
6019
     *               <p>(Mutable) Return this Arrayy object.</p>
6020
     *
6021
     * @phpstan-param  TKey $key
6022
     * @phpstan-param  T $value
6023
     * @phpstan-return static<TKey,T>
6024
     */
6025 28
    public function set($key, $value): self
6026
    {
6027 28
        $this->internalSet($key, $value);
6028
6029 27
        return $this;
6030
    }
6031
6032
    /**
6033
     * Get a value from a array and set it if it was not.
6034
     *
6035
     * WARNING: this method only set the value, if the $key is not already set
6036
     *
6037
     * EXAMPLE: <code>
6038
     * $arrayy = a([1 => 1, 2 => 2, 3 => 3]);
6039
     * $arrayy->setAndGet(1, 4); // 1
6040
     * $arrayy->setAndGet(0, 4); // 4
6041
     * </code>
6042
     *
6043
     * @param mixed $key      <p>The key</p>
6044
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
6045
     *
6046
     * @return mixed
6047
     *               <p>(Mutable)</p>
6048
     */
6049 11
    public function setAndGet($key, $fallback = null)
6050
    {
6051 11
        $this->generatorToArray();
6052
6053
        // If the key doesn't exist, set it.
6054 11
        if (!$this->has($key)) {
6055 4
            $this->array = $this->set($key, $fallback)->toArray();
6056
        }
6057
6058 11
        return $this->get($key);
6059
    }
6060
6061
    /**
6062
     * Shifts a specified value off the beginning of array.
6063
     *
6064
     * @return mixed
6065
     *               <p>(Mutable) A shifted element from the current array.</p>
6066
     */
6067 5
    public function shift()
6068
    {
6069 5
        $this->generatorToArray();
6070
6071 5
        return \array_shift($this->array);
6072
    }
6073
6074
    /**
6075
     * Shuffle the current array.
6076
     *
6077
     * EXAMPLE: <code>
6078
     * a([1 => 'bar', 'foo' => 'foo'])->shuffle(); // e.g.: Arrayy[['foo' => 'foo', 1 => 'bar']]
6079
     * </code>
6080
     *
6081
     * @param bool       $secure <p>using a CSPRNG | @see https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
6082
     * @param array|null $array  [optional]
6083
     *
6084
     * @return static
6085
     *                <p>(Immutable)</p>
6086
     *
6087
     * @phpstan-param  array<TKey,T> $array
6088
     * @phpstan-return static<TKey,T>
6089
     *
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 1
                    $temp = $array[$keys[$r]];
6110 1
                    $array[$keys[$r]] = $array[$keys[$i]];
6111 1
                    $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 1
        return $this->toArray(
6769 1
            $convertAllArrayyElements,
6770 1
            false
6771
        );
6772
    }
6773
6774
    /**
6775
     * Convert the current array to JSON.
6776
     *
6777
     * EXAMPLE: <code>
6778
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6779
     * </code>
6780
     *
6781
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6782
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6783
     *
6784
     * @return string
6785
     */
6786 13
    public function toJson(int $options = 0, int $depth = 512): string
6787
    {
6788 13
        $return = \json_encode($this->toArray(), $options, $depth);
6789 13
        if ($return === false) {
6790
            return '';
6791
        }
6792
6793 13
        return $return;
6794
    }
6795
6796
    /**
6797
     * @param string[]|null $items  [optional]
6798
     * @param string[]      $helper [optional]
6799
     *
6800
     * @return static|static[]
6801
     *
6802
     * @phpstan-return static<int, static<TKey,T>>
6803
     */
6804 1
    public function toPermutation(array $items = null, array $helper = []): self
6805
    {
6806
        // init
6807 1
        $return = [];
6808
6809 1
        if ($items === null) {
6810 1
            $items = $this->toArray();
6811
        }
6812
6813 1
        if (empty($items)) {
6814 1
            $return[] = $helper;
6815
        } else {
6816 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6817 1
                $new_items = $items;
6818 1
                $new_helper = $helper;
6819 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6820
                /** @noinspection PhpSillyAssignmentInspection */
6821
                /** @var string[] $new_items */
6822 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...
6823 1
                \array_unshift($new_helper, $tmp_helper);
6824 1
                $return = \array_merge(
6825 1
                    $return,
6826 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6827
                );
6828
            }
6829
        }
6830
6831 1
        return static::create(
6832 1
            $return,
6833 1
            $this->iteratorClass,
6834 1
            false
6835
        );
6836
    }
6837
6838
    /**
6839
     * Implodes array to a string with specified separator.
6840
     *
6841
     * @param string $separator [optional] <p>The element's separator.</p>
6842
     *
6843
     * @return string
6844
     *                <p>The string representation of array, separated by ",".</p>
6845
     */
6846 19
    public function toString(string $separator = ','): string
6847
    {
6848 19
        return $this->implode($separator);
6849
    }
6850
6851
    /**
6852
     * Return a duplicate free copy of the current array.
6853
     *
6854
     * EXAMPLE: <code>
6855
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6856
     * </code>
6857
     *
6858
     * @return $this
6859
     *               <p>(Mutable)</p>
6860
     *
6861
     * @phpstan-return static<int,T>
6862
     */
6863 13
    public function uniqueNewIndex(): self
6864
    {
6865
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6866
6867 13
        $this->array = $this->reduce(
6868 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...
6869 12
                if (!\in_array($value, $resultArray, true)) {
6870 12
                    $resultArray[] = $value;
6871
                }
6872
6873 12
                return $resultArray;
6874 13
            },
6875 13
            []
6876 13
        )->toArray();
6877 13
        $this->generator = null;
6878
6879 13
        return $this;
6880
    }
6881
6882
    /**
6883
     * Return a duplicate free copy of the current array. (with the old keys)
6884
     *
6885
     * EXAMPLE: <code>
6886
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6887
     * </code>
6888
     *
6889
     * @return $this
6890
     *               <p>(Mutable)</p>
6891
     *
6892
     * @phpstan-return static<TKey,T>
6893
     */
6894 11
    public function uniqueKeepIndex(): self
6895
    {
6896
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6897
6898
        // init
6899 11
        $array = $this->toArray();
6900
6901
        /**
6902
         * @psalm-suppress MissingClosureReturnType
6903
         * @psalm-suppress MissingClosureParamType
6904
         */
6905 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...
6906 11
            \array_keys($array),
6907 11
            static function ($resultArray, $key) use ($array) {
6908 10
                if (!\in_array($array[$key], $resultArray, true)) {
6909 10
                    $resultArray[$key] = $array[$key];
6910
                }
6911
6912 10
                return $resultArray;
6913 11
            },
6914 11
            []
6915
        );
6916 11
        $this->generator = null;
6917
6918 11
        return $this;
6919
    }
6920
6921
    /**
6922
     * alias: for "Arrayy->uniqueNewIndex()"
6923
     *
6924
     * @return static
6925
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6926
     *
6927
     * @see          Arrayy::unique()
6928
     *
6929
     * @phpstan-return static<int,T>
6930
     */
6931 13
    public function unique(): self
6932
    {
6933 13
        return $this->uniqueNewIndex();
6934
    }
6935
6936
    /**
6937
     * Prepends one or more values to the beginning of array at once.
6938
     *
6939
     * @param mixed ...$args
6940
     *
6941
     * @return $this
6942
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6943
     *
6944
     * @phpstan-param  array<TKey,T> ...$args
6945
     * @phpstan-return static<TKey,T>
6946
     */
6947 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...
6948
    {
6949 6
        $this->generatorToArray();
6950
6951
        if (
6952 6
            $this->checkPropertyTypes
6953
            &&
6954 6
            $this->properties !== []
6955
        ) {
6956 2
            foreach ($args as $key => $value) {
6957 2
                $this->checkType($key, $value);
6958
            }
6959
        }
6960
6961 5
        \array_unshift($this->array, ...$args);
6962
6963 5
        return $this;
6964
    }
6965
6966
    /**
6967
     * Tests whether the given closure return something valid for all elements of this array.
6968
     *
6969
     * @param \Closure $closure the predicate
6970
     *
6971
     * @return bool
6972
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6973
     *
6974
     * @phpstan-param \Closure(T=,TKey=):bool $closure
6975
     */
6976 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...
6977
    {
6978 1
        foreach ($this->getGenerator() as $key => $value) {
6979 1
            if (!$closure($value, $key)) {
6980 1
                return false;
6981
            }
6982
        }
6983
6984 1
        return true;
6985
    }
6986
6987
    /**
6988
     * Get all values from a array.
6989
     *
6990
     * EXAMPLE: <code>
6991
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
6992
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
6993
     * </code>
6994
     *
6995
     * @return static
6996
     *                <p>(Immutable)</p>
6997
     *
6998
     * @phpstan-return static<TKey,T>
6999
     * @psalm-mutation-free
7000
     */
7001 2
    public function values(): self
7002
    {
7003 2
        return static::create(
7004 2
            function () {
7005 2
                foreach ($this->getGenerator() as $value) {
7006 2
                    yield $value;
7007
                }
7008 2
            },
7009 2
            $this->iteratorClass,
7010 2
            false
7011
        );
7012
    }
7013
7014
    /**
7015
     * Apply the given function to every element in the array, discarding the results.
7016
     *
7017
     * EXAMPLE: <code>
7018
     * $callable = function (&$value, $key) {
7019
     *     $value = $key;
7020
     * };
7021
     * $arrayy = a([1, 2, 3]);
7022
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
7023
     * </code>
7024
     *
7025
     * @param callable $callable
7026
     * @param bool $recursive
7027
     *                            [optional] <p>Whether array will be walked recursively or no</p>
7028
     * @param mixed $userData
7029
     *                            [optional] <p>
7030
     *                            If the optional $userData parameter is supplied,
7031
     *                            it will be passed as the third parameter to the $callable.
7032
     *                            </p>
7033
     *
7034
     * @return $this
7035
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
7036
     *
7037
     * @phpstan-return static<TKey,T>
7038
     */
7039 12
    public function walk(
7040
        $callable,
7041
        bool $recursive = false,
7042
        $userData = self::ARRAYY_HELPER_WALK
7043
    ): self {
7044 12
        $this->generatorToArray();
7045
7046 12
        if ($this->array !== []) {
7047 10
            if ($recursive === true) {
7048 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
7049
                    \array_walk_recursive($this->array, $callable, $userData);
7050
                } else {
7051 5
                    \array_walk_recursive($this->array, $callable);
7052
                }
7053
            } else {
7054
                /** @noinspection NestedPositiveIfStatementsInspection */
7055 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
7056
                    \array_walk($this->array, $callable, $userData);
7057
                } else {
7058 5
                    \array_walk($this->array, $callable);
7059
                }
7060
            }
7061
        }
7062
7063 12
        return $this;
7064
    }
7065
7066
    /**
7067
     * Returns a collection of matching items.
7068
     *
7069
     * @param string $keyOrPropertyOrMethod
7070
     *                                      <p>The property or method to evaluate.</p>
7071
     * @param mixed $value
7072
     *                                      <p>The value to match.</p>
7073
     *
7074
     * @return static
7075
     *
7076
     * @phpstan-return static<TKey,T>
7077
     *
7078
     * @throws \InvalidArgumentException if property or method is not defined
7079
     */
7080 1
    public function where(string $keyOrPropertyOrMethod, $value): self
7081
    {
7082 1
        return $this->filter(
7083 1
            function ($item) use ($keyOrPropertyOrMethod, $value) {
7084
                $accessorValue = $this->extractValue(
7085
                    $item,
7086
                    $keyOrPropertyOrMethod
7087
                );
7088
7089
                return $accessorValue === $value;
7090 1
            }
7091
        );
7092
    }
7093
7094
    /**
7095
     * Convert an array into a object.
7096
     *
7097
     * @param array $array
7098
     *
7099
     * @return \stdClass
7100
     *
7101
     * @phpstan-param array<int|string,mixed> $array
7102
     */
7103 4
    final protected static function arrayToObject(array $array = []): \stdClass
7104
    {
7105
        // init
7106 4
        $object = new \stdClass();
7107
7108 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
7109 1
            return $object;
7110
        }
7111
7112 3
        foreach ($array as $name => $value) {
7113 3
            if (\is_array($value)) {
7114 1
                $object->{$name} = static::arrayToObject($value);
7115
            } else {
7116 3
                $object->{$name} = $value;
7117
            }
7118
        }
7119
7120 3
        return $object;
7121
    }
7122
7123
    /**
7124
     * @param array|\Generator|null $input         <p>
7125
     *                                             An array containing keys to return.
7126
     *                                             </p>
7127
     * @param mixed|null            $search_values [optional] <p>
7128
     *                                             If specified, then only keys containing these values are returned.
7129
     *                                             </p>
7130
     * @param bool                  $strict        [optional] <p>
7131
     *                                             Determines if strict comparison (===) should be used during the
7132
     *                                             search.
7133
     *                                             </p>
7134
     *
7135
     * @return array
7136
     *               <p>An array of all the keys in input.</p>
7137
     *
7138
     * @template TInput
7139
     *
7140
     * @phpstan-param  array<array-key,TInput>|\Generator<array-key,TInput>|null $input
7141
     * @phpstan-return array<array-key>|array<TKey>
7142
     *
7143
     * @psalm-mutation-free
7144
     */
7145 11
    protected function array_keys_recursive(
7146
        $input = null,
7147
        $search_values = null,
7148
        bool $strict = true
7149
    ): array {
7150
        // init
7151 11
        $keys = [];
7152 11
        $keysTmp = [];
7153
7154 11
        if ($input === null) {
7155 4
            $input = $this->getGenerator();
7156
        }
7157
7158 11
        if ($search_values === null) {
7159 11
            foreach ($input as $key => $value) {
7160 11
                $keys[] = $key;
7161
7162
                // check if recursive is needed
7163 11
                if (\is_array($value)) {
7164 4
                    $keysTmp[] = $this->array_keys_recursive($value);
7165
                }
7166
            }
7167
        } else {
7168 1
            $is_array_tmp = \is_array($search_values);
7169
7170 1
            foreach ($input as $key => $value) {
7171 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...
7172
                    (
7173 1
                        $is_array_tmp === false
7174
                        &&
7175 1
                        $strict === true
7176
                        &&
7177 1
                        $search_values === $value
7178
                    )
7179
                    ||
7180
                    (
7181 1
                        $is_array_tmp === false
7182
                        &&
7183 1
                        $strict === false
7184
                        &&
7185 1
                        $search_values == $value
7186
                    )
7187
                    ||
7188
                    (
7189 1
                        $is_array_tmp === true
7190
                        &&
7191 1
                        \in_array($value, $search_values, $strict)
7192
                    )
7193
                ) {
7194 1
                    $keys[] = $key;
7195
                }
7196
7197
                // check if recursive is needed
7198 1
                if (\is_array($value)) {
7199 1
                    $keysTmp[] = $this->array_keys_recursive($value);
7200
                }
7201
            }
7202
        }
7203
7204 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
7205
    }
7206
7207
    /**
7208
     * @param string     $path
7209
     * @param callable   $callable
7210
     * @param array|null $currentOffset
7211
     *
7212
     * @return void
7213
     *
7214
     * @phpstan-param array<TKey,T>|null $currentOffset
7215
     * @psalm-mutation-free
7216
     */
7217 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
7218
    {
7219 10
        $this->generatorToArray();
7220
7221 10
        if ($currentOffset === null) {
7222 10
            $currentOffset = &$this->array;
7223
        }
7224
7225 10
        $explodedPath = \explode($this->pathSeparator, $path);
7226 10
        if ($explodedPath === false) {
7227
            return;
7228
        }
7229
7230 10
        $nextPath = \array_shift($explodedPath);
7231
7232 10
        if (!isset($currentOffset[$nextPath])) {
7233 1
            return;
7234
        }
7235
7236 9
        if (!empty($explodedPath)) {
7237 1
            $this->callAtPath(
7238 1
                \implode($this->pathSeparator, $explodedPath),
7239
                $callable,
7240 1
                $currentOffset[$nextPath]
7241
            );
7242
        } else {
7243 9
            $callable($currentOffset[$nextPath]);
7244
        }
7245 9
    }
7246
7247
    /**
7248
     * Extracts the value of the given property or method from the object.
7249
     *
7250
     * @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...
7251
     *                                      <p>The object to extract the value from.</p>
7252
     * @param string $keyOrPropertyOrMethod
7253
     *                                      <p>The property or method for which the
7254
     *                                      value should be extracted.</p>
7255
     *
7256
     * @return mixed
7257
     *               <p>The value extracted from the specified property or method.</p>
7258
     *
7259
     * @phpstan-param self<TKey,T> $object
7260
     *
7261
     * @throws \InvalidArgumentException if the method or property is not defined
7262
     */
7263 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
7264
    {
7265 1
        if (isset($object[$keyOrPropertyOrMethod])) {
7266 1
            $return = $object->get($keyOrPropertyOrMethod);
7267
7268 1
            if ($return instanceof self) {
7269
                return $return->toArray();
7270
            }
7271
7272 1
            return $return;
7273
        }
7274
7275
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
7276
            return $object->{$keyOrPropertyOrMethod};
7277
        }
7278
7279
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
7280
            return $object->{$keyOrPropertyOrMethod}();
7281
        }
7282
7283
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
7284
    }
7285
7286
    /**
7287
     * create a fallback for array
7288
     *
7289
     * 1. use the current array, if it's a array
7290
     * 2. fallback to empty array, if there is nothing
7291
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
7292
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
7293
     * 5. call "__toArray()" on object, if the method exists
7294
     * 6. cast a string or object with "__toString()" into an array
7295
     * 7. throw a "InvalidArgumentException"-Exception
7296
     *
7297
     * @param mixed $data
7298
     *
7299
     * @throws \InvalidArgumentException
7300
     *
7301
     * @return array
7302
     *
7303
     * @phpstan-return array<mixed>|array<TKey,T>
7304
     */
7305 1214
    protected function fallbackForArray(&$data): array
7306
    {
7307 1214
        $data = $this->internalGetArray($data);
7308
7309 1214
        if ($data === null) {
7310 2
            throw new \InvalidArgumentException('Passed value should be a array');
7311
        }
7312
7313 1212
        return $data;
7314
    }
7315
7316
    /**
7317
     * @param bool $preserveKeys <p>
7318
     *                           e.g.: A generator maybe return the same key more then once,
7319
     *                           so maybe you will ignore the keys.
7320
     *                           </p>
7321
     *
7322
     * @return bool
7323
     *
7324
     * @noinspection ReturnTypeCanBeDeclaredInspection
7325
     * @psalm-mutation-free :/
7326
     */
7327 1123
    protected function generatorToArray(bool $preserveKeys = true)
7328
    {
7329 1123
        if ($this->generator) {
7330 2
            $this->array = $this->toArray(false, $preserveKeys);
7331 2
            $this->generator = null;
7332
7333 2
            return true;
7334
        }
7335
7336 1123
        return false;
7337
    }
7338
7339
    /**
7340
     * Get correct PHP constant for direction.
7341
     *
7342
     * @param int|string $direction
7343
     *
7344
     * @return int
7345
     * @psalm-mutation-free
7346
     */
7347 43
    protected function getDirection($direction): int
7348
    {
7349 43
        if ((string) $direction === $direction) {
7350 10
            $direction = \strtolower($direction);
7351
7352 10
            if ($direction === 'desc') {
7353 2
                $direction = \SORT_DESC;
7354
            } else {
7355 9
                $direction = \SORT_ASC;
7356
            }
7357
        }
7358
7359
        if (
7360 43
            $direction !== \SORT_DESC
7361
            &&
7362 43
            $direction !== \SORT_ASC
7363
        ) {
7364
            $direction = \SORT_ASC;
7365
        }
7366
7367 43
        return $direction;
7368
    }
7369
7370
    /**
7371
     * @return TypeCheckInterface[]
7372
     *
7373
     * @noinspection ReturnTypeCanBeDeclaredInspection
7374
     */
7375 24
    protected function getPropertiesFromPhpDoc()
7376
    {
7377 24
        static $PROPERTY_CACHE = [];
7378 24
        $cacheKey = 'Class::' . static::class;
7379
7380 24
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7381 22
            return $PROPERTY_CACHE[$cacheKey];
7382
        }
7383
7384
        // init
7385 4
        $properties = [];
7386
7387 4
        $reflector = new \ReflectionClass($this);
7388 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7389 4
        $docComment = $reflector->getDocComment();
7390 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...
7391 4
            $docblock = $factory->create($docComment);
7392
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7393 4
            foreach ($docblock->getTagsByName('property') as $tag) {
7394 3
                $typeName = $tag->getVariableName();
7395
                /** @var string|null $typeName */
7396 3
                if ($typeName !== null) {
7397 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7398 3
                    if ($typeCheckPhpDoc !== null) {
7399 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7400
                    }
7401
                }
7402
            }
7403
        }
7404
7405
        /** @noinspection PhpAssignmentInConditionInspection */
7406 4
        while ($reflector = $reflector->getParentClass()) {
7407 4
            $docComment = $reflector->getDocComment();
7408 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...
7409 4
                $docblock = $factory->create($docComment);
7410
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7411 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7412 1
                    $typeName = $tag->getVariableName();
7413
                    /** @var string|null $typeName */
7414 1
                    if ($typeName !== null) {
7415 1
                        if (isset($properties[$typeName])) {
7416 1
                            continue;
7417
                        }
7418
7419 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7420 1
                        if ($typeCheckPhpDoc !== null) {
7421 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7422
                        }
7423
                    }
7424
                }
7425
            }
7426
        }
7427
7428 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7429
    }
7430
7431
    /**
7432
     * @param string $glue
7433
     * @param mixed $pieces
7434
     * @param bool  $useKeys
7435
     *
7436
     * @return string
7437
     *
7438
     * @phpstan-param scalar|object|self<TKey|T>|array<TKey,T> $pieces
7439
     * @psalm-mutation-free
7440
     */
7441 36
    protected function implode_recursive(
7442
        $glue = '',
7443
        $pieces = [],
7444
        bool $useKeys = false
7445
    ): string {
7446 36
        if ($pieces instanceof self) {
7447 1
            $pieces = $pieces->toArray();
7448
        }
7449
7450 36
        if (\is_array($pieces)) {
7451
            /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */
7452
            /** @phpstan-var array<TKey,T> $pieces */
7453 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...
7454
7455 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7456 36
            $pieces_count_not_zero = $pieces_count > 0;
7457
7458 36
            return \implode(
7459 36
                $glue,
7460 36
                \array_map(
7461 36
                    [$this, 'implode_recursive'],
7462 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7463 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7464
                )
7465
            );
7466
        }
7467
7468
        if (
7469 36
            \is_scalar($pieces) === true
7470
            ||
7471 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7472
        ) {
7473 32
            return (string) $pieces;
7474
        }
7475
7476 8
        return '';
7477
    }
7478
7479
    /**
7480
     * @param mixed                 $needle   <p>
7481
     *                                        The searched value.
7482
     *                                        </p>
7483
     *                                        <p>
7484
     *                                        If needle is a string, the comparison is done
7485
     *                                        in a case-sensitive manner.
7486
     *                                        </p>
7487
     * @param array|\Generator|null $haystack <p>
7488
     *                                        The array.
7489
     *                                        </p>
7490
     * @param bool                  $strict   [optional] <p>
7491
     *                                        If the third parameter strict is set to true
7492
     *                                        then the in_array function will also check the
7493
     *                                        types of the
7494
     *                                        needle in the haystack.
7495
     *                                        </p>
7496
     *
7497
     * @return bool
7498
     *              <p>true if needle is found in the array, false otherwise</p>
7499
     *
7500
     * @phpstan-param (array&T)|array<TKey,T>|\Generator<TKey,T>|null $haystack
7501
     *
7502
     * @psalm-mutation-free
7503
     */
7504 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7505
    {
7506 18
        if ($haystack === null) {
7507
            $haystack = $this->getGenerator();
7508
        }
7509
7510 18
        foreach ($haystack as $item) {
7511 14
            if (\is_array($item)) {
7512 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7513
            } else {
7514
                /** @noinspection NestedPositiveIfStatementsInspection */
7515 14
                if ($strict === true) {
7516 14
                    $returnTmp = $item === $needle;
7517
                } else {
7518
                    $returnTmp = $item == $needle;
7519
                }
7520
            }
7521
7522 14
            if ($returnTmp === true) {
7523 10
                return true;
7524
            }
7525
        }
7526
7527 8
        return false;
7528
    }
7529
7530
    /**
7531
     * @param mixed $data
7532
     *
7533
     * @return array<mixed>|null
7534
     */
7535 1214
    protected function internalGetArray(&$data)
7536
    {
7537 1214
        if (\is_array($data)) {
7538 1208
            return $data;
7539
        }
7540
7541 110
        if (!$data) {
7542 7
            return [];
7543
        }
7544
7545 109
        if (\is_object($data) === true) {
7546 102
            if ($data instanceof \ArrayObject) {
7547 5
                return $data->getArrayCopy();
7548
            }
7549
7550 98
            if ($data instanceof \Generator) {
7551
                return static::createFromGeneratorImmutable($data)->toArray();
7552
            }
7553
7554 98
            if ($data instanceof \Traversable) {
7555
                return static::createFromObject($data)->toArray();
7556
            }
7557
7558 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...
7559
                return (array) $data->jsonSerialize();
7560
            }
7561
7562 98
            if (\method_exists($data, '__toArray')) {
7563
                return (array) $data->__toArray();
7564
            }
7565
7566 98
            if (\method_exists($data, '__toString')) {
7567
                return [(string) $data];
7568
            }
7569
        }
7570
7571 105
        if (\is_callable($data)) {
7572
            /**
7573
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7574
             */
7575 96
            $this->generator = new ArrayyRewindableGenerator($data);
7576
7577 96
            return [];
7578
        }
7579
7580 11
        if (\is_scalar($data)) {
7581 9
            return [$data];
7582
        }
7583
7584 2
        return null;
7585
    }
7586
7587
    /**
7588
     * Internal mechanics of remove method.
7589
     *
7590
     * @param int|string|float $key
7591
     *
7592
     * @return bool
7593
     */
7594 22
    protected function internalRemove($key): bool
7595
    {
7596 22
        $this->generatorToArray();
7597
7598
        if (
7599 22
            $this->pathSeparator
7600
            &&
7601 22
            (string) $key === $key
7602
            &&
7603 22
            \strpos($key, $this->pathSeparator) !== false
7604
        ) {
7605
            $path = \explode($this->pathSeparator, (string) $key);
7606
7607
            if ($path !== false) {
7608
                // crawl though the keys
7609
                while (\count($path, \COUNT_NORMAL) > 1) {
7610
                    $key = \array_shift($path);
7611
7612
                    if (!$this->has($key)) {
7613
                        return false;
7614
                    }
7615
7616
                    $this->array = &$this->array[$key];
7617
                }
7618
7619
                $key = \array_shift($path);
7620
            }
7621
        }
7622
7623 22
        unset($this->array[$key]);
7624
7625 22
        return true;
7626
    }
7627
7628
    /**
7629
     * Internal mechanic of set method.
7630
     *
7631
     * @param int|string|null $key
7632
     * @param mixed           $value
7633
     * @param bool            $checkProperties
7634
     *
7635
     * @return bool
7636
     */
7637 1064
    protected function internalSet(
7638
        $key,
7639
        &$value,
7640
        bool $checkProperties = true
7641
    ): bool {
7642
        if (
7643 1064
            $checkProperties === true
7644
            &&
7645 1064
            $this->properties !== []
7646
        ) {
7647 117
            $this->checkType($key, $value);
7648
        }
7649
7650 1062
        if ($key === null) {
7651
            return false;
7652
        }
7653
7654 1062
        $this->generatorToArray();
7655
7656 1062
        $array = &$this->array;
7657
7658
        /**
7659
         * https://github.com/vimeo/psalm/issues/2536
7660
         *
7661
         * @psalm-suppress PossiblyInvalidArgument
7662
         * @psalm-suppress InvalidScalarArgument
7663
         */
7664
        if (
7665 1062
            $this->pathSeparator
7666
            &&
7667 1062
            (string) $key === $key
7668
            &&
7669 1062
            \strpos($key, $this->pathSeparator) !== false
7670
        ) {
7671 9
            $path = \explode($this->pathSeparator, (string) $key);
7672
7673 9
            if ($path !== false) {
7674
                // crawl through the keys
7675 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7676 9
                    $key = \array_shift($path);
7677
7678 9
                    $array = &$array[$key];
7679
                }
7680
7681 9
                $key = \array_shift($path);
7682
            }
7683
        }
7684
7685 1062
        if ($array === null) {
7686 4
            $array = [];
7687 1059
        } elseif (!\is_array($array)) {
7688 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7689
        }
7690
7691 1062
        $array[$key] = $value;
7692
7693 1062
        return true;
7694
    }
7695
7696
    /**
7697
     * Convert a object into an array.
7698
     *
7699
     * @param mixed|object $object
7700
     *
7701
     * @return array|mixed
7702
     *
7703
     * @psalm-mutation-free
7704
     */
7705 5
    protected static function objectToArray($object)
7706
    {
7707 5
        if (!\is_object($object)) {
7708 4
            return $object;
7709
        }
7710
7711 5
        $object = \get_object_vars($object);
7712
7713
        /**
7714
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7715
         */
7716 5
        return \array_map(['static', 'objectToArray'], $object);
7717
    }
7718
7719
    /**
7720
     * @param array $data
7721
     * @param bool  $checkPropertiesInConstructor
7722
     *
7723
     * @return void
7724
     *
7725
     * @phpstan-param array<mixed,T> $data
7726
     */
7727 1212
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7728
    {
7729 1212
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7730
                                        &&
7731 1212
                                        $checkPropertiesInConstructor === true;
7732
7733 1212
        if ($this->properties !== []) {
7734 104
            foreach ($data as $key => &$valueInner) {
7735 104
                $this->internalSet(
7736 104
                    $key,
7737
                    $valueInner,
7738
                    $checkPropertiesInConstructor
7739
                );
7740
            }
7741
        } else {
7742
            if (
7743 1127
                $this->checkPropertyTypes === true
7744
                ||
7745 1127
                $checkPropertiesInConstructor === true
7746
            ) {
7747 23
                $this->properties = $this->getPropertiesFromPhpDoc();
7748
            }
7749
7750
            /** @var TypeCheckInterface[] $properties */
7751 1127
            $properties = $this->properties;
7752
7753
            if (
7754 1127
                $this->checkPropertiesMismatchInConstructor === true
7755
                &&
7756 1127
                \count($data) !== 0
7757
                &&
7758 1127
                \count(\array_diff_key($properties, $data)) > 0
7759
            ) {
7760 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...
7761
            }
7762
7763 1126
            foreach ($data as $key => &$valueInner) {
7764 955
                $this->internalSet(
7765 955
                    $key,
7766
                    $valueInner,
7767
                    $checkPropertiesInConstructor
7768
                );
7769
            }
7770
        }
7771 1204
    }
7772
7773
    /**
7774
     * sorting keys
7775
     *
7776
     * @param array      $elements
7777
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7778
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7779
     *                              <strong>SORT_NATURAL</strong></p>
7780
     *
7781
     * @return $this
7782
     *               <p>(Mutable) Return this Arrayy object.</p>
7783
     *
7784
     * @phpstan-param  array<mixed|TKey,T> $elements
7785
     * @phpstan-return static<TKey,T>
7786
     */
7787 18
    protected function sorterKeys(
7788
        array &$elements,
7789
        $direction = \SORT_ASC,
7790
        int $strategy = \SORT_REGULAR
7791
    ): self {
7792 18
        $direction = $this->getDirection($direction);
7793
7794 18
        switch ($direction) {
7795 18
            case 'desc':
7796
            case \SORT_DESC:
7797 6
                \krsort($elements, $strategy);
7798
7799 6
                break;
7800 13
            case 'asc':
7801 13
            case \SORT_ASC:
7802
            default:
7803 13
                \ksort($elements, $strategy);
7804
        }
7805
7806 18
        return $this;
7807
    }
7808
7809
    /**
7810
     * @param array      $elements  <p>Warning: used as reference</p>
7811
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7812
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7813
     *                              <strong>SORT_NATURAL</strong></p>
7814
     * @param bool       $keepKeys
7815
     *
7816
     * @return $this
7817
     *               <p>(Mutable) Return this Arrayy object.</p>
7818
     *
7819
     * @phpstan-param array<mixed|TKey,T> $elements
7820
     * @phpstan-return static<int|TKey,T>
7821
     */
7822 24
    protected function sorting(
7823
        array &$elements,
7824
        $direction = \SORT_ASC,
7825
        int $strategy = \SORT_REGULAR,
7826
        bool $keepKeys = false
7827
    ): self {
7828 24
        $direction = $this->getDirection($direction);
7829
7830 24
        if (!$strategy) {
7831 24
            $strategy = \SORT_REGULAR;
7832
        }
7833
7834 24
        switch ($direction) {
7835 24
            case 'desc':
7836
            case \SORT_DESC:
7837 13
                if ($keepKeys) {
7838 9
                    \arsort($elements, $strategy);
7839
                } else {
7840 4
                    \rsort($elements, $strategy);
7841
                }
7842
7843 13
                break;
7844 11
            case 'asc':
7845 11
            case \SORT_ASC:
7846
            default:
7847 11
                if ($keepKeys) {
7848 4
                    \asort($elements, $strategy);
7849
                } else {
7850 7
                    \sort($elements, $strategy);
7851
                }
7852
        }
7853
7854 24
        return $this;
7855
    }
7856
7857
    /**
7858
     * @param array $array
7859
     *
7860
     * @return array
7861
     *
7862
     * @psalm-mutation-free
7863
     */
7864 25
    private function getArrayRecursiveHelperArrayy(array $array)
7865
    {
7866 25
        if ($array === []) {
7867
            return [];
7868
        }
7869
7870 25
        \array_walk_recursive(
7871 25
            $array,
7872
            /**
7873
             * @param array|self $item
7874
             *
7875
             * @return void
7876
             */
7877 25
            static function (&$item) {
7878 25
                if ($item instanceof self) {
7879 1
                    $item = $item->getArray();
7880
                }
7881 25
            }
7882
        );
7883
7884 25
        return $array;
7885
    }
7886
7887
    /**
7888
     * @param int|string|null $key
7889
     * @param mixed           $value
7890
     *
7891
     * @return void
7892
     */
7893 117
    private function checkType($key, $value)
7894
    {
7895
        if (
7896 117
            $key !== null
7897
            &&
7898 117
            isset($this->properties[$key]) === false
7899
            &&
7900 117
            $this->checkPropertiesMismatch === true
7901
        ) {
7902
            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...
7903
        }
7904
7905 117
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7906 102
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7907 24
        } elseif ($key !== null && isset($this->properties[$key])) {
7908 24
            $this->properties[$key]->checkType($value);
7909
        }
7910 115
    }
7911
}
7912