Completed
Push — master ( 065cb9...5b342a )
by Lars
177:26 queued 83:44
created

Arrayy::keys()   C

Complexity

Conditions 13
Paths 3

Size

Total Lines 71

Duplication

Lines 25
Ratio 35.21 %

Code Coverage

Tests 30
CRAP Score 13

Importance

Changes 0
Metric Value
cc 13
nc 3
nop 3
dl 25
loc 71
ccs 30
cts 30
cp 1
crap 13
rs 5.926
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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