Completed
Push — master ( 10a2dd...baf3d3 )
by Lars
01:51
created

Arrayy::slice()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1

Importance

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

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

Loading history...
3097
     *
3098
     * @internal
3099
     */
3100 6
    public function getPhpDocPropertiesFromClass()
3101
    {
3102 6
        if ($this->properties === []) {
3103 1
            $this->properties = $this->getPropertiesFromPhpDoc();
3104
        }
3105
3106 6
        return $this->properties;
3107
    }
3108
3109
    /**
3110
     * Get the current array from the "Arrayy"-object as list.
3111
     *
3112
     * alias for "toList()"
3113
     *
3114
     * @param bool $convertAllArrayyElements <p>
3115
     *                                       Convert all Child-"Arrayy" objects also to arrays.
3116
     *                                       </p>
3117
     *
3118
     * @return array
3119
     *
3120
     * @phpstan-return list<mixed>|list<T>
3121
     * @psalm-mutation-free
3122
     *
3123
     * @see Arrayy::toList()
3124
     */
3125 1
    public function getList(bool $convertAllArrayyElements = false): array
3126
    {
3127 1
        return $this->toList($convertAllArrayyElements);
3128
    }
3129
3130
    /**
3131
     * Returns the values from a single column of the input array, identified by
3132
     * the $columnKey, can be used to extract data-columns from multi-arrays.
3133
     *
3134
     * EXAMPLE: <code>
3135
     * a([['foo' => 'bar', 'id' => 1], ['foo => 'lall', 'id' => 2]])->getColumn('foo', 'id'); // Arrayy[1 => 'bar', 2 => 'lall']
3136
     * </code>
3137
     *
3138
     * INFO: Optionally, you may provide an $indexKey to index the values in the returned
3139
     *       array by the values from the $indexKey column in the input array.
3140
     *
3141
     * @param int|string|null $columnKey
3142
     * @param int|string|null $indexKey
3143
     *
3144
     * @return static
3145
     *                <p>(Immutable)</p>
3146
     *
3147
     * @phpstan-return static<TKey,T>
3148
     * @psalm-mutation-free
3149
     */
3150 1
    public function getColumn($columnKey = null, $indexKey = null): self
3151
    {
3152 1
        if ($columnKey === null && $indexKey === null) {
3153
            $generator = function () {
3154 1
                foreach ($this->getGenerator() as $key => $value) {
3155 1
                    yield $value;
3156
                }
3157 1
            };
3158
        } else {
3159
            $generator = function () use ($columnKey, $indexKey) {
3160 1
                foreach ($this->getGenerator() as $key => $value) {
3161
                    // reset
3162 1
                    $newKey = null;
3163 1
                    $newValue = null;
3164 1
                    $newValueFound = false;
3165
3166 1
                    if ($indexKey !== null) {
3167 1
                        foreach ($value as $keyInner => $valueInner) {
3168 1
                            if ($indexKey === $keyInner) {
3169 1
                                $newKey = $valueInner;
3170
                            }
3171
3172 1
                            if ($columnKey === $keyInner) {
3173 1
                                $newValue = $valueInner;
3174 1
                                $newValueFound = true;
3175
                            }
3176
                        }
3177
                    } else {
3178 1
                        foreach ($value as $keyInner => $valueInner) {
3179 1
                            if ($columnKey === $keyInner) {
3180 1
                                $newValue = $valueInner;
3181 1
                                $newValueFound = true;
3182
                            }
3183
                        }
3184
                    }
3185
3186 1
                    if ($newValueFound === false) {
3187 1
                        if ($newKey !== null) {
3188 1
                            yield $newKey => $value;
3189
                        } else {
3190 1
                            yield $value;
3191
                        }
3192
                    } else {
3193
                        /** @noinspection NestedPositiveIfStatementsInspection */
3194 1
                        if ($newKey !== null) {
3195 1
                            yield $newKey => $newValue;
3196
                        } else {
3197 1
                            yield $newValue;
3198
                        }
3199
                    }
3200
                }
3201 1
            };
3202
        }
3203
3204 1
        return static::create(
3205 1
            $generator,
3206 1
            $this->iteratorClass,
3207 1
            false
3208
        );
3209
    }
3210
3211
    /**
3212
     * Get the current array from the "Arrayy"-object as generator by reference.
3213
     *
3214
     * @return \Generator
3215
     *
3216
     * @phpstan-return \Generator<mixed,T>|\Generator<TKey,T>
3217
     */
3218 75
    public function &getGeneratorByReference(): \Generator
3219
    {
3220 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3221
            // -> false-positive -> see "&" from method
3222
            /** @noinspection YieldFromCanBeUsedInspection */
3223 17
            foreach ($this->generator as $key => $value) {
3224 17
                yield $key => $value;
3225
            }
3226
3227 5
            return;
3228
        }
3229
3230
        // -> false-positive -> see "&$value"
3231
        /** @noinspection YieldFromCanBeUsedInspection */
3232
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3233 59
        foreach ($this->array as $key => &$value) {
3234 54
            yield $key => $value;
3235
        }
3236 35
    }
3237
3238
    /**
3239
     * Get the current array from the "Arrayy"-object as generator.
3240
     *
3241
     * @return \Generator
3242
     *
3243
     * @phpstan-return \Generator<mixed,T>|\Generator<TKey,T>
3244
     * @psalm-mutation-free
3245
     */
3246 1073
    public function getGenerator(): \Generator
3247
    {
3248 1073
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3249 77
            yield from $this->generator;
3250
3251 77
            return;
3252
        }
3253
3254 1071
        yield from $this->array;
3255 1030
    }
3256
3257
    /**
3258
     * alias: for "Arrayy->keys()"
3259
     *
3260
     * @return static
3261
     *                <p>(Immutable)</p>
3262
     *
3263
     * @see          Arrayy::keys()
3264
     *
3265
     * @phpstan-return static<int,TKey>
3266
     * @psalm-mutation-free
3267
     */
3268 2
    public function getKeys()
3269
    {
3270 2
        return $this->keys();
3271
    }
3272
3273
    /**
3274
     * Get the current array from the "Arrayy"-object as object.
3275
     *
3276
     * @return \stdClass
3277
     */
3278 4
    public function getObject(): \stdClass
3279
    {
3280 4
        return self::arrayToObject($this->toArray());
3281
    }
3282
3283
    /**
3284
     * alias: for "Arrayy->randomImmutable()"
3285
     *
3286
     * @return static
3287
     *                <p>(Immutable)</p>
3288
     *
3289
     * @see          Arrayy::randomImmutable()
3290
     *
3291
     * @phpstan-return static<int|array-key,T>
3292
     */
3293 4
    public function getRandom(): self
3294
    {
3295 4
        return $this->randomImmutable();
3296
    }
3297
3298
    /**
3299
     * alias: for "Arrayy->randomKey()"
3300
     *
3301
     * @return mixed
3302
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3303
     *
3304
     * @see Arrayy::randomKey()
3305
     */
3306 3
    public function getRandomKey()
3307
    {
3308 3
        return $this->randomKey();
3309
    }
3310
3311
    /**
3312
     * alias: for "Arrayy->randomKeys()"
3313
     *
3314
     * @param int $number
3315
     *
3316
     * @return static
3317
     *                <p>(Immutable)</p>
3318
     *
3319
     * @see          Arrayy::randomKeys()
3320
     *
3321
     * @phpstan-return static<TKey,T>
3322
     */
3323 8
    public function getRandomKeys(int $number): self
3324
    {
3325 8
        return $this->randomKeys($number);
3326
    }
3327
3328
    /**
3329
     * alias: for "Arrayy->randomValue()"
3330
     *
3331
     * @return mixed
3332
     *               <p>Get a random value or null if there wasn't a value.</p>
3333
     *
3334
     * @see Arrayy::randomValue()
3335
     */
3336 3
    public function getRandomValue()
3337
    {
3338 3
        return $this->randomValue();
3339
    }
3340
3341
    /**
3342
     * alias: for "Arrayy->randomValues()"
3343
     *
3344
     * @param int $number
3345
     *
3346
     * @return static
3347
     *                <p>(Immutable)</p>
3348
     *
3349
     * @see          Arrayy::randomValues()
3350
     *
3351
     * @phpstan-return static<TKey,T>
3352
     */
3353 6
    public function getRandomValues(int $number): self
3354
    {
3355 6
        return $this->randomValues($number);
3356
    }
3357
3358
    /**
3359
     * Gets all values.
3360
     *
3361
     * @return static
3362
     *                <p>The values of all elements in this array, in the order they
3363
     *                appear in the array.</p>
3364
     *
3365
     * @phpstan-return static<TKey,T>
3366
     */
3367 4
    public function getValues()
3368
    {
3369 4
        $this->generatorToArray(false);
3370
3371 4
        return static::create(
3372 4
            \array_values($this->array),
3373 4
            $this->iteratorClass,
3374 4
            false
3375
        );
3376
    }
3377
3378
    /**
3379
     * Gets all values via Generator.
3380
     *
3381
     * @return \Generator
3382
     *                    <p>The values of all elements in this array, in the order they
3383
     *                    appear in the array as Generator.</p>
3384
     *
3385
     * @phpstan-return \Generator<TKey,T>
3386
     */
3387 4
    public function getValuesYield(): \Generator
3388
    {
3389 4
        yield from $this->getGenerator();
3390 4
    }
3391
3392
    /**
3393
     * Group values from a array according to the results of a closure.
3394
     *
3395
     * @param callable|string $grouper  <p>A callable function name.</p>
3396
     * @param bool            $saveKeys
3397
     *
3398
     * @return static
3399
     *                <p>(Immutable)</p>
3400
     *
3401
     * @phpstan-return static<TKey,T>
3402
     * @psalm-mutation-free
3403
     */
3404 4
    public function group($grouper, bool $saveKeys = false): self
3405
    {
3406
        // init
3407 4
        $result = [];
3408
3409
        // Iterate over values, group by property/results from closure.
3410 4
        foreach ($this->getGenerator() as $key => $value) {
3411 4
            if (\is_callable($grouper) === true) {
3412 3
                $groupKey = $grouper($value, $key);
3413
            } else {
3414 1
                $groupKey = $this->get($grouper);
3415
            }
3416
3417 4
            $newValue = $this->get($groupKey, null, $result);
3418
3419 4
            if ($groupKey instanceof self) {
3420
                $groupKey = $groupKey->toArray();
3421
            }
3422
3423 4
            if ($newValue instanceof self) {
3424 4
                $newValue = $newValue->toArray();
3425
            }
3426
3427
            // Add to results.
3428 4
            if ($groupKey !== null) {
3429 3
                if ($saveKeys) {
3430 2
                    $result[$groupKey] = $newValue;
3431 2
                    $result[$groupKey][$key] = $value;
3432
                } else {
3433 1
                    $result[$groupKey] = $newValue;
3434 4
                    $result[$groupKey][] = $value;
3435
                }
3436
            }
3437
        }
3438
3439 4
        return static::create(
3440 4
            $result,
3441 4
            $this->iteratorClass,
3442 4
            false
3443
        );
3444
    }
3445
3446
    /**
3447
     * Check if an array has a given key.
3448
     *
3449
     * @param mixed $key
3450
     *
3451
     * @return bool
3452
     */
3453 30
    public function has($key): bool
3454
    {
3455 30
        static $UN_FOUND = null;
3456
3457 30
        if ($UN_FOUND === null) {
3458
            // Generate unique string to use as marker.
3459 1
            $UN_FOUND = 'arrayy--' . \uniqid('arrayy', true);
3460
        }
3461
3462 30
        if (\is_array($key)) {
3463 1
            if ($key === []) {
3464
                return false;
3465
            }
3466
3467 1
            foreach ($key as $keyTmp) {
3468 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3469 1
                if ($found === false) {
3470 1
                    return false;
3471
                }
3472
            }
3473
3474 1
            return true;
3475
        }
3476
3477 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3478
    }
3479
3480
    /**
3481
     * Check if an array has a given value.
3482
     *
3483
     * INFO: If you need to search recursive please use ```contains($value, true)```.
3484
     *
3485
     * @param mixed $value
3486
     *
3487
     * @return bool
3488
     */
3489 1
    public function hasValue($value): bool
3490
    {
3491 1
        return $this->contains($value);
3492
    }
3493
3494
    /**
3495
     * Implodes the values of this array.
3496
     *
3497
     * EXAMPLE: <code>
3498
     * a([0 => -9, 1, 2])->implode('|'); // '-9|1|2'
3499
     * </code>
3500
     *
3501
     * @param string $glue
3502
     * @param string $prefix
3503
     *
3504
     * @return string
3505
     * @psalm-mutation-free
3506
     */
3507 28
    public function implode(string $glue = '', string $prefix = ''): string
3508
    {
3509 28
        return $prefix . $this->implode_recursive($glue, $this->toArray(), false);
3510
    }
3511
3512
    /**
3513
     * Implodes the keys of this array.
3514
     *
3515
     * @param string $glue
3516
     *
3517
     * @return string
3518
     * @psalm-mutation-free
3519
     */
3520 8
    public function implodeKeys(string $glue = ''): string
3521
    {
3522 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3523
    }
3524
3525
    /**
3526
     * Given a list and an iterate-function that returns
3527
     * a key for each element in the list (or a property name),
3528
     * returns an object with an index of each item.
3529
     *
3530
     * @param mixed $key
3531
     *
3532
     * @return static
3533
     *                <p>(Immutable)</p>
3534
     *
3535
     * @phpstan-return static<TKey,T>
3536
     * @psalm-mutation-free
3537
     */
3538 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...
3539
    {
3540
        // init
3541 4
        $results = [];
3542
3543 4
        foreach ($this->getGenerator() as $a) {
3544 4
            if (\array_key_exists($key, $a) === true) {
3545 4
                $results[$a[$key]] = $a;
3546
            }
3547
        }
3548
3549 4
        return static::create(
3550 4
            $results,
3551 4
            $this->iteratorClass,
3552 4
            false
3553
        );
3554
    }
3555
3556
    /**
3557
     * alias: for "Arrayy->searchIndex()"
3558
     *
3559
     * @param mixed $value <p>The value to search for.</p>
3560
     *
3561
     * @return false|mixed
3562
     *
3563
     * @see Arrayy::searchIndex()
3564
     */
3565 4
    public function indexOf($value)
3566
    {
3567 4
        return $this->searchIndex($value);
3568
    }
3569
3570
    /**
3571
     * Get everything but the last..$to items.
3572
     *
3573
     * EXAMPLE: <code>
3574
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->initial(2); // Arrayy[0 => 'foo']
3575
     * </code>
3576
     *
3577
     * @param int $to
3578
     *
3579
     * @return static
3580
     *                <p>(Immutable)</p>
3581
     *
3582
     * @phpstan-return static<TKey,T>
3583
     * @psalm-mutation-free
3584
     */
3585 12
    public function initial(int $to = 1): self
3586
    {
3587 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3588
    }
3589
3590
    /**
3591
     * Return an array with all elements found in input array.
3592
     *
3593
     * EXAMPLE: <code>
3594
     * a(['foo', 'bar'])->intersection(['bar', 'baz']); // Arrayy['bar']
3595
     * </code>
3596
     *
3597
     * @param array $search
3598
     * @param bool  $keepKeys
3599
     *
3600
     * @return static
3601
     *                <p>(Immutable)</p>
3602
     *
3603
     * @phpstan-param  array<TKey,T> $search
3604
     * @phpstan-return static<TKey,T>
3605
     * @psalm-mutation-free
3606
     */
3607 4
    public function intersection(array $search, bool $keepKeys = false): self
3608
    {
3609 4
        if ($keepKeys) {
3610
            /**
3611
             * @psalm-suppress MissingClosureReturnType
3612
             * @psalm-suppress MissingClosureParamType
3613
             */
3614 1
            return static::create(
3615 1
                \array_uintersect(
3616 1
                    $this->toArray(),
3617 1
                    $search,
3618
                    static function ($a, $b) {
3619 1
                        return $a === $b ? 0 : -1;
3620 1
                    }
3621
                ),
3622 1
                $this->iteratorClass,
3623 1
                false
3624
            );
3625
        }
3626
3627 3
        return static::create(
3628 3
            \array_values(\array_intersect($this->toArray(), $search)),
3629 3
            $this->iteratorClass,
3630 3
            false
3631
        );
3632
    }
3633
3634
    /**
3635
     * Return an array with all elements found in input array.
3636
     *
3637
     * @param array ...$array
3638
     *
3639
     * @return static
3640
     *                <p>(Immutable)</p>
3641
     *
3642
     * @phpstan-param  array<array<TKey,T>> ...$array
3643
     * @phpstan-return static<TKey,T>
3644
     * @psalm-mutation-free
3645
     */
3646 1
    public function intersectionMulti(...$array): self
3647
    {
3648 1
        return static::create(
3649 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3650 1
            $this->iteratorClass,
3651 1
            false
3652
        );
3653
    }
3654
3655
    /**
3656
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3657
     *
3658
     * EXAMPLE: <code>
3659
     * a(['foo', 'bar'])->intersects(['föö', 'bär']); // false
3660
     * </code>
3661
     *
3662
     * @param array $search
3663
     *
3664
     * @return bool
3665
     *
3666
     * @phpstan-param array<TKey,T> $search
3667
     */
3668 1
    public function intersects(array $search): bool
3669
    {
3670 1
        return $this->intersection($search)->count() > 0;
3671
    }
3672
3673
    /**
3674
     * Invoke a function on all of an array's values.
3675
     *
3676
     * @param callable $callable
3677
     * @param mixed    $arguments
3678
     *
3679
     * @return static
3680
     *                <p>(Immutable)</p>
3681
     *
3682
     * @phpstan-param  callable(T=,mixed):mixed $callable
3683
     * @phpstan-return static<TKey,T>
3684
     * @psalm-mutation-free
3685
     */
3686 1
    public function invoke($callable, $arguments = []): self
3687
    {
3688
        // If one argument given for each iteration, create an array for it.
3689 1
        if (!\is_array($arguments)) {
3690 1
            $arguments = \array_fill(
3691 1
                0,
3692 1
                $this->count(),
3693 1
                $arguments
3694
            );
3695
        }
3696
3697
        // If the callable has arguments, pass them.
3698 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...
3699 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3700
        } else {
3701 1
            $array = $this->map($callable);
3702
        }
3703
3704 1
        return static::create(
3705 1
            $array,
3706 1
            $this->iteratorClass,
3707 1
            false
3708
        );
3709
    }
3710
3711
    /**
3712
     * Check whether array is associative or not.
3713
     *
3714
     * EXAMPLE: <code>
3715
     * a(['foo' => 'bar', 2, 3])->isAssoc(); // true
3716
     * </code>
3717
     *
3718
     * @param bool $recursive
3719
     *
3720
     * @return bool
3721
     *              <p>Returns true if associative, false otherwise.</p>
3722
     */
3723 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...
3724
    {
3725 15
        if ($this->isEmpty()) {
3726 3
            return false;
3727
        }
3728
3729
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3730 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3731 13
            if ((string) $key !== $key) {
3732 13
                return false;
3733
            }
3734
        }
3735
3736 3
        return true;
3737
    }
3738
3739
    /**
3740
     * Check if a given key or keys are empty.
3741
     *
3742
     * @param int|int[]|string|string[]|null $keys
3743
     *
3744
     * @return bool
3745
     *              <p>Returns true if empty, false otherwise.</p>
3746
     * @psalm-mutation-free
3747
     */
3748 45
    public function isEmpty($keys = null): bool
3749
    {
3750 45
        if ($this->generator) {
3751
            return $this->toArray() === [];
3752
        }
3753
3754 45
        if ($keys === null) {
3755 43
            return $this->array === [];
3756
        }
3757
3758 2
        foreach ((array) $keys as $key) {
3759 2
            if (!empty($this->get($key))) {
3760 2
                return false;
3761
            }
3762
        }
3763
3764 2
        return true;
3765
    }
3766
3767
    /**
3768
     * Check if the current array is equal to the given "$array" or not.
3769
     *
3770
     * EXAMPLE: <code>
3771
     * a(['💩'])->isEqual(['💩']); // true
3772
     * </code>
3773
     *
3774
     * @param array $array
3775
     *
3776
     * @return bool
3777
     *
3778
     * @phpstan-param array<int|string,mixed> $array
3779
     */
3780 1
    public function isEqual(array $array): bool
3781
    {
3782 1
        return $this->toArray() === $array;
3783
    }
3784
3785
    /**
3786
     * Check if the current array is a multi-array.
3787
     *
3788
     * EXAMPLE: <code>
3789
     * a(['foo' => [1, 2 , 3]])->isMultiArray(); // true
3790
     * </code>
3791
     *
3792
     * @return bool
3793
     */
3794 22
    public function isMultiArray(): bool
3795
    {
3796 22
        foreach ($this->getGenerator() as $key => $value) {
3797 20
            if (\is_array($value)) {
3798 20
                return true;
3799
            }
3800
        }
3801
3802 18
        return false;
3803
    }
3804
3805
    /**
3806
     * Check whether array is numeric or not.
3807
     *
3808
     * @return bool
3809
     *              <p>Returns true if numeric, false otherwise.</p>
3810
     */
3811 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...
3812
    {
3813 5
        if ($this->isEmpty()) {
3814 2
            return false;
3815
        }
3816
3817
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3818 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3819 4
            if ((int) $key !== $key) {
3820 4
                return false;
3821
            }
3822
        }
3823
3824 2
        return true;
3825
    }
3826
3827
    /**
3828
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3829
     *
3830
     * EXAMPLE: <code>
3831
     * a([0 => 'foo', 1 => 'lall', 2 => 'foobar'])->isSequential(); // true
3832
     * </code>
3833
     *
3834
     * INFO: If the array is empty we count it as non-sequential.
3835
     *
3836
     * @param bool $recursive
3837
     *
3838
     * @return bool
3839
     * @psalm-mutation-free
3840
     */
3841 10
    public function isSequential(bool $recursive = false): bool
3842
    {
3843 10
        $i = 0;
3844 10
        foreach ($this->getGenerator() as $key => $value) {
3845
            /** @noinspection IsIterableCanBeUsedInspection */
3846
            if (
3847 9
                $recursive
3848
                &&
3849 9
                (\is_array($value) || $value instanceof \Traversable)
3850
                &&
3851 9
                self::create($value)->isSequential() === false
3852
            ) {
3853 1
                return false;
3854
            }
3855
3856 9
            if ($key !== $i) {
3857 3
                return false;
3858
            }
3859
3860 8
            ++$i;
3861
        }
3862
3863
        /** @noinspection IfReturnReturnSimplificationInspection */
3864 9
        if ($i === 0) {
3865 3
            return false;
3866
        }
3867
3868 8
        return true;
3869
    }
3870
3871
    /**
3872
     * @return array
3873
     *
3874
     * @phpstan-return array<TKey,T>
3875
     */
3876 2
    public function jsonSerialize(): array
3877
    {
3878 2
        return $this->toArray();
3879
    }
3880
3881
    /**
3882
     * Gets the key/index of the element at the current internal iterator position.
3883
     *
3884
     * @return int|string|null
3885
     * @phpstan-return array-key|null
3886
     */
3887
    public function key()
3888
    {
3889
        if ($this->generator) {
3890
            return $this->generator->key();
3891
        }
3892
3893
        return \key($this->array);
3894
    }
3895
3896
    /**
3897
     * Checks if the given key exists in the provided array.
3898
     *
3899
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3900
     *       then you need to use "Arrayy->offsetExists()".
3901
     *
3902
     * @param int|string $key the key to look for
3903
     *
3904
     * @return bool
3905
     * @psalm-mutation-free
3906
     */
3907 174
    public function keyExists($key): bool
3908
    {
3909 174
        foreach ($this->getGenerator() as $keyTmp => $value) {
3910 169
            if ($key === $keyTmp) {
3911 169
                return true;
3912
            }
3913
        }
3914
3915 131
        return false;
3916
    }
3917
3918
    /**
3919
     * Get all keys from the current array.
3920
     *
3921
     * EXAMPLE: <code>
3922
     * a([1 => 'foo', 2 => 'foo2', 3 => 'bar'])->keys(); // Arrayy[1, 2, 3]
3923
     * </code>
3924
     *
3925
     * @param bool       $recursive     [optional] <p>
3926
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3927
     *                                  </p>
3928
     * @param mixed|null $search_values [optional] <p>
3929
     *                                  If specified, then only keys containing these values are returned.
3930
     *                                  </p>
3931
     * @param bool       $strict        [optional] <p>
3932
     *                                  Determines if strict comparison (===) should be used during the search.
3933
     *                                  </p>
3934
     *
3935
     * @return static
3936
     *                <p>(Immutable) An array of all the keys in input.</p>
3937
     *
3938
     * @phpstan-return static<int,TKey>
3939
     * @psalm-mutation-free
3940
     */
3941 29
    public function keys(
3942
        bool $recursive = false,
3943
        $search_values = null,
3944
        bool $strict = true
3945
    ): self {
3946
3947
        // recursive
3948
3949 29
        if ($recursive === true) {
3950 4
            $array = $this->array_keys_recursive(
3951 4
                null,
3952 4
                $search_values,
3953 4
                $strict
3954
            );
3955
3956 4
            return static::create(
3957 4
                $array,
3958 4
                $this->iteratorClass,
3959 4
                false
3960
            );
3961
        }
3962
3963
        // non recursive
3964
3965 28
        if ($search_values === null) {
3966
            $arrayFunction = function (): \Generator {
3967 28
                foreach ($this->getGenerator() as $key => $value) {
3968 26
                    yield $key;
3969
                }
3970 28
            };
3971
        } else {
3972
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3973 1
                $is_array_tmp = \is_array($search_values);
3974
3975
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3976 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3977 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...
3978
                        (
3979 1
                            $is_array_tmp === false
3980
                            &&
3981 1
                            $strict === true
3982
                            &&
3983 1
                            $search_values === $value
3984
                        )
3985
                        ||
3986
                        (
3987 1
                            $is_array_tmp === false
3988
                            &&
3989 1
                            $strict === false
3990
                            &&
3991 1
                            $search_values == $value
3992
                        )
3993
                        ||
3994
                        (
3995 1
                            $is_array_tmp === true
3996
                            &&
3997 1
                            \in_array($value, $search_values, $strict)
3998
                        )
3999
                    ) {
4000 1
                        yield $key;
4001
                    }
4002
                }
4003 1
            };
4004
        }
4005
4006 28
        return static::create(
4007 28
            $arrayFunction,
4008 28
            $this->iteratorClass,
4009 28
            false
4010
        );
4011
    }
4012
4013
    /**
4014
     * Sort an array by key in reverse order.
4015
     *
4016
     * @param int $sort_flags [optional] <p>
4017
     *                        You may modify the behavior of the sort using the optional
4018
     *                        parameter sort_flags, for details
4019
     *                        see sort.
4020
     *                        </p>
4021
     *
4022
     * @return $this
4023
     *               <p>(Mutable) Return this Arrayy object.</p>
4024
     *
4025
     * @phpstan-return static<TKey,T>
4026
     */
4027 4
    public function krsort(int $sort_flags = 0): self
4028
    {
4029 4
        $this->generatorToArray();
4030
4031 4
        \krsort($this->array, $sort_flags);
4032
4033 4
        return $this;
4034
    }
4035
4036
    /**
4037
     * Sort an array by key in reverse order.
4038
     *
4039
     * @param int $sort_flags [optional] <p>
4040
     *                        You may modify the behavior of the sort using the optional
4041
     *                        parameter sort_flags, for details
4042
     *                        see sort.
4043
     *                        </p>
4044
     *
4045
     * @return $this
4046
     *               <p>(Immutable) Return this Arrayy object.</p>
4047
     *
4048
     * @phpstan-return static<TKey,T>
4049
     * @psalm-mutation-free
4050
     */
4051 4
    public function krsortImmutable(int $sort_flags = 0): self
4052
    {
4053 4
        $that = clone $this;
4054
4055
        /**
4056
         * @psalm-suppress ImpureMethodCall - object is already cloned
4057
         */
4058 4
        $that->krsort($sort_flags);
4059
4060 4
        return $that;
4061
    }
4062
4063
    /**
4064
     * Get the last value from the current array.
4065
     *
4066
     * EXAMPLE: <code>
4067
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->last(); // 'lall'
4068
     * </code>
4069
     *
4070
     * @return mixed|null
4071
     *                    <p>Return null if there wasn't a element.</p>
4072
     *
4073
     * @phpstan-return T|null
4074
     * @psalm-mutation-free
4075
     */
4076 17
    public function last()
4077
    {
4078 17
        $key_last = $this->lastKey();
4079 17
        if ($key_last === null) {
4080 2
            return null;
4081
        }
4082
4083 15
        return $this->get($key_last);
4084
    }
4085
4086
    /**
4087
     * Get the last key from the current array.
4088
     *
4089
     * @return mixed|null
4090
     *                    <p>Return null if there wasn't a element.</p>
4091
     * @psalm-mutation-free
4092
     */
4093 21
    public function lastKey()
4094
    {
4095 21
        $this->generatorToArray();
4096
4097 21
        return \array_key_last($this->array);
4098
    }
4099
4100
    /**
4101
     * Get the last value(s) from the current array.
4102
     *
4103
     * EXAMPLE: <code>
4104
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
4105
     * </code>
4106
     *
4107
     * @param int|null $number
4108
     *
4109
     * @return static
4110
     *                <p>(Immutable)</p>
4111
     *
4112
     * @phpstan-return static<TKey,T>
4113
     * @psalm-mutation-free
4114
     */
4115 13
    public function lastsImmutable(int $number = null): self
4116
    {
4117 13
        if ($this->isEmpty()) {
4118 1
            return static::create(
4119 1
                [],
4120 1
                $this->iteratorClass,
4121 1
                false
4122
            );
4123
        }
4124
4125 12
        if ($number === null) {
4126 8
            $poppedValue = $this->last();
4127
4128 8
            if ($poppedValue === null) {
4129 1
                $poppedValue = [$poppedValue];
4130
            } else {
4131 7
                $poppedValue = (array) $poppedValue;
4132
            }
4133
4134 8
            $arrayy = static::create(
4135 8
                $poppedValue,
4136 8
                $this->iteratorClass,
4137 8
                false
4138
            );
4139
        } else {
4140 4
            $arrayy = $this->rest(-$number);
4141
        }
4142
4143 12
        return $arrayy;
4144
    }
4145
4146
    /**
4147
     * Get the last value(s) from the current array.
4148
     *
4149
     * EXAMPLE: <code>
4150
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
4151
     * </code>
4152
     *
4153
     * @param int|null $number
4154
     *
4155
     * @return $this
4156
     *               <p>(Mutable)</p>
4157
     *
4158
     * @phpstan-return static<TKey,T>
4159
     */
4160 13
    public function lastsMutable(int $number = null): self
4161
    {
4162 13
        if ($this->isEmpty()) {
4163 1
            return $this;
4164
        }
4165
4166 12
        $this->array = $this->lastsImmutable($number)->toArray();
4167 12
        $this->generator = null;
4168
4169 12
        return $this;
4170
    }
4171
4172
    /**
4173
     * Count the values from the current array.
4174
     *
4175
     * alias: for "Arrayy->count()"
4176
     *
4177
     * @param int $mode
4178
     *
4179
     * @return int
4180
     *
4181
     * @see Arrayy::count()
4182
     */
4183 20
    public function length(int $mode = \COUNT_NORMAL): int
4184
    {
4185 20
        return $this->count($mode);
4186
    }
4187
4188
    /**
4189
     * Apply the given function to the every element of the array,
4190
     * collecting the results.
4191
     *
4192
     * EXAMPLE: <code>
4193
     * a(['foo', 'Foo'])->map('mb_strtoupper'); // Arrayy['FOO', 'FOO']
4194
     * </code>
4195
     *
4196
     * @param callable $callable
4197
     * @param bool     $useKeyAsSecondParameter
4198
     * @param mixed    ...$arguments
4199
     *
4200
     * @return static
4201
     *                <p>(Immutable) Arrayy object with modified elements.</p>
4202
     *
4203
     * @template T2
4204
     *              <p>The output value type.</p>
4205
     *
4206
     * @phpstan-param callable(T,TKey=,mixed=):T2 $callable
4207
     * @phpstan-return static<TKey,T2>
4208
     * @psalm-mutation-free
4209
     */
4210 6
    public function map(
4211
        callable $callable,
4212
        bool $useKeyAsSecondParameter = false,
4213
        ...$arguments
4214
    ) {
4215
        /**
4216
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
4217
         */
4218 6
        $useArguments = \func_num_args() > 2;
4219
4220 6
        return static::create(
4221
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
4222 6
                foreach ($this->getGenerator() as $key => $value) {
4223 5
                    if ($useArguments) {
4224 3
                        if ($useKeyAsSecondParameter) {
4225
                            yield $key => $callable($value, $key, ...$arguments);
4226
                        } else {
4227 3
                            yield $key => $callable($value, ...$arguments);
4228
                        }
4229
                    } else {
4230
                        /** @noinspection NestedPositiveIfStatementsInspection */
4231 5
                        if ($useKeyAsSecondParameter) {
4232
                            yield $key => $callable($value, $key);
4233
                        } else {
4234 5
                            yield $key => $callable($value);
4235
                        }
4236
                    }
4237
                }
4238 6
            },
4239 6
            $this->iteratorClass,
4240 6
            false
4241
        );
4242
    }
4243
4244
    /**
4245
     * Check if all items in current array match a truth test.
4246
     *
4247
     * EXAMPLE: <code>
4248
     * $closure = function ($value, $key) {
4249
     *     return ($value % 2 === 0);
4250
     * };
4251
     * a([2, 4, 8])->matches($closure); // true
4252
     * </code>
4253
     *
4254
     * @param \Closure $closure
4255
     *
4256
     * @return bool
4257
     *
4258
     * @phpstan-param \Closure(T=,TKey=):bool $closure
4259
     */
4260 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...
4261
    {
4262 15
        if ($this->count() === 0) {
4263 2
            return false;
4264
        }
4265
4266 13
        foreach ($this->getGenerator() as $key => $value) {
4267 13
            $value = $closure($value, $key);
4268
4269 13
            if ($value === false) {
4270 13
                return false;
4271
            }
4272
        }
4273
4274 7
        return true;
4275
    }
4276
4277
    /**
4278
     * Check if any item in the current array matches a truth test.
4279
     *
4280
     * EXAMPLE: <code>
4281
     * $closure = function ($value, $key) {
4282
     *     return ($value % 2 === 0);
4283
     * };
4284
     * a([1, 4, 7])->matches($closure); // true
4285
     * </code>
4286
     *
4287
     * @param \Closure $closure
4288
     *
4289
     * @return bool
4290
     *
4291
     * @phpstan-param \Closure(T=,TKey=):bool $closure
4292
     */
4293 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...
4294
    {
4295 14
        if ($this->count() === 0) {
4296 2
            return false;
4297
        }
4298
4299 12
        foreach ($this->getGenerator() as $key => $value) {
4300 12
            $value = $closure($value, $key);
4301
4302 12
            if ($value === true) {
4303 12
                return true;
4304
            }
4305
        }
4306
4307 4
        return false;
4308
    }
4309
4310
    /**
4311
     * Get the max value from an array.
4312
     *
4313
     * EXAMPLE: <code>
4314
     * a([-9, -8, -7, 1.32])->max(); // 1.32
4315
     * </code>
4316
     *
4317
     * @return false|float|int|string
4318
     *                                <p>Will return false if there are no values.</p>
4319
     */
4320 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...
4321
    {
4322 10
        if ($this->count() === 0) {
4323 1
            return false;
4324
        }
4325
4326 9
        $max = false;
4327
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4328 9
        foreach ($this->getGeneratorByReference() as &$value) {
4329
            if (
4330 9
                $max === false
4331
                ||
4332 9
                $value > $max
4333
            ) {
4334 9
                $max = $value;
4335
            }
4336
        }
4337
4338 9
        return $max;
4339
    }
4340
4341
    /**
4342
     * Merge the new $array into the current array.
4343
     *
4344
     * - keep key,value from the current array, also if the index is in the new $array
4345
     *
4346
     * EXAMPLE: <code>
4347
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4348
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4349
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[1 => 'one', 'foo' => 'bar2', 3 => 'three']
4350
     * // ---
4351
     * $array1 = [0 => 'one', 1 => 'foo'];
4352
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4353
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2']
4354
     * </code>
4355
     *
4356
     * @param array $array
4357
     * @param bool  $recursive
4358
     *
4359
     * @return static
4360
     *                <p>(Immutable)</p>
4361
     *
4362
     * @phpstan-param  array<int|TKey,T> $array
4363
     * @phpstan-return static<int|TKey,T>
4364
     * @psalm-mutation-free
4365
     */
4366 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...
4367
    {
4368 33
        if ($recursive === true) {
4369 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
4370 9
            $result = \array_replace_recursive($this->toArray(), $array);
4371
        } else {
4372 24
            $result = \array_replace($this->toArray(), $array);
4373
        }
4374
4375 33
        return static::create(
4376 33
            $result,
4377 33
            $this->iteratorClass,
4378 33
            false
4379
        );
4380
    }
4381
4382
    /**
4383
     * Merge the new $array into the current array.
4384
     *
4385
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
4386
     * - create new indexes
4387
     *
4388
     * EXAMPLE: <code>
4389
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4390
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4391
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 'foo' => 'bar2', 1 => 'three']
4392
     * // ---
4393
     * $array1 = [0 => 'one', 1 => 'foo'];
4394
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4395
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 1 => 'foo', 2 => 'foo', 3 => 'bar2']
4396
     * </code>
4397
     *
4398
     * @param array $array
4399
     * @param bool  $recursive
4400
     *
4401
     * @return static
4402
     *                <p>(Immutable)</p>
4403
     *
4404
     * @phpstan-param  array<TKey,T> $array
4405
     * @phpstan-return static<int,T>
4406
     * @psalm-mutation-free
4407
     */
4408 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...
4409
    {
4410 20
        if ($recursive === true) {
4411 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
4412 5
            $result = \array_merge_recursive($this->toArray(), $array);
4413
        } else {
4414 15
            $result = \array_merge($this->toArray(), $array);
4415
        }
4416
4417 20
        return static::create(
4418 20
            $result,
4419 20
            $this->iteratorClass,
4420 20
            false
4421
        );
4422
    }
4423
4424
    /**
4425
     * Merge the the current array into the $array.
4426
     *
4427
     * - use key,value from the new $array, also if the index is in the current array
4428
     *
4429
     * EXAMPLE: <code>
4430
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4431
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4432
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy['foo' => 'bar1', 3 => 'three', 1 => 'one']
4433
     * // ---
4434
     * $array1 = [0 => 'one', 1 => 'foo'];
4435
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4436
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy[0 => 'one', 1 => 'foo']
4437
     * </code>
4438
     *
4439
     * @param array $array
4440
     * @param bool  $recursive
4441
     *
4442
     * @return static
4443
     *                <p>(Immutable)</p>
4444
     *
4445
     * @phpstan-param  array<TKey,T> $array
4446
     * @phpstan-return static<TKey,T>
4447
     * @psalm-mutation-free
4448
     */
4449 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...
4450
    {
4451 17
        if ($recursive === true) {
4452 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
4453 4
            $result = \array_replace_recursive($array, $this->toArray());
4454
        } else {
4455 13
            $result = \array_replace($array, $this->toArray());
4456
        }
4457
4458 17
        return static::create(
4459 17
            $result,
4460 17
            $this->iteratorClass,
4461 17
            false
4462
        );
4463
    }
4464
4465
    /**
4466
     * Merge the current array into the new $array.
4467
     *
4468
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
4469
     * - create new indexes
4470
     *
4471
     * EXAMPLE: <code>
4472
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4473
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4474
     * a($array1)->mergePrependNewIndex($array2); // Arrayy['foo' => 'bar1', 0 => 'three', 1 => 'one']
4475
     * // ---
4476
     * $array1 = [0 => 'one', 1 => 'foo'];
4477
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4478
     * a($array1)->mergePrependNewIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2', 2 => 'one', 3 => 'foo']
4479
     * </code>
4480
     *
4481
     * @param array $array
4482
     * @param bool  $recursive
4483
     *
4484
     * @return static
4485
     *                <p>(Immutable)</p>
4486
     *
4487
     * @phpstan-param  array<TKey,T> $array
4488
     * @phpstan-return static<int,T>
4489
     * @psalm-mutation-free
4490
     */
4491 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...
4492
    {
4493 21
        if ($recursive === true) {
4494 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
4495 7
            $result = \array_merge_recursive($array, $this->toArray());
4496
        } else {
4497 14
            $result = \array_merge($array, $this->toArray());
4498
        }
4499
4500 21
        return static::create(
4501 21
            $result,
4502 21
            $this->iteratorClass,
4503 21
            false
4504
        );
4505
    }
4506
4507
    /**
4508
     * @return ArrayyMeta|mixed|static
4509
     */
4510 18
    public static function meta()
4511
    {
4512 18
        return (new ArrayyMeta())->getMetaObject(static::class);
4513
    }
4514
4515
    /**
4516
     * Get the min value from an array.
4517
     *
4518
     * EXAMPLE: <code>
4519
     * a([-9, -8, -7, 1.32])->min(); // -9
4520
     * </code>
4521
     *
4522
     * @return false|mixed
4523
     *                     <p>Will return false if there are no values.</p>
4524
     */
4525 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...
4526
    {
4527 10
        if ($this->count() === 0) {
4528 1
            return false;
4529
        }
4530
4531 9
        $min = false;
4532
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4533 9
        foreach ($this->getGeneratorByReference() as &$value) {
4534
            if (
4535 9
                $min === false
4536
                ||
4537 9
                $value < $min
4538
            ) {
4539 9
                $min = $value;
4540
            }
4541
        }
4542
4543 9
        return $min;
4544
    }
4545
4546
    /**
4547
     * Get the most used value from the array.
4548
     *
4549
     * @return mixed|null
4550
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4551
     *
4552
     * @phpstan-return T|null
4553
     * @psalm-mutation-free
4554
     */
4555 3
    public function mostUsedValue()
4556
    {
4557 3
        return $this->countValues()->arsortImmutable()->firstKey();
4558
    }
4559
4560
    /**
4561
     * Get the most used value from the array.
4562
     *
4563
     * @param int|null $number <p>How many values you will take?</p>
4564
     *
4565
     * @return static
4566
     *                <p>(Immutable)</p>
4567
     *
4568
     * @phpstan-return static<TKey,T>
4569
     * @psalm-mutation-free
4570
     */
4571 3
    public function mostUsedValues(int $number = null): self
4572
    {
4573 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4574
    }
4575
4576
    /**
4577
     * Move an array element to a new index.
4578
     *
4579
     * EXAMPLE: <code>
4580
     * $arr2 = new A(['A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e']);
4581
     * $newArr2 = $arr2->moveElement('D', 1); // Arrayy['A' => 'a', 'D' => 'd', 'B' => 'b', 'C' => 'c', 'E' => 'e']
4582
     * </code>
4583
     *
4584
     * @param int|string $from
4585
     * @param int        $to
4586
     *
4587
     * @return static
4588
     *                <p>(Immutable)</p>
4589
     *
4590
     * @phpstan-return static<TKey,T>
4591
     * @psalm-mutation-free
4592
     */
4593 1
    public function moveElement($from, $to): self
4594
    {
4595 1
        $array = $this->toArray();
4596
4597 1
        if ((int) $from === $from) {
4598 1
            $tmp = \array_splice($array, $from, 1);
4599 1
            \array_splice($array, (int) $to, 0, $tmp);
4600 1
            $output = $array;
4601 1
        } elseif ((string) $from === $from) {
4602 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4603 1
            $itemToMove = $array[$from];
4604 1
            if ($indexToMove !== false) {
4605 1
                \array_splice($array, $indexToMove, 1);
4606
            }
4607 1
            $i = 0;
4608 1
            $output = [];
4609 1
            foreach ($array as $key => $item) {
4610 1
                if ($i === $to) {
4611 1
                    $output[$from] = $itemToMove;
4612
                }
4613 1
                $output[$key] = $item;
4614 1
                ++$i;
4615
            }
4616
        } else {
4617
            $output = [];
4618
        }
4619
4620 1
        return static::create(
4621 1
            $output,
4622 1
            $this->iteratorClass,
4623 1
            false
4624
        );
4625
    }
4626
4627
    /**
4628
     * Move an array element to the first place.
4629
     *
4630
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4631
     *       loss the keys of an indexed array.
4632
     *
4633
     * @param int|string $key
4634
     *
4635
     * @return static
4636
     *                <p>(Immutable)</p>
4637
     *
4638
     * @phpstan-return static<TKey,T>
4639
     * @psalm-mutation-free
4640
     */
4641 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...
4642
    {
4643 1
        $array = $this->toArray();
4644
4645 1
        if ($this->offsetExists($key)) {
4646 1
            $tmpValue = $this->get($key);
4647 1
            unset($array[$key]);
4648 1
            $array = [$key => $tmpValue] + $array;
4649
        }
4650
4651 1
        return static::create(
4652 1
            $array,
4653 1
            $this->iteratorClass,
4654 1
            false
4655
        );
4656
    }
4657
4658
    /**
4659
     * Move an array element to the last place.
4660
     *
4661
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4662
     *       loss the keys of an indexed array.
4663
     *
4664
     * @param int|string $key
4665
     *
4666
     * @return static
4667
     *                <p>(Immutable)</p>
4668
     *
4669
     * @phpstan-return static<TKey,T>
4670
     * @psalm-mutation-free
4671
     */
4672 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...
4673
    {
4674 1
        $array = $this->toArray();
4675
4676 1
        if ($this->offsetExists($key)) {
4677 1
            $tmpValue = $this->get($key);
4678 1
            unset($array[$key]);
4679 1
            $array += [$key => $tmpValue];
4680
        }
4681
4682 1
        return static::create(
4683 1
            $array,
4684 1
            $this->iteratorClass,
4685 1
            false
4686
        );
4687
    }
4688
4689
    /**
4690
     * Moves the internal iterator position to the next element and returns this element.
4691
     *
4692
     * @return false|mixed
4693
     *                     <p>(Mutable) Will return false if there are no values.</p>
4694
     *
4695
     * @phpstan-return false|T
4696
     */
4697
    public function next()
4698
    {
4699
        if ($this->generator) {
4700
            $this->generator->next();
4701
4702
            return $this->generator->current() ?? false;
4703
        }
4704
4705
        return \next($this->array);
4706
    }
4707
4708
    /**
4709
     * Get the next nth keys and values from the array.
4710
     *
4711
     * @param int $step
4712
     * @param int $offset
4713
     *
4714
     * @return static
4715
     *                <p>(Immutable)</p>
4716
     *
4717
     * @phpstan-return static<TKey,T>
4718
     * @psalm-mutation-free
4719
     */
4720 1
    public function nth(int $step, int $offset = 0): self
4721
    {
4722
        $arrayFunction = function () use ($step, $offset): \Generator {
4723 1
            $position = 0;
4724 1
            foreach ($this->getGenerator() as $key => $value) {
4725 1
                if ($position++ % $step !== $offset) {
4726 1
                    continue;
4727
                }
4728
4729 1
                yield $key => $value;
4730
            }
4731 1
        };
4732
4733 1
        return static::create(
4734 1
            $arrayFunction,
4735 1
            $this->iteratorClass,
4736 1
            false
4737
        );
4738
    }
4739
4740
    /**
4741
     * Get a subset of the items from the given array.
4742
     *
4743
     * @param int[]|string[] $keys
4744
     *
4745
     * @return static
4746
     *                <p>(Immutable)</p>
4747
     *
4748
     * @phpstan-param array-key[] $keys
4749
     * @phpstan-return static<TKey,T>
4750
     * @psalm-mutation-free
4751
     */
4752 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...
4753
    {
4754 1
        $keys = \array_flip($keys);
4755
4756
        $generator = function () use ($keys): \Generator {
4757 1
            foreach ($this->getGenerator() as $key => $value) {
4758 1
                if (isset($keys[$key])) {
4759 1
                    yield $key => $value;
4760
                }
4761
            }
4762 1
        };
4763
4764 1
        return static::create(
4765 1
            $generator,
4766 1
            $this->iteratorClass,
4767 1
            false
4768
        );
4769
    }
4770
4771
    /**
4772
     * Pad array to the specified size with a given value.
4773
     *
4774
     * @param int   $size  <p>Size of the result array.</p>
4775
     * @param mixed $value <p>Empty value by default.</p>
4776
     *
4777
     * @return static
4778
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4779
     *
4780
     * @phpstan-return static<TKey,T>
4781
     * @psalm-mutation-free
4782
     */
4783 5
    public function pad(int $size, $value): self
4784
    {
4785 5
        return static::create(
4786 5
            \array_pad($this->toArray(), $size, $value),
4787 5
            $this->iteratorClass,
4788 5
            false
4789
        );
4790
    }
4791
4792
    /**
4793
     * Partitions this array in two array according to a predicate.
4794
     * Keys are preserved in the resulting array.
4795
     *
4796
     * @param \Closure $closure
4797
     *                          <p>The predicate on which to partition.</p>
4798
     *
4799
     * @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...
4800
     *                    <p>An array with two elements. The first element contains the array
4801
     *                    of elements where the predicate returned TRUE, the second element
4802
     *                    contains the array of elements where the predicate returned FALSE.</p>
4803
     *
4804
     * @phpstan-param \Closure(T=,TKey=):bool $closure
4805
     * @phpstan-return array<int, static<TKey,T>>
4806
     */
4807 1
    public function partition(\Closure $closure): array
4808
    {
4809
        // init
4810 1
        $matches = [];
4811 1
        $noMatches = [];
4812
4813 1
        foreach ($this->getGenerator() as $key => $value) {
4814 1
            if ($closure($value, $key)) {
4815 1
                $matches[$key] = $value;
4816
            } else {
4817 1
                $noMatches[$key] = $value;
4818
            }
4819
        }
4820
4821 1
        return [self::create($matches), self::create($noMatches)];
4822
    }
4823
4824
    /**
4825
     * Pop a specified value off the end of the current array.
4826
     *
4827
     * @return mixed|null
4828
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4829
     *
4830
     * @phpstan-return T|null
4831
     */
4832 5
    public function pop()
4833
    {
4834 5
        $this->generatorToArray();
4835
4836 5
        return \array_pop($this->array);
4837
    }
4838
4839
    /**
4840
     * Prepend a (key) + value to the current array.
4841
     *
4842
     * EXAMPLE: <code>
4843
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4844
     * </code>
4845
     *
4846
     * @param mixed $value
4847
     * @param mixed $key
4848
     *
4849
     * @return $this
4850
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4851
     *
4852
     * @phpstan-param T $value
4853
     * @phpstan-param TKey|null $key
4854
     * @phpstan-return static<TKey,T>
4855
     */
4856 11
    public function prepend($value, $key = null)
4857
    {
4858 11
        $this->generatorToArray();
4859
4860 11
        if ($this->properties !== []) {
4861 3
            $this->checkType($key, $value);
4862
        }
4863
4864 9
        if ($key === null) {
4865 8
            \array_unshift($this->array, $value);
4866
        } else {
4867 2
            $this->array = [$key => $value] + $this->array;
4868
        }
4869
4870 9
        return $this;
4871
    }
4872
4873
    /**
4874
     * Prepend a (key) + value to the current array.
4875
     *
4876
     * EXAMPLE: <code>
4877
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4878
     * </code>
4879
     *
4880
     * @param mixed $value
4881
     * @param mixed $key
4882
     *
4883
     * @return $this
4884
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4885
     *
4886
     * @phpstan-param T $value
4887
     * @phpstan-param TKey $key
4888
     * @phpstan-return static<TKey,T>
4889
     * @psalm-mutation-free
4890
     */
4891 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...
4892
    {
4893
        $generator = function () use ($key, $value): \Generator {
4894 1
            if ($this->properties !== []) {
4895
                $this->checkType($key, $value);
4896
            }
4897
4898 1
            if ($key !== null) {
4899
                yield $key => $value;
4900
            } else {
4901 1
                yield $value;
4902
            }
4903
4904
            /** @noinspection YieldFromCanBeUsedInspection - FP */
4905 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
4906 1
                yield $keyOld => $itemOld;
4907
            }
4908 1
        };
4909
4910 1
        return static::create(
4911 1
            $generator,
4912 1
            $this->iteratorClass,
4913 1
            false
4914
        );
4915
    }
4916
4917
    /**
4918
     * Add a suffix to each key.
4919
     *
4920
     * @param float|int|string $suffix
4921
     *
4922
     * @return static
4923
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4924
     *
4925
     * @phpstan-return static<TKey,T>
4926
     * @psalm-mutation-free
4927
     */
4928 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...
4929
    {
4930
        // init
4931 10
        $result = [];
4932
4933 10
        foreach ($this->getGenerator() as $key => $item) {
4934 9
            if ($item instanceof self) {
4935
                $result[$key] = $item->prependToEachKey($suffix);
4936 9
            } elseif (\is_array($item)) {
4937
                $result[$key] = self::create(
4938
                    $item,
4939
                    $this->iteratorClass,
4940
                    false
4941
                )->prependToEachKey($suffix)
4942
                    ->toArray();
4943
            } else {
4944 9
                $result[$key . $suffix] = $item;
4945
            }
4946
        }
4947
4948 10
        return self::create(
4949 10
            $result,
4950 10
            $this->iteratorClass,
4951 10
            false
4952
        );
4953
    }
4954
4955
    /**
4956
     * Add a suffix to each value.
4957
     *
4958
     * @param float|int|string $suffix
4959
     *
4960
     * @return static
4961
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4962
     *
4963
     * @phpstan-return static<TKey,T>
4964
     * @psalm-mutation-free
4965
     */
4966 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...
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->prependToEachValue($suffix);
4974 9
            } elseif (\is_array($item)) {
4975
                $result[$key] = self::create(
4976
                    $item,
4977
                    $this->iteratorClass,
4978
                    false
4979
                )->prependToEachValue($suffix)
4980
                    ->toArray();
4981 9
            } elseif (\is_object($item) === true) {
4982 1
                $result[$key] = $item;
4983
            } else {
4984 9
                $result[$key] = $item . $suffix;
4985
            }
4986
        }
4987
4988 10
        return self::create(
4989 10
            $result,
4990 10
            $this->iteratorClass,
4991 10
            false
4992
        );
4993
    }
4994
4995
    /**
4996
     * Return the value of a given key and
4997
     * delete the key.
4998
     *
4999
     * @param int|int[]|string|string[]|null $keyOrKeys
5000
     * @param mixed                          $fallback
5001
     *
5002
     * @return mixed
5003
     */
5004 6
    public function pull($keyOrKeys = null, $fallback = null)
5005
    {
5006 6
        if ($keyOrKeys === null) {
5007 1
            $array = $this->toArray();
5008 1
            $this->clear();
5009
5010 1
            return $array;
5011
        }
5012
5013 5
        if (\is_array($keyOrKeys)) {
5014 1
            $valueOrValues = [];
5015 1
            foreach ($keyOrKeys as $key) {
5016 1
                $valueOrValues[] = $this->get($key, $fallback);
5017 1
                $this->offsetUnset($key);
5018
            }
5019
        } else {
5020 5
            $valueOrValues = $this->get($keyOrKeys, $fallback);
5021 5
            $this->offsetUnset($keyOrKeys);
5022
        }
5023
5024 5
        return $valueOrValues;
5025
    }
5026
5027
    /**
5028
     * Push one or more values onto the end of array at once.
5029
     *
5030
     * @param mixed ...$args
5031
     *
5032
     * @return $this
5033
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
5034
     *
5035
     * @noinspection ReturnTypeCanBeDeclaredInspection
5036
     *
5037
     * @phpstan-param  array<TKey,T> ...$args
5038
     * @phpstan-return static<TKey,T>
5039
     */
5040 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...
5041
    {
5042 9
        $this->generatorToArray();
5043
5044
        if (
5045 9
            $this->checkPropertyTypes
5046
            &&
5047 9
            $this->properties !== []
5048
        ) {
5049 3
            foreach ($args as $key => $value) {
5050 3
                $this->checkType($key, $value);
5051
            }
5052
        }
5053
5054 8
        \array_push($this->array, ...$args);
5055
5056 8
        return $this;
5057
    }
5058
5059
    /**
5060
     * Get a random value from the current array.
5061
     *
5062
     * EXAMPLE: <code>
5063
     * a([1, 2, 3, 4])->randomImmutable(2); // e.g.: Arrayy[1, 4]
5064
     * </code>
5065
     *
5066
     * @param int|null $number <p>How many values you will take?</p>
5067
     *
5068
     * @return static
5069
     *                <p>(Immutable)</p>
5070
     *
5071
     * @phpstan-return static<int|array-key,T>
5072
     */
5073 19
    public function randomImmutable(int $number = null): self
5074
    {
5075 19
        $this->generatorToArray();
5076
5077 19
        if ($this->count() === 0) {
5078 1
            return static::create(
5079 1
                [],
5080 1
                $this->iteratorClass,
5081 1
                false
5082
            );
5083
        }
5084
5085 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...
5086
            /** @noinspection NonSecureArrayRandUsageInspection */
5087 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
5088
5089 13
            return static::create(
5090 13
                $arrayRandValue,
5091 13
                $this->iteratorClass,
5092 13
                false
5093
            );
5094
        }
5095
5096 6
        $arrayTmp = $this->array;
5097
        /** @noinspection NonSecureShuffleUsageInspection */
5098 6
        \shuffle($arrayTmp);
5099
5100 6
        return static::create(
5101 6
            $arrayTmp,
5102 6
            $this->iteratorClass,
5103 6
            false
5104 6
        )->firstsImmutable($number);
5105
    }
5106
5107
    /**
5108
     * Pick a random key/index from the keys of this array.
5109
     *
5110
     * EXAMPLE: <code>
5111
     * $arrayy = A::create([1 => 'one', 2 => 'two']);
5112
     * $arrayy->randomKey(); // e.g. 2
5113
     * </code>
5114
     *
5115
     * @throws \RangeException If array is empty
5116
     *
5117
     * @return mixed
5118
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
5119
     */
5120 4
    public function randomKey()
5121
    {
5122 4
        $result = $this->randomKeys(1);
5123
5124 4
        if (!isset($result[0])) {
5125
            $result[0] = null;
5126
        }
5127
5128 4
        return $result[0];
5129
    }
5130
5131
    /**
5132
     * Pick a given number of random keys/indexes out of this array.
5133
     *
5134
     * EXAMPLE: <code>
5135
     * a([1 => 'one', 2 => 'two'])->randomKeys(); // e.g. Arrayy[1, 2]
5136
     * </code>
5137
     *
5138
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
5139
     *
5140
     * @throws \RangeException If array is empty
5141
     *
5142
     * @return static
5143
     *                <p>(Immutable)</p>
5144
     *
5145
     * @phpstan-return static<TKey,T>
5146
     */
5147 13
    public function randomKeys(int $number): self
5148
    {
5149 13
        $this->generatorToArray();
5150
5151 13
        $count = $this->count();
5152
5153
        if (
5154 13
            $number === 0
5155
            ||
5156 13
            $number > $count
5157
        ) {
5158 2
            throw new \RangeException(
5159 2
                \sprintf(
5160 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
5161 2
                    $number,
5162 2
                    $count
5163
                )
5164
            );
5165
        }
5166
5167 11
        $result = (array) \array_rand($this->array, $number);
5168
5169 11
        return static::create(
5170 11
            $result,
5171 11
            $this->iteratorClass,
5172 11
            false
5173
        );
5174
    }
5175
5176
    /**
5177
     * Get a random value from the current array.
5178
     *
5179
     * EXAMPLE: <code>
5180
     * a([1, 2, 3, 4])->randomMutable(2); // e.g.: Arrayy[1, 4]
5181
     * </code>
5182
     *
5183
     * @param int|null $number <p>How many values you will take?</p>
5184
     *
5185
     * @return $this
5186
     *               <p>(Mutable) Return this Arrayy object.</p>
5187
     *
5188
     * @phpstan-return static<TKey,T>
5189
     */
5190 17
    public function randomMutable(int $number = null): self
5191
    {
5192 17
        $this->generatorToArray();
5193
5194 17
        if ($this->count() === 0) {
5195
            return static::create(
5196
                [],
5197
                $this->iteratorClass,
5198
                false
5199
            );
5200
        }
5201
5202 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...
5203
            /** @noinspection NonSecureArrayRandUsageInspection */
5204 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
5205 7
            $this->array = $arrayRandValue;
5206
5207 7
            return $this;
5208
        }
5209
5210
        /** @noinspection NonSecureShuffleUsageInspection */
5211 11
        \shuffle($this->array);
5212
5213 11
        return $this->firstsMutable($number);
5214
    }
5215
5216
    /**
5217
     * Pick a random value from the values of this array.
5218
     *
5219
     * EXAMPLE: <code>
5220
     * a([1 => 'one', 2 => 'two'])->randomValue(); // e.g. 'one'
5221
     * </code>
5222
     *
5223
     * @return mixed
5224
     *               <p>Get a random value or null if there wasn't a value.</p>
5225
     */
5226 4
    public function randomValue()
5227
    {
5228 4
        $result = $this->randomImmutable();
5229
5230 4
        if (!isset($result[0])) {
5231
            $result[0] = null;
5232
        }
5233
5234 4
        return $result[0];
5235
    }
5236
5237
    /**
5238
     * Pick a given number of random values out of this array.
5239
     *
5240
     * EXAMPLE: <code>
5241
     * a([1 => 'one', 2 => 'two'])->randomValues(); // e.g. Arrayy['one', 'two']
5242
     * </code>
5243
     *
5244
     * @param int $number
5245
     *
5246
     * @return static
5247
     *                <p>(Mutable)</p>
5248
     *
5249
     * @phpstan-return static<TKey,T>
5250
     */
5251 7
    public function randomValues(int $number): self
5252
    {
5253 7
        return $this->randomMutable($number);
5254
    }
5255
5256
    /**
5257
     * Get a random value from an array, with the ability to skew the results.
5258
     *
5259
     * EXAMPLE: <code>
5260
     * a([0 => 3, 1 => 4])->randomWeighted([1 => 4]); // e.g.: Arrayy[4] (has a 66% chance of returning 4)
5261
     * </code>
5262
     *
5263
     * @param array    $array
5264
     * @param int|null $number <p>How many values you will take?</p>
5265
     *
5266
     * @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...
5267
     *                           <p>(Immutable)</p>
5268
     *
5269
     * @phpstan-param  array<T,int> $array
5270
     * @phpstan-return static<(int|string),T>
5271
     */
5272 9
    public function randomWeighted(array $array, int $number = null): self
5273
    {
5274
        // init
5275 9
        $options = [];
5276
5277 9
        foreach ($array as $option => $weight) {
5278 9
            if ($this->searchIndex($option) !== false) {
5279 9
                for ($i = 0; $i < $weight; ++$i) {
5280 1
                    $options[] = $option;
5281
                }
5282
            }
5283
        }
5284
5285 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
5286
    }
5287
5288
    /**
5289
     * Reduce the current array via callable e.g. anonymous-function and return the end result.
5290
     *
5291
     * EXAMPLE: <code>
5292
     * a([1, 2, 3, 4])->reduce(
5293
     *     function ($carry, $item) {
5294
     *         return $carry * $item;
5295
     *     },
5296
     *     1
5297
     * ); // Arrayy[24]
5298
     * </code>
5299
     *
5300
     * @param callable $callable
5301
     * @param mixed    $initial
5302
     *
5303
     * @return static
5304
     *                <p>(Immutable)</p>
5305
     *
5306
     * @template T2
5307
     *              <p>The output value type.</p>
5308
     *
5309
     * @phpstan-param callable(T2, T, TKey): T2 $callable
5310
     * @phpstan-param T2                  $initial
5311
     *
5312
     * @phpstan-return static<TKey,T2>
5313
     * @psalm-mutation-free
5314
     */
5315 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...
5316
    {
5317 18
        foreach ($this->getGenerator() as $key => $value) {
5318 17
            $initial = $callable($initial, $value, $key);
5319
        }
5320
5321 18
        return static::create(
5322 18
            $initial,
5323 18
            $this->iteratorClass,
5324 18
            false
5325
        );
5326
    }
5327
5328
    /**
5329
     * @param bool $unique
5330
     *
5331
     * @return static
5332
     *                <p>(Immutable)</p>
5333
     *
5334
     * @phpstan-return static<int,mixed>
5335
     * @psalm-mutation-free
5336
     */
5337 14
    public function reduce_dimension(bool $unique = true): self
5338
    {
5339
        // init
5340 14
        $result = [];
5341
5342 14
        foreach ($this->getGenerator() as $val) {
5343 12
            if (\is_array($val)) {
5344 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
5345
            } else {
5346 12
                $result[] = [$val];
5347
            }
5348
        }
5349
5350 14
        $result = $result === [] ? [] : \array_merge(...$result);
5351
5352 14
        $resultArrayy = new static($result);
5353
5354
        /**
5355
         * @psalm-suppress ImpureMethodCall - object is already re-created
5356
         * @psalm-suppress InvalidReturnStatement - why?
5357
         */
5358 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
5359
    }
5360
5361
    /**
5362
     * Create a numerically re-indexed Arrayy object.
5363
     *
5364
     * EXAMPLE: <code>
5365
     * a([2 => 1, 3 => 2])->reindex(); // Arrayy[0 => 1, 1 => 2]
5366
     * </code>
5367
     *
5368
     * @return $this
5369
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
5370
     *
5371
     * @phpstan-return static<TKey,T>
5372
     */
5373 9
    public function reindex(): self
5374
    {
5375 9
        $this->generatorToArray(false);
5376
5377 9
        $this->array = \array_values($this->array);
5378
5379 9
        return $this;
5380
    }
5381
5382
    /**
5383
     * Return all items that fail the truth test.
5384
     *
5385
     * EXAMPLE: <code>
5386
     * $closure = function ($value) {
5387
     *     return $value % 2 !== 0;
5388
     * }
5389
     * a([1, 2, 3, 4])->reject($closure); // Arrayy[1 => 2, 3 => 4]
5390
     * </code>
5391
     *
5392
     * @param \Closure $closure
5393
     *
5394
     * @return static
5395
     *                <p>(Immutable)</p>
5396
     *
5397
     * @phpstan-param \Closure(T=,TKey=):bool  $closure
5398
     * @phpstan-return static<TKey,T>
5399
     * @psalm-mutation-free
5400
     */
5401 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...
5402
    {
5403
        // init
5404 1
        $filtered = [];
5405
5406 1
        foreach ($this->getGenerator() as $key => $value) {
5407 1
            if (!$closure($value, $key)) {
5408 1
                $filtered[$key] = $value;
5409
            }
5410
        }
5411
5412 1
        return static::create(
5413 1
            $filtered,
5414 1
            $this->iteratorClass,
5415 1
            false
5416
        );
5417
    }
5418
5419
    /**
5420
     * Remove a value from the current array (optional using dot-notation).
5421
     *
5422
     * EXAMPLE: <code>
5423
     * a([1 => 'bar', 'foo' => 'foo'])->remove(1); // Arrayy['foo' => 'foo']
5424
     * </code>
5425
     *
5426
     * @param mixed $key
5427
     *
5428
     * @return static
5429
     *                <p>(Mutable)</p>
5430
     *
5431
     * @phpstan-param  TKey $key
5432
     * @phpstan-return static<TKey,T>
5433
     */
5434 22
    public function remove($key)
5435
    {
5436
        // recursive call
5437 22
        if (\is_array($key)) {
5438 1
            foreach ($key as $k) {
5439 1
                $this->internalRemove($k);
5440
            }
5441
5442 1
            return static::create(
5443 1
                $this->toArray(),
5444 1
                $this->iteratorClass,
5445 1
                false
5446
            );
5447
        }
5448
5449 21
        $this->internalRemove($key);
5450
5451 21
        return static::create(
5452 21
            $this->toArray(),
5453 21
            $this->iteratorClass,
5454 21
            false
5455
        );
5456
    }
5457
5458
    /**
5459
     * alias: for "Arrayy->removeValue()"
5460
     *
5461
     * @param mixed $element
5462
     *
5463
     * @return static
5464
     *                <p>(Immutable)</p>
5465
     *
5466
     * @phpstan-param  T $element
5467
     * @phpstan-return static<TKey,T>
5468
     * @psalm-mutation-free
5469
     */
5470 8
    public function removeElement($element)
5471
    {
5472 8
        return $this->removeValue($element);
5473
    }
5474
5475
    /**
5476
     * Remove the first value from the current array.
5477
     *
5478
     * EXAMPLE: <code>
5479
     * a([1 => 'bar', 'foo' => 'foo'])->removeFirst(); // Arrayy['foo' => 'foo']
5480
     * </code>
5481
     *
5482
     * @return static
5483
     *                <p>(Immutable)</p>
5484
     *
5485
     * @phpstan-return static<TKey,T>
5486
     * @psalm-mutation-free
5487
     */
5488 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...
5489
    {
5490 7
        $tmpArray = $this->toArray();
5491
5492 7
        \array_shift($tmpArray);
5493
5494 7
        return static::create(
5495 7
            $tmpArray,
5496 7
            $this->iteratorClass,
5497 7
            false
5498
        );
5499
    }
5500
5501
    /**
5502
     * Remove the last value from the current array.
5503
     *
5504
     * EXAMPLE: <code>
5505
     * a([1 => 'bar', 'foo' => 'foo'])->removeLast(); // Arrayy[1 => 'bar']
5506
     * </code>
5507
     *
5508
     * @return static
5509
     *                <p>(Immutable)</p>
5510
     *
5511
     * @phpstan-return static<TKey,T>
5512
     * @psalm-mutation-free
5513
     */
5514 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...
5515
    {
5516 7
        $tmpArray = $this->toArray();
5517
5518 7
        \array_pop($tmpArray);
5519
5520 7
        return static::create(
5521 7
            $tmpArray,
5522 7
            $this->iteratorClass,
5523 7
            false
5524
        );
5525
    }
5526
5527
    /**
5528
     * Removes a particular value from an array (numeric or associative).
5529
     *
5530
     * EXAMPLE: <code>
5531
     * a([1 => 'bar', 'foo' => 'foo'])->removeValue('foo'); // Arrayy[1 => 'bar']
5532
     * </code>
5533
     *
5534
     * @param mixed $value
5535
     *
5536
     * @return static
5537
     *                <p>(Immutable)</p>
5538
     *
5539
     * @phpstan-param  T $value
5540
     * @phpstan-return static<TKey,T>
5541
     * @psalm-mutation-free
5542
     */
5543 8
    public function removeValue($value): self
5544
    {
5545 8
        $this->generatorToArray();
5546
5547
        // init
5548 8
        $isSequentialArray = $this->isSequential();
5549
5550 8
        foreach ($this->array as $key => $item) {
5551 7
            if ($item === $value) {
5552
                /** @phpstan-ignore-next-line | "Possibly invalid array key type int|string|TKey.", is this a bug in phpstan? */
5553 7
                unset($this->array[$key]);
5554
            }
5555
        }
5556
5557 8
        if ($isSequentialArray) {
5558 6
            $this->array = \array_values($this->array);
5559
        }
5560
5561 8
        return static::create(
5562 8
            $this->array,
5563 8
            $this->iteratorClass,
5564 8
            false
5565
        );
5566
    }
5567
5568
    /**
5569
     * Generate array of repeated arrays.
5570
     *
5571
     * @param int $times <p>How many times has to be repeated.</p>
5572
     *
5573
     * @return static
5574
     *                <p>(Immutable)</p>
5575
     *
5576
     * @phpstan-return static<TKey,T>
5577
     * @psalm-mutation-free
5578
     */
5579 1
    public function repeat($times): self
5580
    {
5581 1
        if ($times === 0) {
5582 1
            return static::create([], $this->iteratorClass);
5583
        }
5584
5585 1
        return static::create(
5586 1
            \array_fill(0, (int) $times, $this->toArray()),
5587 1
            $this->iteratorClass,
5588 1
            false
5589
        );
5590
    }
5591
5592
    /**
5593
     * Replace a key with a new key/value pair.
5594
     *
5595
     * EXAMPLE: <code>
5596
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
5597
     * $arrayy->replace(2, 'notfoo', 'notbar'); // Arrayy[1 => 'foo', 'notfoo' => 'notbar', 3 => 'bar']
5598
     * </code>
5599
     *
5600
     * @param mixed $oldKey
5601
     * @param mixed $newKey
5602
     * @param mixed $newValue
5603
     *
5604
     * @return static
5605
     *                <p>(Immutable)</p>
5606
     *
5607
     * @phpstan-return static<TKey,T>
5608
     * @psalm-mutation-free
5609
     */
5610 5
    public function replace($oldKey, $newKey, $newValue): self
5611
    {
5612 5
        $that = clone $this;
5613
5614
        /**
5615
         * @psalm-suppress ImpureMethodCall - object is already cloned
5616
         */
5617 5
        return $that->remove($oldKey)
5618 5
            ->set($newKey, $newValue);
5619
    }
5620
5621
    /**
5622
     * Create an array using the current array as values and the other array as keys.
5623
     *
5624
     * EXAMPLE: <code>
5625
     * $firstArray = [
5626
     *     1 => 'one',
5627
     *     2 => 'two',
5628
     *     3 => 'three',
5629
     * ];
5630
     * $secondArray = [
5631
     *     'one' => 1,
5632
     *     1     => 'one',
5633
     *     2     => 2,
5634
     * ];
5635
     * $arrayy = a($firstArray);
5636
     * $arrayy->replaceAllKeys($secondArray); // Arrayy[1 => "one", 'one' => "two", 2 => "three"]
5637
     * </code>
5638
     *
5639
     * @param int[]|string[] $keys <p>An array of keys.</p>
5640
     *
5641
     * @return static
5642
     *                <p>(Immutable) Arrayy object with keys from the other array, empty Arrayy object if the number of elements
5643
     *                for each array isn't equal or if the arrays are empty.
5644
     *                </p>
5645
     *
5646
     * @phpstan-param  array<array-key,TKey> $keys
5647
     * @phpstan-return static<TKey,T>
5648
     * @psalm-mutation-free
5649
     */
5650 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...
5651
    {
5652 2
        $data = \array_combine($keys, $this->toArray());
5653 2
        if ($data === false) {
5654
            $data = [];
5655
        }
5656
5657 2
        return static::create(
5658 2
            $data,
5659 2
            $this->iteratorClass,
5660 2
            false
5661
        );
5662
    }
5663
5664
    /**
5665
     * Create an array using the current array as keys and the other array as values.
5666
     *
5667
     * EXAMPLE: <code>
5668
     * $firstArray = [
5669
     *     1 => 'one',
5670
     *     2 => 'two',
5671
     *     3 => 'three',
5672
     * ];
5673
     * $secondArray = [
5674
     *     'one' => 1,
5675
     *     1     => 'one',
5676
     *     2     => 2,
5677
     * ];
5678
     * $arrayy = a($firstArray);
5679
     * $arrayy->replaceAllValues($secondArray); // Arrayy['one' => 1, 'two' => 'one', 'three' => 2]
5680
     * </code>
5681
     *
5682
     * @param array $array <p>An array of values.</p>
5683
     *
5684
     * @return static
5685
     *                <p>(Immutable) Arrayy object with values from the other array, empty Arrayy object if the number of elements
5686
     *                for each array isn't equal or if the arrays are empty.
5687
     *                </p>
5688
     *
5689
     * @phpstan-param  array<array-key,T> $array
5690
     * @phpstan-return static<TKey,T>
5691
     * @psalm-mutation-free
5692
     */
5693 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...
5694
    {
5695 2
        $data = \array_combine($this->toArray(), $array);
5696 2
        if ($data === false) {
5697
            $data = [];
5698
        }
5699
5700 2
        return static::create(
5701 2
            $data,
5702 2
            $this->iteratorClass,
5703 2
            false
5704
        );
5705
    }
5706
5707
    /**
5708
     * Replace the keys in an array with another set.
5709
     *
5710
     * EXAMPLE: <code>
5711
     * a([1 => 'bar', 'foo' => 'foo'])->replaceKeys([1 => 2, 'foo' => 'replaced']); // Arrayy[2 => 'bar', 'replaced' => 'foo']
5712
     * </code>
5713
     *
5714
     * @param array $keys <p>An array of keys matching the array's size.</p>
5715
     *
5716
     * @return static
5717
     *                <p>(Immutable)</p>
5718
     *
5719
     * @phpstan-param  array<array-key,TKey> $keys
5720
     * @phpstan-return static<TKey,T>
5721
     * @psalm-mutation-free
5722
     */
5723 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...
5724
    {
5725 1
        $values = \array_values($this->toArray());
5726 1
        $result = \array_combine($keys, $values);
5727 1
        if ($result === false) {
5728
            $result = [];
5729
        }
5730
5731 1
        return static::create(
5732 1
            $result,
5733 1
            $this->iteratorClass,
5734 1
            false
5735
        );
5736
    }
5737
5738
    /**
5739
     * Replace the first matched value in an array.
5740
     *
5741
     * EXAMPLE: <code>
5742
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5743
     * a($testArray)->replaceOneValue('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'foobar']
5744
     * </code>
5745
     *
5746
     * @param mixed $search      <p>The value to replace.</p>
5747
     * @param mixed $replacement <p>The value to replace.</p>
5748
     *
5749
     * @return static
5750
     *                <p>(Immutable)</p>
5751
     *
5752
     * @phpstan-return static<TKey,T>
5753
     * @psalm-mutation-free
5754
     */
5755 3
    public function replaceOneValue($search, $replacement = ''): self
5756
    {
5757 3
        $array = $this->toArray();
5758 3
        $key = \array_search($search, $array, true);
5759
5760 3
        if ($key !== false) {
5761 3
            $array[$key] = $replacement;
5762
        }
5763
5764 3
        return static::create(
5765 3
            $array,
5766 3
            $this->iteratorClass,
5767 3
            false
5768
        );
5769
    }
5770
5771
    /**
5772
     * Replace values in the current array.
5773
     *
5774
     * EXAMPLE: <code>
5775
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5776
     * a($testArray)->replaceValues('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'replacedbar']
5777
     * </code>
5778
     *
5779
     * @param string $search      <p>The value to replace.</p>
5780
     * @param string $replacement <p>What to replace it with.</p>
5781
     *
5782
     * @return static
5783
     *                <p>(Immutable)</p>
5784
     *
5785
     * @phpstan-return static<TKey,T>
5786
     * @psalm-mutation-free
5787
     */
5788 1
    public function replaceValues($search, $replacement = ''): self
5789
    {
5790
        $function = static function ($value) use ($search, $replacement) {
5791 1
            return \str_replace($search, $replacement, $value);
5792 1
        };
5793
5794
        /** @phpstan-ignore-next-line | ignore Closure with one or two parameters, is this a bug in phpstan? */
5795 1
        return $this->each($function);
5796
    }
5797
5798
    /**
5799
     * Get the last elements from index $from until the end of this array.
5800
     *
5801
     * EXAMPLE: <code>
5802
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->rest(2); // Arrayy[0 => 'lall']
5803
     * </code>
5804
     *
5805
     * @param int $from
5806
     *
5807
     * @return static
5808
     *                <p>(Immutable)</p>
5809
     *
5810
     * @phpstan-return static<TKey,T>
5811
     * @psalm-mutation-free
5812
     */
5813 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...
5814
    {
5815 15
        $tmpArray = $this->toArray();
5816
5817 15
        return static::create(
5818 15
            \array_splice($tmpArray, $from),
5819 15
            $this->iteratorClass,
5820 15
            false
5821
        );
5822
    }
5823
5824
    /**
5825
     * Return the array in the reverse order.
5826
     *
5827
     * EXAMPLE: <code>
5828
     * a([1, 2, 3])->reverse(); // self[3, 2, 1]
5829
     * </code>
5830
     *
5831
     * @return $this
5832
     *               <p>(Mutable) Return this Arrayy object.</p>
5833
     *
5834
     * @phpstan-return static<TKey,T>
5835
     */
5836 9
    public function reverse(): self
5837
    {
5838 9
        $this->generatorToArray();
5839
5840 9
        $this->array = \array_reverse($this->array);
5841
5842 9
        return $this;
5843
    }
5844
5845
    /**
5846
     * Sort an array in reverse order.
5847
     *
5848
     * @param int $sort_flags [optional] <p>
5849
     *                        You may modify the behavior of the sort using the optional
5850
     *                        parameter sort_flags, for details
5851
     *                        see sort.
5852
     *                        </p>
5853
     *
5854
     * @return $this
5855
     *               <p>(Mutable) Return this Arrayy object.</p>
5856
     *
5857
     * @phpstan-return static<TKey,T>
5858
     */
5859 4
    public function rsort(int $sort_flags = 0): self
5860
    {
5861 4
        $this->generatorToArray();
5862
5863 4
        \rsort($this->array, $sort_flags);
5864
5865 4
        return $this;
5866
    }
5867
5868
    /**
5869
     * Sort an array in reverse order.
5870
     *
5871
     * @param int $sort_flags [optional] <p>
5872
     *                        You may modify the behavior of the sort using the optional
5873
     *                        parameter sort_flags, for details
5874
     *                        see sort.
5875
     *                        </p>
5876
     *
5877
     * @return $this
5878
     *               <p>(Immutable) Return this Arrayy object.</p>
5879
     *
5880
     * @phpstan-return static<TKey,T>
5881
     * @psalm-mutation-free
5882
     */
5883 4
    public function rsortImmutable(int $sort_flags = 0): self
5884
    {
5885 4
        $that = clone $this;
5886
5887
        /**
5888
         * @psalm-suppress ImpureMethodCall - object is already cloned
5889
         */
5890 4
        $that->rsort($sort_flags);
5891
5892 4
        return $that;
5893
    }
5894
5895
    /**
5896
     * Search for the first index of the current array via $value.
5897
     *
5898
     * EXAMPLE: <code>
5899
     * a(['fòô' => 'bàř', 'lall' => 'bàř'])->searchIndex('bàř'); // Arrayy[0 => 'fòô']
5900
     * </code>
5901
     *
5902
     * @param mixed $value
5903
     *
5904
     * @return false|float|int|string
5905
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5906
     * @psalm-mutation-free
5907
     */
5908 21
    public function searchIndex($value)
5909
    {
5910 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5911 20
            if ($value === $valueFromArray) {
5912 20
                return $keyFromArray;
5913
            }
5914
        }
5915
5916 11
        return false;
5917
    }
5918
5919
    /**
5920
     * Search for the value of the current array via $index.
5921
     *
5922
     * EXAMPLE: <code>
5923
     * a(['fòô' => 'bàř'])->searchValue('fòô'); // Arrayy[0 => 'bàř']
5924
     * </code>
5925
     *
5926
     * @param mixed $index
5927
     *
5928
     * @return static
5929
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5930
     *
5931
     * @phpstan-return static<TKey,T>
5932
     * @psalm-mutation-free
5933
     */
5934 9
    public function searchValue($index): self
5935
    {
5936 9
        $this->generatorToArray();
5937
5938
        // init
5939 9
        $return = [];
5940
5941 9
        if ($this->array === []) {
5942
            return static::create(
5943
                [],
5944
                $this->iteratorClass,
5945
                false
5946
            );
5947
        }
5948
5949
        // php cast "bool"-index into "int"-index
5950 9
        if ((bool) $index === $index) {
5951 1
            $index = (int) $index;
5952
        }
5953
5954 9
        if ($this->offsetExists($index)) {
5955 7
            $return = [$this->array[$index]];
5956
        }
5957
5958 9
        return static::create(
5959 9
            $return,
5960 9
            $this->iteratorClass,
5961 9
            false
5962
        );
5963
    }
5964
5965
    /**
5966
     * Set a value for the current array (optional using dot-notation).
5967
     *
5968
     * EXAMPLE: <code>
5969
     * $arrayy = a(['Lars' => ['lastname' => 'Moelleken']]);
5970
     * $arrayy->set('Lars.lastname', 'Müller'); // Arrayy['Lars', ['lastname' => 'Müller']]]
5971
     * </code>
5972
     *
5973
     * @param string $key   <p>The key to set.</p>
5974
     * @param mixed  $value <p>Its value.</p>
5975
     *
5976
     * @return $this
5977
     *               <p>(Mutable) Return this Arrayy object.</p>
5978
     *
5979
     * @phpstan-param  TKey $key
5980
     * @phpstan-param  T $value
5981
     * @phpstan-return static<TKey,T>
5982
     */
5983 28
    public function set($key, $value): self
5984
    {
5985 28
        $this->internalSet($key, $value);
5986
5987 27
        return $this;
5988
    }
5989
5990
    /**
5991
     * Get a value from a array and set it if it was not.
5992
     *
5993
     * WARNING: this method only set the value, if the $key is not already set
5994
     *
5995
     * EXAMPLE: <code>
5996
     * $arrayy = a([1 => 1, 2 => 2, 3 => 3]);
5997
     * $arrayy->setAndGet(1, 4); // 1
5998
     * $arrayy->setAndGet(0, 4); // 4
5999
     * </code>
6000
     *
6001
     * @param mixed $key      <p>The key</p>
6002
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
6003
     *
6004
     * @return mixed
6005
     *               <p>(Mutable)</p>
6006
     */
6007 11
    public function setAndGet($key, $fallback = null)
6008
    {
6009 11
        $this->generatorToArray();
6010
6011
        // If the key doesn't exist, set it.
6012 11
        if (!$this->has($key)) {
6013 4
            $this->array = $this->set($key, $fallback)->toArray();
6014
        }
6015
6016 11
        return $this->get($key);
6017
    }
6018
6019
    /**
6020
     * Shifts a specified value off the beginning of array.
6021
     *
6022
     * @return mixed
6023
     *               <p>(Mutable) A shifted element from the current array.</p>
6024
     */
6025 5
    public function shift()
6026
    {
6027 5
        $this->generatorToArray();
6028
6029 5
        return \array_shift($this->array);
6030
    }
6031
6032
    /**
6033
     * Shuffle the current array.
6034
     *
6035
     * EXAMPLE: <code>
6036
     * a([1 => 'bar', 'foo' => 'foo'])->shuffle(); // e.g.: Arrayy[['foo' => 'foo', 1 => 'bar']]
6037
     * </code>
6038
     *
6039
     * @param bool       $secure <p>using a CSPRNG | @see https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
6040
     * @param array|null $array  [optional]
6041
     *
6042
     * @return static
6043
     *                <p>(Immutable)</p>
6044
     *
6045
     * @phpstan-param  array<TKey,T> $array
6046
     * @phpstan-return static<TKey,T>
6047
     *
6048
     * @noinspection BadExceptionsProcessingInspection
6049
     * @noinspection NonSecureShuffleUsageInspection
6050
     */
6051 2
    public function shuffle(bool $secure = false, array $array = null): self
6052
    {
6053 2
        if ($array === null) {
6054 2
            $array = $this->toArray(false);
6055
        }
6056
6057 2
        if ($secure !== true) {
6058 2
            \shuffle($array);
6059
        } else {
6060 1
            $size = \count($array, \COUNT_NORMAL);
6061 1
            $keys = \array_keys($array);
6062 1
            for ($i = $size - 1; $i > 0; --$i) {
6063
                try {
6064 1
                    $r = \random_int(0, $i);
6065
                } catch (\Exception $e) {
6066
                    $r = \mt_rand(0, $i);
6067
                }
6068 1
                if ($r !== $i) {
6069 1
                    $temp = $array[$keys[$r]];
6070 1
                    $array[$keys[$r]] = $array[$keys[$i]];
6071 1
                    $array[$keys[$i]] = $temp;
6072
                }
6073
            }
6074
        }
6075
6076 2
        foreach ($array as $key => $value) {
6077
            // check if recursive is needed
6078 2
            if (\is_array($value)) {
6079
                /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */
6080
                /** @phpstan-var array<TKey,T> $value */
6081
                $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...
6082
6083 2
                $array[$key] = $this->shuffle($secure, $value);
6084
            }
6085
        }
6086
6087 2
        return static::create(
6088 2
            $array,
6089 2
            $this->iteratorClass,
6090 2
            false
6091
        );
6092
    }
6093
6094
    /**
6095
     * Count the values from the current array.
6096
     *
6097
     * alias: for "Arrayy->count()"
6098
     *
6099
     * @param int $mode
6100
     *
6101
     * @return int
6102
     */
6103 20
    public function size(int $mode = \COUNT_NORMAL): int
6104
    {
6105 20
        return $this->count($mode);
6106
    }
6107
6108
    /**
6109
     * Checks whether array has exactly $size items.
6110
     *
6111
     * @param int $size
6112
     *
6113
     * @return bool
6114
     */
6115 1
    public function sizeIs(int $size): bool
6116
    {
6117
        // init
6118 1
        $itemsTempCount = 0;
6119
6120
        /** @noinspection PhpUnusedLocalVariableInspection */
6121
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
6122 1
        foreach ($this->getGeneratorByReference() as &$value) {
6123 1
            ++$itemsTempCount;
6124 1
            if ($itemsTempCount > $size) {
6125 1
                return false;
6126
            }
6127
        }
6128
6129 1
        return $itemsTempCount === $size;
6130
    }
6131
6132
    /**
6133
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
6134
     * smaller than $fromSize.
6135
     *
6136
     * @param int $fromSize
6137
     * @param int $toSize
6138
     *
6139
     * @return bool
6140
     */
6141 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
6142
    {
6143 1
        if ($fromSize > $toSize) {
6144 1
            $tmp = $toSize;
6145 1
            $toSize = $fromSize;
6146 1
            $fromSize = $tmp;
6147
        }
6148
6149
        // init
6150 1
        $itemsTempCount = 0;
6151
6152 1
        foreach ($this->getGenerator() as $key => $value) {
6153 1
            ++$itemsTempCount;
6154 1
            if ($itemsTempCount > $toSize) {
6155 1
                return false;
6156
            }
6157
        }
6158
6159 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
6160
    }
6161
6162
    /**
6163
     * Checks whether array has more than $size items.
6164
     *
6165
     * @param int $size
6166
     *
6167
     * @return bool
6168
     */
6169 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...
6170
    {
6171
        // init
6172 1
        $itemsTempCount = 0;
6173
6174 1
        foreach ($this->getGenerator() as $key => $value) {
6175 1
            ++$itemsTempCount;
6176 1
            if ($itemsTempCount > $size) {
6177 1
                return true;
6178
            }
6179
        }
6180
6181 1
        return $itemsTempCount > $size;
6182
    }
6183
6184
    /**
6185
     * Checks whether array has less than $size items.
6186
     *
6187
     * @param int $size
6188
     *
6189
     * @return bool
6190
     */
6191 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...
6192
    {
6193
        // init
6194 1
        $itemsTempCount = 0;
6195
6196 1
        foreach ($this->getGenerator() as $key => $value) {
6197 1
            ++$itemsTempCount;
6198 1
            if ($itemsTempCount > $size) {
6199 1
                return false;
6200
            }
6201
        }
6202
6203 1
        return $itemsTempCount < $size;
6204
    }
6205
6206
    /**
6207
     * Counts all elements in an array, or something in an object.
6208
     *
6209
     * <p>
6210
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
6211
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
6212
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
6213
     * implemented and used in PHP.
6214
     * </p>
6215
     *
6216
     * @return int
6217
     *             <p>
6218
     *             The number of elements in var, which is
6219
     *             typically an array, since anything else will have one
6220
     *             element.
6221
     *             </p>
6222
     *             <p>
6223
     *             If var is not an array or an object with
6224
     *             implemented Countable interface,
6225
     *             1 will be returned.
6226
     *             There is one exception, if var is &null;,
6227
     *             0 will be returned.
6228
     *             </p>
6229
     *             <p>
6230
     *             Caution: count may return 0 for a variable that isn't set,
6231
     *             but it may also return 0 for a variable that has been initialized with an
6232
     *             empty array. Use isset to test if a variable is set.
6233
     *             </p>
6234
     */
6235 10
    public function sizeRecursive(): int
6236
    {
6237 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
6238
    }
6239
6240
    /**
6241
     * Extract a slice of the array.
6242
     *
6243
     * @param int      $offset       <p>Slice begin index.</p>
6244
     * @param int|null $length       <p>Length of the slice.</p>
6245
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
6246
     *
6247
     * @return static
6248
     *                <p>(Immutable) A slice of the original array with length $length.</p>
6249
     *
6250
     * @phpstan-return static<TKey,T>
6251
     * @psalm-mutation-free
6252
     */
6253 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
6254
    {
6255 5
        return static::create(
6256 5
            \array_slice(
6257 5
                $this->toArray(),
6258 5
                $offset,
6259 5
                $length,
6260 5
                $preserveKeys
6261
            ),
6262 5
            $this->iteratorClass,
6263 5
            false
6264
        );
6265
    }
6266
6267
    /**
6268
     * Sort the current array and optional you can keep the keys.
6269
     *
6270
     * EXAMPLE: <code>
6271
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sort(SORT_ASC, SORT_NATURAL, false); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6272
     * </code>
6273
     *
6274
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6275
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6276
     *                              <strong>SORT_NATURAL</strong></p>
6277
     * @param bool       $keepKeys
6278
     *
6279
     * @return static
6280
     *                <p>(Mutable) Return this Arrayy object.</p>
6281
     *
6282
     * @phpstan-return static<int|TKey,T>
6283
     */
6284 20
    public function sort(
6285
        $direction = \SORT_ASC,
6286
        int $strategy = \SORT_REGULAR,
6287
        bool $keepKeys = false
6288
    ): self {
6289 20
        $this->generatorToArray();
6290
6291 20
        return $this->sorting(
6292 20
            $this->array,
6293 20
            $direction,
6294 20
            $strategy,
6295 20
            $keepKeys
6296
        );
6297
    }
6298
6299
    /**
6300
     * Sort the current array and optional you can keep the keys.
6301
     *
6302
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6303
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6304
     *                              <strong>SORT_NATURAL</strong></p>
6305
     * @param bool       $keepKeys
6306
     *
6307
     * @return static
6308
     *                <p>(Immutable) Return this Arrayy object.</p>
6309
     *
6310
     * @phpstan-return static<int|TKey,T>
6311
     */
6312 12
    public function sortImmutable(
6313
        $direction = \SORT_ASC,
6314
        int $strategy = \SORT_REGULAR,
6315
        bool $keepKeys = false
6316
    ): self {
6317 12
        $that = clone $this;
6318
6319 12
        $that->generatorToArray();
6320
6321 12
        return $that->sorting(
6322 12
            $that->array,
6323 12
            $direction,
6324 12
            $strategy,
6325 12
            $keepKeys
6326
        );
6327
    }
6328
6329
    /**
6330
     * Sort the current array by key.
6331
     *
6332
     * EXAMPLE: <code>
6333
     * a([1 => 2, 0 => 1])->sortKeys(\SORT_ASC); // Arrayy[0 => 1, 1 => 2]
6334
     * </code>
6335
     *
6336
     * @see http://php.net/manual/en/function.ksort.php
6337
     * @see http://php.net/manual/en/function.krsort.php
6338
     *
6339
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6340
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6341
     *                              <strong>SORT_NATURAL</strong></p>
6342
     *
6343
     * @return $this
6344
     *               <p>(Mutable) Return this Arrayy object.</p>
6345
     *
6346
     * @phpstan-return static<TKey,T>
6347
     */
6348 18
    public function sortKeys(
6349
        $direction = \SORT_ASC,
6350
        int $strategy = \SORT_REGULAR
6351
    ): self {
6352 18
        $this->generatorToArray();
6353
6354 18
        $this->sorterKeys($this->array, $direction, $strategy);
6355
6356 18
        return $this;
6357
    }
6358
6359
    /**
6360
     * Sort the current array by key.
6361
     *
6362
     * @see          http://php.net/manual/en/function.ksort.php
6363
     * @see          http://php.net/manual/en/function.krsort.php
6364
     *
6365
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6366
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6367
     *                              <strong>SORT_NATURAL</strong></p>
6368
     *
6369
     * @return $this
6370
     *               <p>(Immutable) Return this Arrayy object.</p>
6371
     *
6372
     * @phpstan-return static<TKey,T>
6373
     * @psalm-mutation-free
6374
     */
6375 8
    public function sortKeysImmutable(
6376
        $direction = \SORT_ASC,
6377
        int $strategy = \SORT_REGULAR
6378
    ): self {
6379 8
        $that = clone $this;
6380
6381
        /**
6382
         * @psalm-suppress ImpureMethodCall - object is already cloned
6383
         */
6384 8
        $that->sortKeys($direction, $strategy);
6385
6386 8
        return $that;
6387
    }
6388
6389
    /**
6390
     * Sort the current array by value.
6391
     *
6392
     * EXAMPLE: <code>
6393
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueKeepIndex(SORT_ASC, SORT_REGULAR); // Arrayy[0 => 'a', 3 => 'd', 2 => 'f']
6394
     * </code>
6395
     *
6396
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6397
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6398
     *                              <strong>SORT_NATURAL</strong></p>
6399
     *
6400
     * @return static
6401
     *                <p>(Mutable)</p>
6402
     *
6403
     * @phpstan-return static<int|TKey,T>
6404
     */
6405 1
    public function sortValueKeepIndex(
6406
        $direction = \SORT_ASC,
6407
        int $strategy = \SORT_REGULAR
6408
    ): self {
6409 1
        return $this->sort($direction, $strategy, true);
6410
    }
6411
6412
    /**
6413
     * Sort the current array by value.
6414
     *
6415
     * EXAMPLE: <code>
6416
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueNewIndex(SORT_ASC, SORT_NATURAL); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6417
     * </code>
6418
     *
6419
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6420
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6421
     *                              <strong>SORT_NATURAL</strong></p>
6422
     *
6423
     * @return static
6424
     *                <p>(Mutable)</p>
6425
     *
6426
     * @phpstan-return static<int|TKey,T>
6427
     */
6428 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6429
    {
6430 1
        return $this->sort($direction, $strategy, false);
6431
    }
6432
6433
    /**
6434
     * Sort a array by value or by a closure.
6435
     *
6436
     * - If the sorter is null, the array is sorted naturally.
6437
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
6438
     *
6439
     * EXAMPLE: <code>
6440
     * $testArray = range(1, 5);
6441
     * $under = a($testArray)->sorter(
6442
     *     function ($value) {
6443
     *         return $value % 2 === 0;
6444
     *     }
6445
     * );
6446
     * var_dump($under); // Arrayy[1, 3, 5, 2, 4]
6447
     * </code>
6448
     *
6449
     * @param callable|mixed|null $sorter
6450
     * @param int|string          $direction <p>use <strong>SORT_ASC</strong> (default) or
6451
     *                                       <strong>SORT_DESC</strong></p>
6452
     * @param int                 $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6453
     *                                       <strong>SORT_NATURAL</strong></p>
6454
     *
6455
     * @return static
6456
     *                <p>(Immutable)</p>
6457
     *
6458
     * @pslam-param callable|T|null $sorter
6459
     * @phpstan-return static<TKey,T>
6460
     * @psalm-mutation-free
6461
     */
6462 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6463
    {
6464 1
        $array = $this->toArray();
6465 1
        $direction = $this->getDirection($direction);
6466
6467
        // Transform all values into their results.
6468 1
        if ($sorter) {
6469 1
            $arrayy = static::create(
6470 1
                $array,
6471 1
                $this->iteratorClass,
6472 1
                false
6473
            );
6474
6475
            /**
6476
             * @psalm-suppress MissingClosureReturnType
6477
             * @psalm-suppress MissingClosureParamType
6478
             */
6479 1
            $results = $arrayy->each(
6480
                static function ($value) use ($sorter) {
6481 1
                    if (\is_callable($sorter) === true) {
6482 1
                        return $sorter($value);
6483
                    }
6484
6485 1
                    return $sorter === $value;
6486 1
                }
6487
            );
6488
6489 1
            $results = $results->toArray();
6490
        } else {
6491 1
            $results = $array;
6492
        }
6493
6494
        // Sort by the results and replace by original values
6495 1
        \array_multisort($results, $direction, $strategy, $array);
6496
6497 1
        return static::create(
6498 1
            $array,
6499 1
            $this->iteratorClass,
6500 1
            false
6501
        );
6502
    }
6503
6504
    /**
6505
     * @param int      $offset
6506
     * @param int|null $length
6507
     * @param array    $replacement
6508
     *
6509
     * @return static
6510
     *                <p>(Immutable)</p>
6511
     *
6512
     * @phpstan-param  array<mixed,T> $replacement
6513
     * @phpstan-return static<TKey,T>
6514
     * @psalm-mutation-free
6515
     */
6516 1
    public function splice(int $offset, int $length = null, $replacement = []): self
6517
    {
6518 1
        $tmpArray = $this->toArray();
6519
6520 1
        \array_splice(
6521 1
            $tmpArray,
6522 1
            $offset,
6523 1
            $length ?? $this->count(),
6524 1
            $replacement
6525
        );
6526
6527 1
        return static::create(
6528 1
            $tmpArray,
6529 1
            $this->iteratorClass,
6530 1
            false
6531
        );
6532
    }
6533
6534
    /**
6535
     * Split an array in the given amount of pieces.
6536
     *
6537
     * EXAMPLE: <code>
6538
     * a(['a' => 1, 'b' => 2])->split(2, true); // Arrayy[['a' => 1], ['b' => 2]]
6539
     * </code>
6540
     *
6541
     * @param int  $numberOfPieces
6542
     * @param bool $keepKeys
6543
     *
6544
     * @return static
6545
     *                <p>(Immutable)</p>
6546
     *
6547
     * @phpstan-return static<TKey,T>
6548
     * @psalm-mutation-free
6549
     */
6550 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
6551
    {
6552 1
        if ($keepKeys) {
6553
            $generator = function () use ($numberOfPieces) {
6554 1
                $carry = [];
6555 1
                $i = 1;
6556 1
                foreach ($this->getGenerator() as $key => $value) {
6557 1
                    $carry[$key] = $value;
6558
6559 1
                    if ($i % $numberOfPieces !== 0) {
6560 1
                        ++$i;
6561
6562 1
                        continue;
6563
                    }
6564
6565 1
                    yield $carry;
6566
6567 1
                    $carry = [];
6568 1
                    $i = 1;
6569
                }
6570
6571 1
                if ($carry !== []) {
6572 1
                    yield $carry;
6573
                }
6574 1
            };
6575 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...
6576
            $generator = function () use ($numberOfPieces) {
6577 1
                $carry = [];
6578 1
                $i = 1;
6579 1
                foreach ($this->getGenerator() as $key => $value) {
6580 1
                    $carry[] = $value;
6581
6582 1
                    if ($i % $numberOfPieces !== 0) {
6583 1
                        ++$i;
6584
6585 1
                        continue;
6586
                    }
6587
6588 1
                    yield $carry;
6589
6590 1
                    $carry = [];
6591 1
                    $i = 1;
6592
                }
6593
6594 1
                if ($carry !== []) {
6595 1
                    yield $carry;
6596
                }
6597 1
            };
6598
        }
6599
6600 1
        return static::create(
6601 1
            $generator,
6602 1
            $this->iteratorClass,
6603 1
            false
6604
        );
6605
    }
6606
6607
    /**
6608
     * Strip all empty items from the current array.
6609
     *
6610
     * EXAMPLE: <code>
6611
     * a(['a' => 1, 'b' => ''])->stripEmpty(); // Arrayy[['a' => 1]]
6612
     * </code>
6613
     *
6614
     * @return static
6615
     *                <p>(Immutable)</p>
6616
     *
6617
     * @phpstan-return static<TKey,T>
6618
     * @psalm-mutation-free
6619
     */
6620 1
    public function stripEmpty(): self
6621
    {
6622 1
        return $this->filter(
6623
            static function ($item) {
6624 1
                if ($item === null) {
6625 1
                    return false;
6626
                }
6627
6628 1
                return (bool) \trim((string) $item);
6629 1
            }
6630
        );
6631
    }
6632
6633
    /**
6634
     * Swap two values between positions by key.
6635
     *
6636
     * EXAMPLE: <code>
6637
     * a(['a' => 1, 'b' => ''])->swap('a', 'b'); // Arrayy[['a' => '', 'b' => 1]]
6638
     * </code>
6639
     *
6640
     * @param int|string $swapA <p>a key in the array</p>
6641
     * @param int|string $swapB <p>a key in the array</p>
6642
     *
6643
     * @return static
6644
     *                <p>(Immutable)</p>
6645
     *
6646
     * @phpstan-return static<TKey,T>
6647
     * @psalm-mutation-free
6648
     */
6649 1
    public function swap($swapA, $swapB): self
6650
    {
6651 1
        $array = $this->toArray();
6652
6653 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
6654
6655 1
        return static::create(
6656 1
            $array,
6657 1
            $this->iteratorClass,
6658 1
            false
6659
        );
6660
    }
6661
6662
    /**
6663
     * Get the current array from the "Arrayy"-object.
6664
     * alias for "getArray()"
6665
     *
6666
     * @param bool $convertAllArrayyElements <p>
6667
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6668
     *                                       </p>
6669
     * @param bool $preserveKeys             <p>
6670
     *                                       e.g.: A generator maybe return the same key more then once,
6671
     *                                       so maybe you will ignore the keys.
6672
     *                                       </p>
6673
     *
6674
     * @return array
6675
     *
6676
     * @phpstan-return array<TKey,T>
6677
     * @psalm-mutation-free
6678
     */
6679 942
    public function toArray(
6680
        bool $convertAllArrayyElements = false,
6681
        bool $preserveKeys = true
6682
    ): array {
6683
        // init
6684 942
        $array = [];
6685
6686 942
        if ($convertAllArrayyElements) {
6687 3
            foreach ($this->getGenerator() as $key => $value) {
6688 3
                if ($value instanceof self) {
6689 2
                    $value = $value->toArray(
6690 2
                        $convertAllArrayyElements,
6691 2
                        $preserveKeys
6692
                    );
6693
                }
6694
6695 3
                if ($preserveKeys) {
6696 2
                    $array[$key] = $value;
6697
                } else {
6698 3
                    $array[] = $value;
6699
                }
6700
            }
6701
        } else {
6702 941
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
6703
        }
6704
6705
        /** @phpstan-ignore-next-line - depends on the $convertAllArrayyElements parameter :/ */
6706 942
        return $array;
6707
    }
6708
6709
    /**
6710
     * Get the current array from the "Arrayy"-object as list.
6711
     *
6712
     * @param bool $convertAllArrayyElements <p>
6713
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6714
     *                                       </p>
6715
     *
6716
     * @return array
6717
     *
6718
     * @phpstan-return list<mixed>|list<T>
6719
     * @psalm-mutation-free
6720
     */
6721 1
    public function toList(bool $convertAllArrayyElements = false): array
6722
    {
6723 1
        return $this->toArray(
6724 1
            $convertAllArrayyElements,
6725 1
            false
6726
        );
6727
    }
6728
6729
    /**
6730
     * Convert the current array to JSON.
6731
     *
6732
     * EXAMPLE: <code>
6733
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6734
     * </code>
6735
     *
6736
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6737
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6738
     *
6739
     * @return string
6740
     */
6741 12
    public function toJson(int $options = 0, int $depth = 512): string
6742
    {
6743 12
        $return = \json_encode($this->toArray(), $options, $depth);
6744 12
        if ($return === false) {
6745
            return '';
6746
        }
6747
6748 12
        return $return;
6749
    }
6750
6751
    /**
6752
     * @param string[]|null $items  [optional]
6753
     * @param string[]      $helper [optional]
6754
     *
6755
     * @return static|static[]
6756
     *
6757
     * @phpstan-return static<int, static<TKey,T>>
6758
     */
6759 1
    public function toPermutation(array $items = null, array $helper = []): self
6760
    {
6761
        // init
6762 1
        $return = [];
6763
6764 1
        if ($items === null) {
6765 1
            $items = $this->toArray();
6766
        }
6767
6768 1
        if (empty($items)) {
6769 1
            $return[] = $helper;
6770
        } else {
6771 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6772 1
                $new_items = $items;
6773 1
                $new_helper = $helper;
6774 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6775
                /** @noinspection PhpSillyAssignmentInspection */
6776
                /** @var string[] $new_items */
6777 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...
6778 1
                \array_unshift($new_helper, $tmp_helper);
6779 1
                $return = \array_merge(
6780 1
                    $return,
6781 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6782
                );
6783
            }
6784
        }
6785
6786 1
        return static::create(
6787 1
            $return,
6788 1
            $this->iteratorClass,
6789 1
            false
6790
        );
6791
    }
6792
6793
    /**
6794
     * Implodes array to a string with specified separator.
6795
     *
6796
     * @param string $separator [optional] <p>The element's separator.</p>
6797
     *
6798
     * @return string
6799
     *                <p>The string representation of array, separated by ",".</p>
6800
     */
6801 19
    public function toString(string $separator = ','): string
6802
    {
6803 19
        return $this->implode($separator);
6804
    }
6805
6806
    /**
6807
     * Return a duplicate free copy of the current array.
6808
     *
6809
     * EXAMPLE: <code>
6810
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6811
     * </code>
6812
     *
6813
     * @return $this
6814
     *               <p>(Mutable)</p>
6815
     *
6816
     * @phpstan-return static<int,T>
6817
     */
6818 13
    public function uniqueNewIndex(): self
6819
    {
6820
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6821
6822 13
        $this->array = $this->reduce(
6823
            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...
6824 12
                if (!\in_array($value, $resultArray, true)) {
6825 12
                    $resultArray[] = $value;
6826
                }
6827
6828 12
                return $resultArray;
6829 13
            },
6830 13
            []
6831 13
        )->toArray();
6832 13
        $this->generator = null;
6833
6834 13
        return $this;
6835
    }
6836
6837
    /**
6838
     * Return a duplicate free copy of the current array. (with the old keys)
6839
     *
6840
     * EXAMPLE: <code>
6841
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6842
     * </code>
6843
     *
6844
     * @return $this
6845
     *               <p>(Mutable)</p>
6846
     *
6847
     * @phpstan-return static<TKey,T>
6848
     */
6849 11
    public function uniqueKeepIndex(): self
6850
    {
6851
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6852
6853
        // init
6854 11
        $array = $this->toArray();
6855
6856
        /**
6857
         * @psalm-suppress MissingClosureReturnType
6858
         * @psalm-suppress MissingClosureParamType
6859
         */
6860 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...
6861 11
            \array_keys($array),
6862
            static function ($resultArray, $key) use ($array) {
6863 10
                if (!\in_array($array[$key], $resultArray, true)) {
6864 10
                    $resultArray[$key] = $array[$key];
6865
                }
6866
6867 10
                return $resultArray;
6868 11
            },
6869 11
            []
6870
        );
6871 11
        $this->generator = null;
6872
6873 11
        return $this;
6874
    }
6875
6876
    /**
6877
     * alias: for "Arrayy->uniqueNewIndex()"
6878
     *
6879
     * @return static
6880
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6881
     *
6882
     * @see          Arrayy::unique()
6883
     *
6884
     * @phpstan-return static<int,T>
6885
     */
6886 13
    public function unique(): self
6887
    {
6888 13
        return $this->uniqueNewIndex();
6889
    }
6890
6891
    /**
6892
     * Prepends one or more values to the beginning of array at once.
6893
     *
6894
     * @param mixed ...$args
6895
     *
6896
     * @return $this
6897
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6898
     *
6899
     * @phpstan-param  array<TKey,T> ...$args
6900
     * @phpstan-return static<TKey,T>
6901
     */
6902 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...
6903
    {
6904 6
        $this->generatorToArray();
6905
6906
        if (
6907 6
            $this->checkPropertyTypes
6908
            &&
6909 6
            $this->properties !== []
6910
        ) {
6911 2
            foreach ($args as $key => $value) {
6912 2
                $this->checkType($key, $value);
6913
            }
6914
        }
6915
6916 5
        \array_unshift($this->array, ...$args);
6917
6918 5
        return $this;
6919
    }
6920
6921
    /**
6922
     * Tests whether the given closure return something valid for all elements of this array.
6923
     *
6924
     * @param \Closure $closure the predicate
6925
     *
6926
     * @return bool
6927
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6928
     *
6929
     * @phpstan-param \Closure(T=,TKey=):bool $closure
6930
     */
6931 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...
6932
    {
6933 1
        foreach ($this->getGenerator() as $key => $value) {
6934 1
            if (!$closure($value, $key)) {
6935 1
                return false;
6936
            }
6937
        }
6938
6939 1
        return true;
6940
    }
6941
6942
    /**
6943
     * Get all values from a array.
6944
     *
6945
     * EXAMPLE: <code>
6946
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
6947
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
6948
     * </code>
6949
     *
6950
     * @return static
6951
     *                <p>(Immutable)</p>
6952
     *
6953
     * @phpstan-return static<TKey,T>
6954
     * @psalm-mutation-free
6955
     */
6956 2
    public function values(): self
6957
    {
6958 2
        return static::create(
6959
            function () {
6960
                /** @noinspection YieldFromCanBeUsedInspection */
6961 2
                foreach ($this->getGenerator() as $value) {
6962 2
                    yield $value;
6963
                }
6964 2
            },
6965 2
            $this->iteratorClass,
6966 2
            false
6967
        );
6968
    }
6969
6970
    /**
6971
     * Apply the given function to every element in the array, discarding the results.
6972
     *
6973
     * EXAMPLE: <code>
6974
     * $callable = function (&$value, $key) {
6975
     *     $value = $key;
6976
     * };
6977
     * $arrayy = a([1, 2, 3]);
6978
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
6979
     * </code>
6980
     *
6981
     * @param callable $callable
6982
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6983
     * @param mixed    $userData  [optional] <p>
6984
     *                            If the optional $userData parameter is supplied,
6985
     *                            it will be passed as the third parameter to the $callable.
6986
     *                            </p>
6987
     *
6988
     * @return $this
6989
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6990
     *
6991
     * @phpstan-return static<TKey,T>
6992
     */
6993 12
    public function walk(
6994
        $callable,
6995
        bool $recursive = false,
6996
        $userData = self::ARRAYY_HELPER_WALK
6997
    ): self {
6998 12
        $this->generatorToArray();
6999
7000 12
        if ($this->array !== []) {
7001 10
            if ($recursive === true) {
7002 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
7003
                    \array_walk_recursive($this->array, $callable, $userData);
7004
                } else {
7005 5
                    \array_walk_recursive($this->array, $callable);
7006
                }
7007
            } else {
7008
                /** @noinspection NestedPositiveIfStatementsInspection */
7009 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
7010
                    \array_walk($this->array, $callable, $userData);
7011
                } else {
7012 5
                    \array_walk($this->array, $callable);
7013
                }
7014
            }
7015
        }
7016
7017 12
        return $this;
7018
    }
7019
7020
    /**
7021
     * Returns a collection of matching items.
7022
     *
7023
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
7024
     * @param mixed  $value                 the value to match
7025
     *
7026
     * @throws \InvalidArgumentException if property or method is not defined
7027
     *
7028
     * @return static
7029
     *
7030
     * @phpstan-return static<TKey,T>
7031
     */
7032 1
    public function where(string $keyOrPropertyOrMethod, $value): self
7033
    {
7034 1
        return $this->filter(
7035
            function ($item) use ($keyOrPropertyOrMethod, $value) {
7036
                $accessorValue = $this->extractValue(
7037
                    $item,
7038
                    $keyOrPropertyOrMethod
7039
                );
7040
7041
                return $accessorValue === $value;
7042 1
            }
7043
        );
7044
    }
7045
7046
    /**
7047
     * Convert an array into a object.
7048
     *
7049
     * @param array $array
7050
     *
7051
     * @return \stdClass
7052
     *
7053
     * @phpstan-param array<int|string,mixed> $array
7054
     */
7055 4
    final protected static function arrayToObject(array $array = []): \stdClass
7056
    {
7057
        // init
7058 4
        $object = new \stdClass();
7059
7060 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
7061 1
            return $object;
7062
        }
7063
7064 3
        foreach ($array as $name => $value) {
7065 3
            if (\is_array($value)) {
7066 1
                $object->{$name} = static::arrayToObject($value);
7067
            } else {
7068 3
                $object->{$name} = $value;
7069
            }
7070
        }
7071
7072 3
        return $object;
7073
    }
7074
7075
    /**
7076
     * @param array|\Generator|null $input         <p>
7077
     *                                             An array containing keys to return.
7078
     *                                             </p>
7079
     * @param mixed|null            $search_values [optional] <p>
7080
     *                                             If specified, then only keys containing these values are returned.
7081
     *                                             </p>
7082
     * @param bool                  $strict        [optional] <p>
7083
     *                                             Determines if strict comparison (===) should be used during the
7084
     *                                             search.
7085
     *                                             </p>
7086
     *
7087
     * @return array
7088
     *               <p>An array of all the keys in input.</p>
7089
     *
7090
     * @phpstan-param  array<mixed>|null $input
7091
     * @phpstan-return array<mixed>
7092
     * @psalm-mutation-free
7093
     */
7094 11
    protected function array_keys_recursive(
7095
        $input = null,
7096
        $search_values = null,
7097
        bool $strict = true
7098
    ): array {
7099
        // init
7100 11
        $keys = [];
7101 11
        $keysTmp = [];
7102
7103 11
        if ($input === null) {
7104 4
            $input = $this->getGenerator();
7105
        }
7106
7107 11
        if ($search_values === null) {
7108 11
            foreach ($input as $key => $value) {
7109 11
                $keys[] = $key;
7110
7111
                // check if recursive is needed
7112 11
                if (\is_array($value)) {
7113 11
                    $keysTmp[] = $this->array_keys_recursive($value);
7114
                }
7115
            }
7116
        } else {
7117 1
            $is_array_tmp = \is_array($search_values);
7118
7119 1
            foreach ($input as $key => $value) {
7120 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...
7121
                    (
7122 1
                        $is_array_tmp === false
7123
                        &&
7124 1
                        $strict === true
7125
                        &&
7126 1
                        $search_values === $value
7127
                    )
7128
                    ||
7129
                    (
7130 1
                        $is_array_tmp === false
7131
                        &&
7132 1
                        $strict === false
7133
                        &&
7134 1
                        $search_values == $value
7135
                    )
7136
                    ||
7137
                    (
7138 1
                        $is_array_tmp === true
7139
                        &&
7140 1
                        \in_array($value, $search_values, $strict)
7141
                    )
7142
                ) {
7143 1
                    $keys[] = $key;
7144
                }
7145
7146
                // check if recursive is needed
7147 1
                if (\is_array($value)) {
7148 1
                    $keysTmp[] = $this->array_keys_recursive($value);
7149
                }
7150
            }
7151
        }
7152
7153 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
7154
    }
7155
7156
    /**
7157
     * @param mixed      $path
7158
     * @param callable   $callable
7159
     * @param array|null $currentOffset
7160
     *
7161
     * @return void
7162
     *
7163
     * @phpstan-param array<TKey,T>|null $currentOffset
7164
     * @psalm-mutation-free
7165
     */
7166 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
7167
    {
7168 10
        $this->generatorToArray();
7169
7170 10
        if ($currentOffset === null) {
7171 10
            $currentOffset = &$this->array;
7172
        }
7173
7174 10
        $explodedPath = \explode($this->pathSeparator, $path);
7175 10
        if ($explodedPath === false) {
7176
            return;
7177
        }
7178
7179 10
        $nextPath = \array_shift($explodedPath);
7180
7181 10
        if (!isset($currentOffset[$nextPath])) {
7182 1
            return;
7183
        }
7184
7185 9
        if (!empty($explodedPath)) {
7186 1
            $this->callAtPath(
7187 1
                \implode($this->pathSeparator, $explodedPath),
7188 1
                $callable,
7189 1
                $currentOffset[$nextPath]
7190
            );
7191
        } else {
7192 9
            $callable($currentOffset[$nextPath]);
7193
        }
7194 9
    }
7195
7196
    /**
7197
     * Extracts the value of the given property or method from the object.
7198
     *
7199
     * @param static $object                <p>The object to extract the value from.</p>
7200
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
7201
     *                                      value should be extracted.</p>
7202
     *
7203
     * @throws \InvalidArgumentException if the method or property is not defined
7204
     *
7205
     * @return mixed
7206
     *               <p>The value extracted from the specified property or method.</p>
7207
     *
7208
     * @phpstan-param self<TKey,T> $object
7209
     */
7210 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
7211
    {
7212 1
        if (isset($object[$keyOrPropertyOrMethod])) {
7213 1
            $return = $object->get($keyOrPropertyOrMethod);
7214
7215 1
            if ($return instanceof self) {
7216
                return $return->toArray();
7217
            }
7218
7219 1
            return $return;
7220
        }
7221
7222
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
7223
            return $object->{$keyOrPropertyOrMethod};
7224
        }
7225
7226
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
7227
            return $object->{$keyOrPropertyOrMethod}();
7228
        }
7229
7230
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
7231
    }
7232
7233
    /**
7234
     * create a fallback for array
7235
     *
7236
     * 1. use the current array, if it's a array
7237
     * 2. fallback to empty array, if there is nothing
7238
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
7239
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
7240
     * 5. call "__toArray()" on object, if the method exists
7241
     * 6. cast a string or object with "__toString()" into an array
7242
     * 7. throw a "InvalidArgumentException"-Exception
7243
     *
7244
     * @param mixed $data
7245
     *
7246
     * @throws \InvalidArgumentException
7247
     *
7248
     * @return array
7249
     *
7250
     * @phpstan-return array<mixed>|array<TKey,T>
7251
     */
7252 1213
    protected function fallbackForArray(&$data): array
7253
    {
7254 1213
        $data = $this->internalGetArray($data);
7255
7256 1213
        if ($data === null) {
7257 2
            throw new \InvalidArgumentException('Passed value should be a array');
7258
        }
7259
7260 1211
        return $data;
7261
    }
7262
7263
    /**
7264
     * @param bool $preserveKeys <p>
7265
     *                           e.g.: A generator maybe return the same key more then once,
7266
     *                           so maybe you will ignore the keys.
7267
     *                           </p>
7268
     *
7269
     * @return bool
7270
     *
7271
     * @noinspection ReturnTypeCanBeDeclaredInspection
7272
     * @psalm-mutation-free :/
7273
     */
7274 1122
    protected function generatorToArray(bool $preserveKeys = true)
7275
    {
7276 1122
        if ($this->generator) {
7277 2
            $this->array = $this->toArray(false, $preserveKeys);
7278 2
            $this->generator = null;
7279
7280 2
            return true;
7281
        }
7282
7283 1122
        return false;
7284
    }
7285
7286
    /**
7287
     * Get correct PHP constant for direction.
7288
     *
7289
     * @param int|string $direction
7290
     *
7291
     * @return int
7292
     * @psalm-mutation-free
7293
     */
7294 43
    protected function getDirection($direction): int
7295
    {
7296 43
        if ((string) $direction === $direction) {
7297 10
            $direction = \strtolower($direction);
7298
7299 10
            if ($direction === 'desc') {
7300 2
                $direction = \SORT_DESC;
7301
            } else {
7302 9
                $direction = \SORT_ASC;
7303
            }
7304
        }
7305
7306
        if (
7307 43
            $direction !== \SORT_DESC
7308
            &&
7309 43
            $direction !== \SORT_ASC
7310
        ) {
7311
            $direction = \SORT_ASC;
7312
        }
7313
7314 43
        return $direction;
7315
    }
7316
7317
    /**
7318
     * @return TypeCheckInterface[]
7319
     *
7320
     * @noinspection ReturnTypeCanBeDeclaredInspection
7321
     */
7322 24
    protected function getPropertiesFromPhpDoc()
7323
    {
7324 24
        static $PROPERTY_CACHE = [];
7325 24
        $cacheKey = 'Class::' . static::class;
7326
7327 24
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7328 22
            return $PROPERTY_CACHE[$cacheKey];
7329
        }
7330
7331
        // init
7332 4
        $properties = [];
7333
7334 4
        $reflector = new \ReflectionClass($this);
7335 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7336 4
        $docComment = $reflector->getDocComment();
7337 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...
7338 4
            $docblock = $factory->create($docComment);
7339
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7340 4
            foreach ($docblock->getTagsByName('property') as $tag) {
7341 3
                $typeName = $tag->getVariableName();
7342
                /** @var string|null $typeName */
7343 3
                if ($typeName !== null) {
7344 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7345 3
                    if ($typeCheckPhpDoc !== null) {
7346 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7347
                    }
7348
                }
7349
            }
7350
        }
7351
7352
        /** @noinspection PhpAssignmentInConditionInspection */
7353 4
        while ($reflector = $reflector->getParentClass()) {
7354 4
            $docComment = $reflector->getDocComment();
7355 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...
7356 4
                $docblock = $factory->create($docComment);
7357
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7358 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7359 1
                    $typeName = $tag->getVariableName();
7360
                    /** @var string|null $typeName */
7361 1
                    if ($typeName !== null) {
7362 1
                        if (isset($properties[$typeName])) {
7363 1
                            continue;
7364
                        }
7365
7366 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7367 1
                        if ($typeCheckPhpDoc !== null) {
7368 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7369
                        }
7370
                    }
7371
                }
7372
            }
7373
        }
7374
7375 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7376
    }
7377
7378
    /**
7379
     * @param mixed $glue
7380
     * @param mixed $pieces
7381
     * @param bool  $useKeys
7382
     *
7383
     * @return string
7384
     *
7385
     * @phpstan-param scalar|object|self<TKey|T>|array<TKey,T> $pieces
7386
     * @psalm-mutation-free
7387
     */
7388 36
    protected function implode_recursive(
7389
        $glue = '',
7390
        $pieces = [],
7391
        bool $useKeys = false
7392
    ): string {
7393 36
        if ($pieces instanceof self) {
7394 1
            $pieces = $pieces->toArray();
7395
        }
7396
7397 36
        if (\is_array($pieces)) {
7398
            /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */
7399
            /** @phpstan-var array<TKey,T> $pieces */
7400 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...
7401
7402 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7403 36
            $pieces_count_not_zero = $pieces_count > 0;
7404
7405 36
            return \implode(
7406 36
                $glue,
7407 36
                \array_map(
7408 36
                    [$this, 'implode_recursive'],
7409 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7410 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7411
                )
7412
            );
7413
        }
7414
7415
        if (
7416 36
            \is_scalar($pieces) === true
7417
            ||
7418 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7419
        ) {
7420 32
            return (string) $pieces;
7421
        }
7422
7423 8
        return '';
7424
    }
7425
7426
    /**
7427
     * @param mixed                 $needle   <p>
7428
     *                                        The searched value.
7429
     *                                        </p>
7430
     *                                        <p>
7431
     *                                        If needle is a string, the comparison is done
7432
     *                                        in a case-sensitive manner.
7433
     *                                        </p>
7434
     * @param array|\Generator|null $haystack <p>
7435
     *                                        The array.
7436
     *                                        </p>
7437
     * @param bool                  $strict   [optional] <p>
7438
     *                                        If the third parameter strict is set to true
7439
     *                                        then the in_array function will also check the
7440
     *                                        types of the
7441
     *                                        needle in the haystack.
7442
     *                                        </p>
7443
     *
7444
     * @return bool
7445
     *              <p>true if needle is found in the array, false otherwise</p>
7446
     *
7447
     * @phpstan-param (array&T)|array<TKey,T>|\Generator<TKey,T>|null $haystack
7448
     * @psalm-mutation-free
7449
     */
7450 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7451
    {
7452 18
        if ($haystack === null) {
7453
            $haystack = $this->getGenerator();
7454
        }
7455
7456 18
        foreach ($haystack as $item) {
7457 14
            if (\is_array($item)) {
7458 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7459
            } else {
7460
                /** @noinspection NestedPositiveIfStatementsInspection */
7461 14
                if ($strict === true) {
7462 14
                    $returnTmp = $item === $needle;
7463
                } else {
7464
                    $returnTmp = $item == $needle;
7465
                }
7466
            }
7467
7468 14
            if ($returnTmp === true) {
7469 14
                return true;
7470
            }
7471
        }
7472
7473 8
        return false;
7474
    }
7475
7476
    /**
7477
     * @param mixed $data
7478
     *
7479
     * @return array<mixed>|null
7480
     */
7481 1213
    protected function internalGetArray(&$data)
7482
    {
7483 1213
        if (\is_array($data)) {
7484 1207
            return $data;
7485
        }
7486
7487 110
        if (!$data) {
7488 7
            return [];
7489
        }
7490
7491 109
        if (\is_object($data) === true) {
7492 102
            if ($data instanceof \ArrayObject) {
7493 5
                return $data->getArrayCopy();
7494
            }
7495
7496 98
            if ($data instanceof \Generator) {
7497
                return static::createFromGeneratorImmutable($data)->toArray();
7498
            }
7499
7500 98
            if ($data instanceof \Traversable) {
7501
                return static::createFromObject($data)->toArray();
7502
            }
7503
7504 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...
7505
                return (array) $data->jsonSerialize();
7506
            }
7507
7508 98
            if (\method_exists($data, '__toArray')) {
7509
                return (array) $data->__toArray();
7510
            }
7511
7512 98
            if (\method_exists($data, '__toString')) {
7513
                return [(string) $data];
7514
            }
7515
        }
7516
7517 105
        if (\is_callable($data)) {
7518
            /**
7519
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7520
             */
7521 96
            $this->generator = new ArrayyRewindableGenerator($data);
7522
7523 96
            return [];
7524
        }
7525
7526 11
        if (\is_scalar($data)) {
7527 9
            return [$data];
7528
        }
7529
7530 2
        return null;
7531
    }
7532
7533
    /**
7534
     * Internal mechanics of remove method.
7535
     *
7536
     * @param mixed $key
7537
     *
7538
     * @return bool
7539
     */
7540 22
    protected function internalRemove($key): bool
7541
    {
7542 22
        $this->generatorToArray();
7543
7544
        if (
7545 22
            $this->pathSeparator
7546
            &&
7547 22
            (string) $key === $key
7548
            &&
7549 22
            \strpos($key, $this->pathSeparator) !== false
7550
        ) {
7551
            $path = \explode($this->pathSeparator, (string) $key);
7552
7553
            if ($path !== false) {
7554
                // crawl though the keys
7555
                while (\count($path, \COUNT_NORMAL) > 1) {
7556
                    $key = \array_shift($path);
7557
7558
                    if (!$this->has($key)) {
7559
                        return false;
7560
                    }
7561
7562
                    $this->array = &$this->array[$key];
7563
                }
7564
7565
                $key = \array_shift($path);
7566
            }
7567
        }
7568
7569 22
        unset($this->array[$key]);
7570
7571 22
        return true;
7572
    }
7573
7574
    /**
7575
     * Internal mechanic of set method.
7576
     *
7577
     * @param int|string|null $key
7578
     * @param mixed           $value
7579
     * @param bool            $checkProperties
7580
     *
7581
     * @return bool
7582
     */
7583 1063
    protected function internalSet(
7584
        $key,
7585
        &$value,
7586
        bool $checkProperties = true
7587
    ): bool {
7588
        if (
7589 1063
            $checkProperties === true
7590
            &&
7591 1063
            $this->properties !== []
7592
        ) {
7593 117
            $this->checkType($key, $value);
7594
        }
7595
7596 1061
        if ($key === null) {
7597
            return false;
7598
        }
7599
7600 1061
        $this->generatorToArray();
7601
7602
        /** @phpstan-var array<int|string,mixed> $array */
7603 1061
        $array = &$this->array;
7604
7605
        /**
7606
         * https://github.com/vimeo/psalm/issues/2536
7607
         *
7608
         * @psalm-suppress PossiblyInvalidArgument
7609
         * @psalm-suppress InvalidScalarArgument
7610
         */
7611
        if (
7612 1061
            $this->pathSeparator
7613
            &&
7614 1061
            (string) $key === $key
7615
            &&
7616 1061
            \strpos($key, $this->pathSeparator) !== false
7617
        ) {
7618 9
            $path = \explode($this->pathSeparator, (string) $key);
7619
7620 9
            if ($path !== false) {
7621
                // crawl through the keys
7622 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7623 9
                    $key = \array_shift($path);
7624
7625 9
                    $array = &$array[$key];
7626
                }
7627
7628 9
                $key = \array_shift($path);
7629
            }
7630
        }
7631
7632 1061
        if ($array === null) {
7633 4
            $array = [];
7634 1058
        } elseif (!\is_array($array)) {
7635 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7636
        }
7637
7638 1061
        $array[$key] = $value;
7639
7640 1061
        return true;
7641
    }
7642
7643
    /**
7644
     * Convert a object into an array.
7645
     *
7646
     * @param mixed|object $object
7647
     *
7648
     * @return array|mixed
7649
     *
7650
     * @psalm-mutation-free
7651
     */
7652 5
    protected static function objectToArray($object)
7653
    {
7654 5
        if (!\is_object($object)) {
7655 4
            return $object;
7656
        }
7657
7658 5
        $object = \get_object_vars($object);
7659
7660
        /**
7661
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7662
         */
7663 5
        return \array_map(['static', 'objectToArray'], $object);
7664
    }
7665
7666
    /**
7667
     * @param array $data
7668
     * @param bool  $checkPropertiesInConstructor
7669
     *
7670
     * @return void
7671
     *
7672
     * @phpstan-param array<mixed,T> $data
7673
     */
7674 1211
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7675
    {
7676 1211
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7677
                                        &&
7678 1211
                                        $checkPropertiesInConstructor === true;
7679
7680 1211
        if ($this->properties !== []) {
7681 104
            foreach ($data as $key => &$valueInner) {
7682 104
                $this->internalSet(
7683 104
                    $key,
7684 104
                    $valueInner,
7685 104
                    $checkPropertiesInConstructor
7686
                );
7687
            }
7688
        } else {
7689
            if (
7690 1126
                $this->checkPropertyTypes === true
7691
                ||
7692 1126
                $checkPropertiesInConstructor === true
7693
            ) {
7694 23
                $this->properties = $this->getPropertiesFromPhpDoc();
7695
            }
7696
7697
            /** @var TypeCheckInterface[] $properties */
7698 1126
            $properties = $this->properties;
7699
7700
            if (
7701 1126
                $this->checkPropertiesMismatchInConstructor === true
7702
                &&
7703 1126
                \count($data) !== 0
7704
                &&
7705 1126
                \count(\array_diff_key($properties, $data)) > 0
7706
            ) {
7707 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...
7708
            }
7709
7710 1125
            foreach ($data as $key => &$valueInner) {
7711 954
                $this->internalSet(
7712 954
                    $key,
7713 954
                    $valueInner,
7714 954
                    $checkPropertiesInConstructor
7715
                );
7716
            }
7717
        }
7718 1203
    }
7719
7720
    /**
7721
     * sorting keys
7722
     *
7723
     * @param array      $elements
7724
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7725
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7726
     *                              <strong>SORT_NATURAL</strong></p>
7727
     *
7728
     * @return $this
7729
     *               <p>(Mutable) Return this Arrayy object.</p>
7730
     *
7731
     * @phpstan-param  array<mixed|TKey,T> $elements
7732
     * @phpstan-return static<TKey,T>
7733
     */
7734 18
    protected function sorterKeys(
7735
        array &$elements,
7736
        $direction = \SORT_ASC,
7737
        int $strategy = \SORT_REGULAR
7738
    ): self {
7739 18
        $direction = $this->getDirection($direction);
7740
7741
        switch ($direction) {
7742 18
            case 'desc':
7743 18
            case \SORT_DESC:
7744 6
                \krsort($elements, $strategy);
7745
7746 6
                break;
7747 13
            case 'asc':
7748 13
            case \SORT_ASC:
7749
            default:
7750 13
                \ksort($elements, $strategy);
7751
        }
7752
7753 18
        return $this;
7754
    }
7755
7756
    /**
7757
     * @param array      $elements  <p>Warning: used as reference</p>
7758
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7759
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7760
     *                              <strong>SORT_NATURAL</strong></p>
7761
     * @param bool       $keepKeys
7762
     *
7763
     * @return $this
7764
     *               <p>(Mutable) Return this Arrayy object.</p>
7765
     *
7766
     * @phpstan-param array<mixed|TKey,T> $elements
7767
     * @phpstan-return static<int|TKey,T>
7768
     */
7769 24
    protected function sorting(
7770
        array &$elements,
7771
        $direction = \SORT_ASC,
7772
        int $strategy = \SORT_REGULAR,
7773
        bool $keepKeys = false
7774
    ): self {
7775 24
        $direction = $this->getDirection($direction);
7776
7777 24
        if (!$strategy) {
7778 24
            $strategy = \SORT_REGULAR;
7779
        }
7780
7781
        switch ($direction) {
7782 24
            case 'desc':
7783 24
            case \SORT_DESC:
7784 13
                if ($keepKeys) {
7785 9
                    \arsort($elements, $strategy);
7786
                } else {
7787 4
                    \rsort($elements, $strategy);
7788
                }
7789
7790 13
                break;
7791 11
            case 'asc':
7792 11
            case \SORT_ASC:
7793
            default:
7794 11
                if ($keepKeys) {
7795 4
                    \asort($elements, $strategy);
7796
                } else {
7797 7
                    \sort($elements, $strategy);
7798
                }
7799
        }
7800
7801 24
        return $this;
7802
    }
7803
7804
    /**
7805
     * @param array $array
7806
     *
7807
     * @return array
7808
     *
7809
     * @psalm-mutation-free
7810
     */
7811 25
    private function getArrayRecursiveHelperArrayy(array $array)
7812
    {
7813 25
        if ($array === []) {
7814
            return [];
7815
        }
7816
7817 25
        \array_walk_recursive(
7818 25
            $array,
7819
            /**
7820
             * @param array|self $item
7821
             *
7822
             * @return void
7823
             */
7824
            static function (&$item) {
7825 25
                if ($item instanceof self) {
7826 1
                    $item = $item->getArray();
7827
                }
7828 25
            }
7829
        );
7830
7831 25
        return $array;
7832
    }
7833
7834
    /**
7835
     * @param int|string|null $key
7836
     * @param mixed           $value
7837
     *
7838
     * @return void
7839
     */
7840 117
    private function checkType($key, $value)
7841
    {
7842
        if (
7843 117
            $key !== null
7844
            &&
7845 117
            isset($this->properties[$key]) === false
7846
            &&
7847 117
            $this->checkPropertiesMismatch === true
7848
        ) {
7849
            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...
7850
        }
7851
7852 117
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7853 102
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7854 24
        } elseif ($key !== null && isset($this->properties[$key])) {
7855 24
            $this->properties[$key]->checkType($value);
7856
        }
7857 115
    }
7858
}
7859