Completed
Push — master ( 1695bf...a4f4c1 )
by Lars
01:36
created

Arrayy::containsKeysRecursive()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/** @noinspection ReturnTypeCanBeDeclaredInspection */
4
/** @noinspection ClassReImplementsParentInterfaceInspection */
5
6
declare(strict_types=1);
7
8
namespace Arrayy;
9
10
use Arrayy\Type\TypeInterface;
11
use Arrayy\TypeCheck\TypeCheckArray;
12
use Arrayy\TypeCheck\TypeCheckInterface;
13
use Arrayy\TypeCheck\TypeCheckPhpDoc;
14
15
/**
16
 * Methods to manage arrays.
17
 *
18
 * For the full copyright and license information, please view the LICENSE
19
 * file that was distributed with this source code.
20
 *
21
 * @template TKey of array-key
22
 * @template T
23
 * @template-extends \ArrayObject<TKey,T>
24
 * @template-implements \IteratorAggregate<TKey,T>
25
 * @template-implements \ArrayAccess<TKey,T>
26
 */
27
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
28
{
29
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
30
31
    const ARRAYY_HELPER_WALK = '!!!!Arrayy_Helper_Walk!!!!';
32
33
    /**
34
     * @var array
35
     *
36
     * @psalm-var array<mixed,mixed>|array<TKey,T>
37
     */
38
    protected $array = [];
39
40
    /**
41
     * @var \Arrayy\ArrayyRewindableGenerator|null
42
     *
43
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
44
     */
45
    protected $generator;
46
47
    /**
48
     * @var string
49
     *
50
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
51
     */
52
    protected $iteratorClass = ArrayyIterator::class;
53
54
    /**
55
     * @var string
56
     */
57
    protected $pathSeparator = '.';
58
59
    /**
60
     * @var bool
61
     */
62
    protected $checkPropertyTypes = false;
63
64
    /**
65
     * @var bool
66
     */
67
    protected $checkForMissingPropertiesInConstructor = false;
68
69
    /**
70
     * @var bool
71
     */
72
    protected $checkPropertiesMismatchInConstructor = false;
73
74
    /**
75
     * @var bool
76
     */
77
    protected $checkPropertiesMismatch = true;
78
79
    /**
80
     * @var array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
81
     */
82
    protected $properties = [];
83
84
    /**
85
     * Initializes
86
     *
87
     * @param mixed  $data                         <p>
88
     *                                             Should be an array or a generator, otherwise it will try
89
     *                                             to convert it into an array.
90
     *                                             </p>
91
     * @param string $iteratorClass                optional <p>
92
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
93
     *                                             need this option.
94
     *                                             </p>
95
     * @param bool   $checkPropertiesInConstructor optional <p>
96
     *                                             You need to extend the "Arrayy"-class and you need to set
97
     *                                             the $checkPropertiesMismatchInConstructor class property
98
     *                                             to
99
     *                                             true, otherwise this option didn't not work anyway.
100
     *                                             </p>
101
     *
102
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
103
     */
104 1212
    public function __construct(
105
        $data = [],
106
        string $iteratorClass = ArrayyIterator::class,
107
        bool $checkPropertiesInConstructor = true
108
    ) {
109 1212
        $data = $this->fallbackForArray($data);
110
111
        // used only for serialize + unserialize, all other methods are overwritten
112
        /**
113
         * @psalm-suppress InvalidArgument - why?
114
         */
115 1210
        parent::__construct([], 0, $iteratorClass);
116
117 1210
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
118
119 1202
        $this->setIteratorClass($iteratorClass);
120 1202
    }
121
122
    /**
123
     * @return void
124
     */
125 52
    public function __clone()
126
    {
127 52
        if (!\is_array($this->properties)) {
128
            $this->properties = clone $this->properties;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->properties of type object is incompatible with the declared type array of property $properties.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
129
        }
130
131 52
        if ($this->generator !== null) {
132
            $this->generator = clone $this->generator;
133
        }
134 52
    }
135
136
    /**
137
     * Call object as function.
138
     *
139
     * @param mixed $key
140
     *
141
     * @return mixed
142
     */
143 1
    public function __invoke($key = null)
144
    {
145 1
        if ($key !== null) {
146 1
            $this->generatorToArray();
147
148 1
            return $this->array[$key] ?? false;
149
        }
150
151
        return $this->toArray();
152
    }
153
154
    /**
155
     * Whether or not an element exists by key.
156
     *
157
     * @param mixed $key
158
     *
159
     * @return bool
160
     *              <p>True is the key/index exists, otherwise false.</p>
161
     */
162
    public function __isset($key): bool
163
    {
164
        return $this->offsetExists($key);
165
    }
166
167
    /**
168
     * Assigns a value to the specified element.
169
     *
170
     * @param mixed $key
171
     * @param mixed $value
172
     *
173
     * @return void
174
     */
175 3
    public function __set($key, $value)
176
    {
177 3
        $this->internalSet($key, $value);
178 3
    }
179
180
    /**
181
     * magic to string
182
     *
183
     * @return string
184
     */
185 15
    public function __toString(): string
186
    {
187 15
        return $this->toString();
188
    }
189
190
    /**
191
     * Unset element by key.
192
     *
193
     * @param mixed $key
194
     */
195
    public function __unset($key)
196
    {
197
        $this->internalRemove($key);
198
    }
199
200
    /**
201
     * Get a value by key.
202
     *
203
     * @param mixed $key
204
     *
205
     * @return mixed
206
     *               <p>Get a Value from the current array.</p>
207
     */
208 133
    public function &__get($key)
209
    {
210 133
        $return = $this->get($key, null, null, true);
211
212 133
        if (\is_array($return) === true) {
213
            $return = static::create(
214
                [],
215
                $this->iteratorClass,
216
                false
217
            )->createByReference($return);
218
        }
219
220 133
        return $return;
221
    }
222
223
    /**
224
     * Add new values (optional using dot-notation).
225
     *
226
     * @param mixed           $value
227
     * @param int|string|null $key
228
     *
229
     * @return static
230
     *                <p>(Immutable) Return this Arrayy object, with the appended values.</p>
231
     *
232
     * @psalm-param  T $value
233
     * @psalm-return static<TKey,T>
234
     *
235
     * @psalm-param T $value
236
     * @psalm-param TKey $key
237
     * @psalm-mutation-free
238
     */
239 13
    public function add($value, $key = null)
240
    {
241 13
        if ($key !== null) {
242 5
            $get = $this->get($key);
243 5
            if ($get !== null) {
244 1
                $value = \array_merge_recursive(
245 1
                    !$get instanceof self ? [$get] : $get->getArray(),
246 1
                    !\is_array($value) ? [$value] : $value
247
                );
248
            }
249
250 5
            $this->internalSet($key, $value);
251
252 4
            return $this;
253
        }
254
255 8
        return $this->append($value);
256
    }
257
258
    /**
259
     * Append a (key) + value to the current array.
260
     *
261
     * EXAMPLE: <code>
262
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
263
     * </code>
264
     *
265
     * @param mixed $value
266
     * @param mixed $key
267
     *
268
     * @return $this
269
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
270
     *
271
     * @psalm-param T $value
272
     * @psalm-param TKey|null $key
273
     * @psalm-return static<TKey,T>
274
     */
275 20
    public function append($value, $key = null): self
276
    {
277 20
        $this->generatorToArray();
278
279 20
        if ($this->properties !== []) {
280 6
            $this->checkType($key, $value);
281
        }
282
283 19
        if ($key !== null) {
284
            if (
285 2
                isset($this->array[$key])
286
                &&
287 2
                \is_array($this->array[$key])
288
            ) {
289
                $this->array[$key][] = $value;
290
            } else {
291 2
                $this->array[$key] = $value;
292
            }
293
        } else {
294 17
            $this->array[] = $value;
295
        }
296
297 19
        return $this;
298
    }
299
300
    /**
301
     * Append a (key) + value to the current array.
302
     *
303
     * EXAMPLE: <code>
304
     * a(['fòô' => 'bàř'])->appendImmutable('foo')->getArray(); // ['fòô' => 'bàř', 0 => 'foo']
305
     * </code>
306
     *
307
     * @param mixed $value
308
     * @param mixed $key
309
     *
310
     * @return $this
311
     *               <p>(Immutable) Return this Arrayy object, with the appended values.</p>
312
     *
313
     * @psalm-param T $value
314
     * @psalm-param TKey $key
315
     * @psalm-return static<TKey,T>
316
     * @psalm-mutation-free
317
     */
318 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...
319
    {
320
        $generator = function () use ($key, $value): \Generator {
321 1
            if ($this->properties !== []) {
322
                $this->checkType($key, $value);
323
            }
324
325
            /** @noinspection YieldFromCanBeUsedInspection - FP */
326 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
327 1
                yield $keyOld => $itemOld;
328
            }
329
330 1
            if ($key !== null) {
331
                yield $key => $value;
332
            } else {
333 1
                yield $value;
334
            }
335 1
        };
336
337 1
        return static::create(
338 1
            $generator,
339 1
            $this->iteratorClass,
340 1
            false
341
        );
342
    }
343
344
    /**
345
     * Sort the entries by value.
346
     *
347
     * @param int $sort_flags [optional] <p>
348
     *                        You may modify the behavior of the sort using the optional
349
     *                        parameter sort_flags, for details
350
     *                        see sort.
351
     *                        </p>
352
     *
353
     * @return $this
354
     *               <p>(Mutable) Return this Arrayy object.</p>
355
     *
356
     * @psalm-return static<TKey,T>
357
     */
358 4
    public function asort(int $sort_flags = 0): self
359
    {
360 4
        $this->generatorToArray();
361
362 4
        \asort($this->array, $sort_flags);
363
364 4
        return $this;
365
    }
366
367
    /**
368
     * Sort the entries by value.
369
     *
370
     * @param int $sort_flags [optional] <p>
371
     *                        You may modify the behavior of the sort using the optional
372
     *                        parameter sort_flags, for details
373
     *                        see sort.
374
     *                        </p>
375
     *
376
     * @return $this
377
     *               <p>(Immutable) Return this Arrayy object.</p>
378
     *
379
     * @psalm-return static<TKey,T>
380
     * @psalm-mutation-free
381
     */
382 4
    public function asortImmutable(int $sort_flags = 0): self
383
    {
384 4
        $that = clone $this;
385
386
        /**
387
         * @psalm-suppress ImpureMethodCall - object is already cloned
388
         */
389 4
        $that->asort($sort_flags);
390
391 4
        return $that;
392
    }
393
394
    /**
395
     * Counts all elements in an array, or something in an object.
396
     *
397
     * EXAMPLE: <code>
398
     * a([-9, -8, -7, 1.32])->count(); // 4
399
     * </code>
400
     *
401
     * <p>
402
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
403
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
404
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
405
     * implemented and used in PHP.
406
     * </p>
407
     *
408
     * @see http://php.net/manual/en/function.count.php
409
     *
410
     * @param int $mode [optional] If the optional mode parameter is set to
411
     *                  COUNT_RECURSIVE (or 1), count
412
     *                  will recursively count the array. This is particularly useful for
413
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
414
     *
415
     * @return int
416
     *             <p>
417
     *             The number of elements in var, which is
418
     *             typically an array, since anything else will have one
419
     *             element.
420
     *             </p>
421
     *             <p>
422
     *             If var is not an array or an object with
423
     *             implemented Countable interface,
424
     *             1 will be returned.
425
     *             There is one exception, if var is &null;,
426
     *             0 will be returned.
427
     *             </p>
428
     *             <p>
429
     *             Caution: count may return 0 for a variable that isn't set,
430
     *             but it may also return 0 for a variable that has been initialized with an
431
     *             empty array. Use isset to test if a variable is set.
432
     *             </p>
433
     * @psalm-mutation-free
434
     */
435 147
    public function count(int $mode = \COUNT_NORMAL): int
436
    {
437
        if (
438 147
            $this->generator
439
            &&
440 147
            $mode === \COUNT_NORMAL
441
        ) {
442 4
            return \iterator_count($this->generator);
443
        }
444
445 143
        return \count($this->toArray(), $mode);
446
    }
447
448
    /**
449
     * Exchange the array for another one.
450
     *
451
     * @param array|mixed|static $data
452
     *
453
     * 1. use the current array, if it's a array
454
     * 2. fallback to empty array, if there is nothing
455
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
456
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
457
     * 5. call "__toArray()" on object, if the method exists
458
     * 6. cast a string or object with "__toString()" into an array
459
     * 7. throw a "InvalidArgumentException"-Exception
460
     *
461
     * @return array
462
     *
463
     * @psalm-param  T,array<TKey,T>|self<TKey,T> $data
464
     * @psalm-return array<mixed,mixed>|array<TKey,T>
465
     */
466 1
    public function exchangeArray($data): array
467
    {
468 1
        $this->array = $this->fallbackForArray($data);
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
     * @psalm-return array<mixed,mixed>|array<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
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
498
     */
499 28
    public function getIterator(): \Iterator
500
    {
501 28
        if ($this->generator instanceof ArrayyRewindableGenerator) {
502 1
            $generator = clone $this->generator;
503 1
            $this->generator = new ArrayyRewindableExtendedGenerator(
504
                static function () use ($generator): \Generator {
505 1
                    yield from $generator;
506 1
                },
507 1
                null,
508 1
                static::class
509
            );
510
511 1
            return $this->generator;
512
        }
513
514 27
        $iterator = $this->getIteratorClass();
515
516 27
        if ($iterator === ArrayyIterator::class) {
517 27
            return new $iterator($this->toArray(), 0, static::class);
518
        }
519
520
        $return = new $iterator($this->toArray());
521
        \assert($return instanceof \Iterator);
522
523
        return $return;
524
    }
525
526
    /**
527
     * Gets the iterator classname for the ArrayObject.
528
     *
529
     * @return string
530
     *
531
     * @psalm-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
     * @psalm-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
     * @psalm-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
     * @psalm-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
     * @psalm-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
     * @psalm-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
     * @psalm-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
                    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 21
                $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
                    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 2
    public function serialize(): string
839
    {
840 2
        $this->generatorToArray();
841
842 2
        if (\PHP_VERSION_ID < 70400) {
843 2
            return parent::serialize();
844
        }
845
846
        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
     * @psalm-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
            $iteratorClass = '\\' . $iteratorClass;
870
            if (\class_exists($iteratorClass)) {
871
                /**
872
                 * @psalm-suppress PropertyTypeCoercion
873
                 */
874
                $this->iteratorClass = $iteratorClass;
875
876
                return;
877
            }
878
        }
879
880
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
881
    }
882
883
    /**
884
     * Sort the entries with a user-defined comparison function and maintain key association.
885
     *
886
     * @param callable $function
887
     *
888
     * @throws \InvalidArgumentException
889
     *
890
     * @return $this
891
     *               <p>(Mutable) Return this Arrayy object.</p>
892
     *
893
     * @psalm-return static<TKey,T>
894
     */
895 8 View Code Duplication
    public function uasort($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
896
    {
897 8
        if (!\is_callable($function)) {
898
            throw new \InvalidArgumentException('Passed function must be callable');
899
        }
900
901 8
        $this->generatorToArray();
902
903 8
        \uasort($this->array, $function);
904
905 8
        return $this;
906
    }
907
908
    /**
909
     * Sort the entries with a user-defined comparison function and maintain key association.
910
     *
911
     * @param callable $function
912
     *
913
     * @throws \InvalidArgumentException
914
     *
915
     * @return $this
916
     *               <p>(Immutable) Return this Arrayy object.</p>
917
     *
918
     * @psalm-return static<TKey,T>
919
     * @psalm-mutation-free
920
     */
921 4
    public function uasortImmutable($function): self
922
    {
923 4
        $that = clone $this;
924
925
        /**
926
         * @psalm-suppress ImpureMethodCall - object is already cloned
927
         */
928 4
        $that->uasort($function);
929
930 4
        return $that;
931
    }
932
933
    /**
934
     * Sort the entries by keys using a user-defined comparison function.
935
     *
936
     * @param callable $function
937
     *
938
     * @throws \InvalidArgumentException
939
     *
940
     * @return static
941
     *                <p>(Mutable) Return this Arrayy object.</p>
942
     *
943
     * @psalm-return static<TKey,T>
944
     */
945 5
    public function uksort($function): self
946
    {
947 5
        return $this->customSortKeys($function);
948
    }
949
950
    /**
951
     * Sort the entries by keys using a user-defined comparison function.
952
     *
953
     * @param callable $function
954
     *
955
     * @throws \InvalidArgumentException
956
     *
957
     * @return static
958
     *                <p>(Immutable) Return this Arrayy object.</p>
959
     *
960
     * @psalm-return static<TKey,T>
961
     * @psalm-mutation-free
962
     */
963 1
    public function uksortImmutable($function): self
964
    {
965 1
        return $this->customSortKeysImmutable($function);
966
    }
967
968
    /**
969
     * Unserialize an string and return the instance of the "Arrayy"-class.
970
     *
971
     * EXAMPLE: <code>
972
     * $serialized = a([1, 4, 7])->serialize();
973
     * a()->unserialize($serialized);
974
     * </code>
975
     *
976
     * @param string $string
977
     *
978
     * @return $this
979
     *
980
     * @psalm-return static<TKey,T>
981
     */
982 2
    public function unserialize($string): self
983
    {
984 2
        if (\PHP_VERSION_ID < 70400) {
985 2
            parent::unserialize($string);
986
987 2
            return $this;
988
        }
989
990
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
991
    }
992
993
    /**
994
     * Append a (key) + values to the current array.
995
     *
996
     * EXAMPLE: <code>
997
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
998
     * </code>
999
     *
1000
     * @param array $values
1001
     * @param mixed $key
1002
     *
1003
     * @return $this
1004
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
1005
     *
1006
     * @psalm-param  array<array-key,T> $values
1007
     * @psalm-param  TKey|null $key
1008
     * @psalm-return static<TKey,T>
1009
     */
1010 1
    public function appendArrayValues(array $values, $key = null)
1011
    {
1012 1
        $this->generatorToArray();
1013
1014 1
        if ($key !== null) {
1015
            if (
1016 1
                isset($this->array[$key])
1017
                &&
1018 1
                \is_array($this->array[$key])
1019
            ) {
1020 1
                foreach ($values as $value) {
1021 1
                    $this->array[$key][] = $value;
1022
                }
1023
            } else {
1024
                foreach ($values as $value) {
1025 1
                    $this->array[$key] = $value;
1026
                }
1027
            }
1028
        } else {
1029
            foreach ($values as $value) {
1030
                $this->array[] = $value;
1031
            }
1032
        }
1033
1034 1
        return $this;
1035
    }
1036
1037
    /**
1038
     * Add a suffix to each key.
1039
     *
1040
     * @param int|string $prefix
1041
     *
1042
     * @return static
1043
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
1044
     *
1045
     * @psalm-return static<TKey,T>
1046
     * @psalm-mutation-free
1047
     */
1048 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...
1049
    {
1050
        // init
1051 10
        $result = [];
1052
1053 10
        foreach ($this->getGenerator() as $key => $item) {
1054 9
            if ($item instanceof self) {
1055
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
1056 9
            } elseif (\is_array($item)) {
1057
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
1058
                    ->appendToEachKey($prefix)
1059
                    ->toArray();
1060
            } else {
1061 9
                $result[$prefix . $key] = $item;
1062
            }
1063
        }
1064
1065 10
        return self::create(
1066 10
            $result,
1067 10
            $this->iteratorClass,
1068 10
            false
1069
        );
1070
    }
1071
1072
    /**
1073
     * Add a prefix to each value.
1074
     *
1075
     * @param mixed $prefix
1076
     *
1077
     * @return static
1078
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1079
     *
1080
     * @psalm-return static<TKey,T>
1081
     * @psalm-mutation-free
1082
     */
1083 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...
1084
    {
1085
        // init
1086 10
        $result = [];
1087
1088 10
        foreach ($this->getGenerator() as $key => $item) {
1089 9
            if ($item instanceof self) {
1090
                $result[$key] = $item->appendToEachValue($prefix);
1091 9
            } elseif (\is_array($item)) {
1092
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1093 9
            } elseif (\is_object($item) === true) {
1094 1
                $result[$key] = $item;
1095
            } else {
1096 9
                $result[$key] = $prefix . $item;
1097
            }
1098
        }
1099
1100 10
        return self::create($result, $this->iteratorClass, false);
1101
    }
1102
1103
    /**
1104
     * Sort an array in reverse order and maintain index association.
1105
     *
1106
     * @return $this
1107
     *               <p>(Mutable) Return this Arrayy object.</p>
1108
     *
1109
     * @psalm-return static<TKey,T>
1110
     */
1111 4
    public function arsort(): self
1112
    {
1113 4
        $this->generatorToArray();
1114
1115 4
        \arsort($this->array);
1116
1117 4
        return $this;
1118
    }
1119
1120
    /**
1121
     * Sort an array in reverse order and maintain index association.
1122
     *
1123
     * @return $this
1124
     *               <p>(Immutable) Return this Arrayy object.</p>
1125
     *
1126
     * @psalm-return static<TKey,T>
1127
     * @psalm-mutation-free
1128
     */
1129 10
    public function arsortImmutable(): self
1130
    {
1131 10
        $that = clone $this;
1132
1133 10
        $that->generatorToArray();
1134
1135 10
        \arsort($that->array);
1136
1137 10
        return $that;
1138
    }
1139
1140
    /**
1141
     * Iterate over the current array and execute a callback for each loop.
1142
     *
1143
     * EXAMPLE: <code>
1144
     * $result = A::create();
1145
     * $closure = function ($value, $key) use ($result) {
1146
     *     $result[$key] = ':' . $value . ':';
1147
     * };
1148
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1149
     * </code>
1150
     *
1151
     * @param \Closure $closure
1152
     *
1153
     * @return static
1154
     *                <p>(Immutable)</p>
1155
     *
1156
     * @psalm-return static<TKey,T>
1157
     * @psalm-mutation-free
1158
     */
1159 3
    public function at(\Closure $closure): self
1160
    {
1161 3
        $that = clone $this;
1162
1163 3
        foreach ($that->getGenerator() as $key => $value) {
1164 3
            $closure($value, $key);
1165
        }
1166
1167 3
        return static::create(
1168 3
            $that->toArray(),
1169 3
            $this->iteratorClass,
1170 3
            false
1171
        );
1172
    }
1173
1174
    /**
1175
     * Returns the average value of the current array.
1176
     *
1177
     * EXAMPLE: <code>
1178
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1179
     * </code>
1180
     *
1181
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1182
     *
1183
     * @return float|int
1184
     *                   <p>The average value.</p>
1185
     * @psalm-mutation-free
1186
     */
1187 10
    public function average($decimals = 0)
1188
    {
1189 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1190
1191 10
        if (!$count) {
1192 2
            return 0;
1193
        }
1194
1195 8
        if ((int) $decimals !== $decimals) {
1196 3
            $decimals = 0;
1197
        }
1198
1199 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1200
    }
1201
1202
    /**
1203
     * Changes all keys in an array.
1204
     *
1205
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1206
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1207
     *
1208
     * @return static
1209
     *                <p>(Immutable)</p>
1210
     *
1211
     * @psalm-return static<TKey,T>
1212
     * @psalm-mutation-free
1213
     */
1214 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1215
    {
1216
        if (
1217 1
            $case !== \CASE_LOWER
1218
            &&
1219 1
            $case !== \CASE_UPPER
1220
        ) {
1221
            $case = \CASE_LOWER;
1222
        }
1223
1224 1
        $return = [];
1225 1
        foreach ($this->getGenerator() as $key => $value) {
1226 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1227
1228 1
            if ($case === \CASE_LOWER) {
1229 1
                $key = \mb_strtolower((string) $key);
1230
            } else {
1231 1
                $key = \mb_strtoupper((string) $key);
1232
            }
1233
1234 1
            $return[$key] = $value;
1235
        }
1236
1237 1
        return static::create(
1238 1
            $return,
1239 1
            $this->iteratorClass,
1240 1
            false
1241
        );
1242
    }
1243
1244
    /**
1245
     * Change the path separator of the array wrapper.
1246
     *
1247
     * By default, the separator is: "."
1248
     *
1249
     * @param string $separator <p>Separator to set.</p>
1250
     *
1251
     * @return $this
1252
     *               <p>(Mutable) Return this Arrayy object.</p>
1253
     *
1254
     * @psalm-return static<TKey,T>
1255
     */
1256 11
    public function changeSeparator($separator): self
1257
    {
1258 11
        $this->pathSeparator = $separator;
1259
1260 11
        return $this;
1261
    }
1262
1263
    /**
1264
     * Create a chunked version of the current array.
1265
     *
1266
     * EXAMPLE: <code>
1267
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1268
     * </code>
1269
     *
1270
     * @param int  $size         <p>Size of each chunk.</p>
1271
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1272
     *
1273
     * @return static
1274
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1275
     *
1276
     * @psalm-return static<TKey,T>
1277
     * @psalm-mutation-free
1278
     */
1279 5
    public function chunk($size, $preserveKeys = false): self
1280
    {
1281 5
        if ($preserveKeys) {
1282
            $generator = function () use ($size) {
1283
                $values = [];
1284
                $tmpCounter = 0;
1285
                foreach ($this->getGenerator() as $key => $value) {
1286
                    ++$tmpCounter;
1287
1288
                    $values[$key] = $value;
1289
                    if ($tmpCounter === $size) {
1290
                        yield $values;
1291
1292
                        $values = [];
1293
                        $tmpCounter = 0;
1294
                    }
1295
                }
1296
1297
                if ($values !== []) {
1298
                    yield $values;
1299
                }
1300
            };
1301 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1302
            $generator = function () use ($size) {
1303 5
                $values = [];
1304 5
                $tmpCounter = 0;
1305 5
                foreach ($this->getGenerator() as $key => $value) {
1306 5
                    ++$tmpCounter;
1307
1308 5
                    $values[] = $value;
1309 5
                    if ($tmpCounter === $size) {
1310 5
                        yield $values;
1311
1312 5
                        $values = [];
1313 5
                        $tmpCounter = 0;
1314
                    }
1315
                }
1316
1317 5
                if ($values !== []) {
1318 4
                    yield $values;
1319
                }
1320 5
            };
1321
        }
1322
1323 5
        return static::create(
1324 5
            $generator,
1325 5
            $this->iteratorClass,
1326 5
            false
1327
        );
1328
    }
1329
1330
    /**
1331
     * Clean all falsy values from the current array.
1332
     *
1333
     * EXAMPLE: <code>
1334
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1335
     * </code>
1336
     *
1337
     * @return static
1338
     *                <p>(Immutable)</p>
1339
     *
1340
     * @psalm-return static<TKey,T>
1341
     * @psalm-mutation-free
1342
     */
1343 8
    public function clean(): self
1344
    {
1345 8
        return $this->filter(
1346
            static function ($value) {
1347 7
                return (bool) $value;
1348 8
            }
1349
        );
1350
    }
1351
1352
    /**
1353
     * WARNING!!! -> Clear the current full array or a $key of it.
1354
     *
1355
     * EXAMPLE: <code>
1356
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1357
     * </code>
1358
     *
1359
     * @param int|int[]|string|string[]|null $key
1360
     *
1361
     * @return $this
1362
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1363
     *
1364
     * @psalm-return static<TKey,T>
1365
     */
1366 10
    public function clear($key = null): self
1367
    {
1368 10
        if ($key !== null) {
1369 3
            if (\is_array($key)) {
1370 1
                foreach ($key as $keyTmp) {
1371 1
                    $this->offsetUnset($keyTmp);
1372
                }
1373
            } else {
1374 2
                $this->offsetUnset($key);
1375
            }
1376
1377 3
            return $this;
1378
        }
1379
1380 7
        $this->array = [];
1381 7
        $this->generator = null;
1382
1383 7
        return $this;
1384
    }
1385
1386
    /**
1387
     * Check if an item is in the current array.
1388
     *
1389
     * EXAMPLE: <code>
1390
     * a([1, true])->contains(true); // true
1391
     * </code>
1392
     *
1393
     * @param float|int|string $value
1394
     * @param bool             $recursive
1395
     * @param bool             $strict
1396
     *
1397
     * @return bool
1398
     * @psalm-mutation-free
1399
     */
1400 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1401
    {
1402 23
        if ($recursive === true) {
1403 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1404
        }
1405
1406
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1407 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1408 11
            if ($strict) {
1409 11
                if ($value === $valueFromArray) {
1410 11
                    return true;
1411
                }
1412
            } else {
1413
                /** @noinspection NestedPositiveIfStatementsInspection */
1414
                if ($value == $valueFromArray) {
1415 7
                    return true;
1416
                }
1417
            }
1418
        }
1419
1420 7
        return false;
1421
    }
1422
1423
    /**
1424
     * Check if an (case-insensitive) string is in the current array.
1425
     *
1426
     * EXAMPLE: <code>
1427
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1428
     * </code>
1429
     *
1430
     * @param mixed $value
1431
     * @param bool  $recursive
1432
     *
1433
     * @return bool
1434
     * @psalm-mutation-free
1435
     *
1436
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1437
     */
1438 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1439
    {
1440 26
        if ($value === null) {
1441 2
            return false;
1442
        }
1443
1444 24
        if ($recursive === true) {
1445
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1446 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1447 22
                if (\is_array($valueTmp)) {
1448 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1449 5
                    if ($return === true) {
1450 5
                        return $return;
1451
                    }
1452 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1453 22
                    return true;
1454
                }
1455
            }
1456
1457 8
            return false;
1458
        }
1459
1460
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1461 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1462 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1463 11
                return true;
1464
            }
1465
        }
1466
1467 4
        return false;
1468
    }
1469
1470
    /**
1471
     * Check if the given key/index exists in the array.
1472
     *
1473
     * EXAMPLE: <code>
1474
     * a([1 => true])->containsKey(1); // true
1475
     * </code>
1476
     *
1477
     * @param int|string $key <p>key/index to search for</p>
1478
     *
1479
     * @return bool
1480
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1481
     *
1482
     * @psalm-mutation-free
1483
     */
1484 4
    public function containsKey($key): bool
1485
    {
1486 4
        return $this->offsetExists($key);
1487
    }
1488
1489
    /**
1490
     * Check if all given needles are present in the array as key/index.
1491
     *
1492
     * EXAMPLE: <code>
1493
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1494
     * </code>
1495
     *
1496
     * @param array $needles   <p>The keys you are searching for.</p>
1497
     * @param bool  $recursive
1498
     *
1499
     * @return bool
1500
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1501
     *
1502
     * @psalm-param array<array-key>|array<TKey> $needles
1503
     * @psalm-mutation-free
1504
     */
1505 2
    public function containsKeys(array $needles, $recursive = false): bool
1506
    {
1507 2
        if ($recursive === true) {
1508
            return
1509 2
                \count(
1510 2
                    \array_intersect(
1511 2
                        $needles,
1512 2
                        $this->keys(true)->toArray()
1513
                    ),
1514 2
                    \COUNT_RECURSIVE
1515
                )
1516
                ===
1517 2
                \count(
1518 2
                    $needles,
1519 2
                    \COUNT_RECURSIVE
1520
                );
1521
        }
1522
1523 1
        return \count(
1524 1
            \array_intersect($needles, $this->keys()->toArray()),
1525 1
            \COUNT_NORMAL
1526
        )
1527
                ===
1528 1
                \count(
1529 1
                    $needles,
1530 1
                    \COUNT_NORMAL
1531
                );
1532
    }
1533
1534
    /**
1535
     * Check if all given needles are present in the array as key/index.
1536
     *
1537
     * @param array $needles <p>The keys you are searching for.</p>
1538
     *
1539
     * @return bool
1540
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1541
     *
1542
     * @psalm-param array<array-key>|array<TKey> $needles
1543
     * @psalm-mutation-free
1544
     */
1545 1
    public function containsKeysRecursive(array $needles): bool
1546
    {
1547 1
        return $this->containsKeys($needles, true);
1548
    }
1549
1550
    /**
1551
     * alias: for "Arrayy->contains()"
1552
     *
1553
     * @param float|int|string $value
1554
     *
1555
     * @return bool
1556
     *
1557
     * @see Arrayy::contains()
1558
     * @psalm-mutation-free
1559
     */
1560 9
    public function containsValue($value): bool
1561
    {
1562 9
        return $this->contains($value);
1563
    }
1564
1565
    /**
1566
     * alias: for "Arrayy->contains($value, true)"
1567
     *
1568
     * @param float|int|string $value
1569
     *
1570
     * @return bool
1571
     *
1572
     * @see Arrayy::contains()
1573
     * @psalm-mutation-free
1574
     */
1575 18
    public function containsValueRecursive($value): bool
1576
    {
1577 18
        return $this->contains($value, true);
1578
    }
1579
1580
    /**
1581
     * Check if all given needles are present in the array.
1582
     *
1583
     * EXAMPLE: <code>
1584
     * a([1, true])->containsValues(array(1, true)); // true
1585
     * </code>
1586
     *
1587
     * @param array $needles
1588
     *
1589
     * @return bool
1590
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1591
     *
1592
     * @psalm-param array<mixed>|array<T> $needles
1593
     * @psalm-mutation-free
1594
     */
1595 1
    public function containsValues(array $needles): bool
1596
    {
1597 1
        return \count(
1598 1
            \array_intersect(
1599 1
                $needles,
1600 1
                $this->toArray()
1601
            ),
1602 1
            \COUNT_NORMAL
1603
        )
1604
               ===
1605 1
               \count(
1606 1
                   $needles,
1607 1
                   \COUNT_NORMAL
1608
               );
1609
    }
1610
1611
    /**
1612
     * Counts all the values of an array
1613
     *
1614
     * @see          http://php.net/manual/en/function.array-count-values.php
1615
     *
1616
     * @return static
1617
     *                <p>
1618
     *                (Immutable)
1619
     *                An associative Arrayy-object of values from input as
1620
     *                keys and their count as value.
1621
     *                </p>
1622
     *
1623
     * @psalm-return static<TKey,T>
1624
     * @psalm-mutation-free
1625
     */
1626 7
    public function countValues(): self
1627
    {
1628 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1629
    }
1630
1631
    /**
1632
     * Creates an Arrayy object.
1633
     *
1634
     * @param mixed  $data
1635
     * @param string $iteratorClass
1636
     * @param bool   $checkPropertiesInConstructor
1637
     *
1638
     * @return static
1639
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1640
     *
1641
     * @psalm-param  array<array-key,T>|\Traversable<array-key,T>|callable():\Generator<TKey,T>|(T&\Traversable) $data
1642
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1643
     * @psalm-return static<TKey,T>
1644
     * @psalm-mutation-free
1645
     */
1646 729
    public static function create(
1647
        $data = [],
1648
        string $iteratorClass = ArrayyIterator::class,
1649
        bool $checkPropertiesInConstructor = true
1650
    ) {
1651 729
        return new static(
1652 729
            $data,
1653 729
            $iteratorClass,
1654 729
            $checkPropertiesInConstructor
1655
        );
1656
    }
1657
1658
    /**
1659
     * Flatten an array with the given character as a key delimiter.
1660
     *
1661
     * EXAMPLE: <code>
1662
     * $dot = a(['foo' => ['abc' => 'xyz', 'bar' => ['baz']]]);
1663
     * $flatten = $dot->flatten();
1664
     * $flatten['foo.abc']; // 'xyz'
1665
     * $flatten['foo.bar.0']; // 'baz'
1666
     * </code>
1667
     *
1668
     * @param string     $delimiter
1669
     * @param string     $prepend
1670
     * @param array|null $items
1671
     *
1672
     * @return array
1673
     */
1674 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1675
    {
1676
        // init
1677 2
        $flatten = [];
1678
1679 2
        if ($items === null) {
1680 2
            $items = $this->getArray();
1681
        }
1682
1683 2
        foreach ($items as $key => $value) {
1684 2
            if (\is_array($value) && $value !== []) {
1685 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1686
            } else {
1687 2
                $flatten[] = [$prepend . $key => $value];
1688
            }
1689
        }
1690
1691 2
        if (\count($flatten) === 0) {
1692
            return [];
1693
        }
1694
1695 2
        return \array_merge_recursive([], ...$flatten);
1696
    }
1697
1698
    /**
1699
     * WARNING: Creates an Arrayy object by reference.
1700
     *
1701
     * @param array $array
1702
     *
1703
     * @return $this
1704
     *               <p>(Mutable) Return this Arrayy object.</p>
1705
     *
1706
     * @psalm-param  array<TKey,T> $array
1707
     * @psalm-return $this<TKey,T>
1708
     *
1709
     * @internal this will not check any types because it's set directly as reference
1710
     */
1711 26
    public function createByReference(array &$array = []): self
1712
    {
1713 26
        $this->array = &$array;
1714 26
        $this->generator = null;
1715
1716 26
        return $this;
1717
    }
1718
1719
    /**
1720
     * Create an new instance from a callable function which will return an Generator.
1721
     *
1722
     * @param callable $generatorFunction
1723
     *
1724
     * @return static
1725
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1726
     *
1727
     * @psalm-param callable():\Generator<TKey,T> $generatorFunction
1728
     * @psalm-return static<TKey,T>
1729
     * @psalm-mutation-free
1730
     */
1731 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1732
    {
1733 7
        return self::create($generatorFunction);
1734
    }
1735
1736
    /**
1737
     * Create an new instance filled with a copy of values from a "Generator"-object.
1738
     *
1739
     * @param \Generator $generator
1740
     *
1741
     * @return static
1742
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1743
     *
1744
     * @psalm-param \Generator<TKey,T> $generator
1745
     * @psalm-return static<TKey,T>
1746
     * @psalm-mutation-free
1747
     */
1748 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1749
    {
1750 4
        return self::create(\iterator_to_array($generator, true));
1751
    }
1752
1753
    /**
1754
     * Create an new Arrayy object via JSON.
1755
     *
1756
     * @param string $json
1757
     *
1758
     * @return static
1759
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1760
     *
1761
     * @psalm-return static<mixed,mixed>
1762
     * @psalm-mutation-free
1763
     */
1764 5
    public static function createFromJson(string $json): self
1765
    {
1766 5
        return static::create(\json_decode($json, true));
1767
    }
1768
1769
    /**
1770
     * Create an new Arrayy object via JSON.
1771
     *
1772
     * @param array $array
1773
     *
1774
     * @return static
1775
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1776
     *
1777
     * @psalm-param array<TKey,T> $array
1778
     * @psalm-return static<TKey,T>
1779
     * @psalm-mutation-free
1780
     */
1781 1
    public static function createFromArray(array $array): self
1782
    {
1783 1
        return static::create($array);
1784
    }
1785
1786
    /**
1787
     * Create an new instance filled with values from an object that is iterable.
1788
     *
1789
     * @param \Traversable $object <p>iterable object</p>
1790
     *
1791
     * @return static
1792
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1793
     *
1794
     * @psalm-param \Traversable<array-key,T> $object
1795
     * @psalm-return static<array-key,T>
1796
     * @psalm-mutation-free
1797
     */
1798 4
    public static function createFromObject(\Traversable $object): self
1799
    {
1800
        // init
1801 4
        $arrayy = new static();
1802
1803 4
        if ($object instanceof self) {
1804 4
            $objectArray = $object->getGenerator();
1805
        } else {
1806
            $objectArray = $object;
1807
        }
1808
1809 4
        foreach ($objectArray as $key => $value) {
1810
            /**
1811
             * @psalm-suppress ImpureMethodCall - object is already re-created
1812
             */
1813 3
            $arrayy->internalSet($key, $value);
1814
        }
1815
1816 4
        return $arrayy;
1817
    }
1818
1819
    /**
1820
     * Create an new instance filled with values from an object.
1821
     *
1822
     * @param object $object
1823
     *
1824
     * @return static
1825
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1826
     *
1827
     * @psalm-return static<array-key,mixed>
1828
     * @psalm-mutation-free
1829
     */
1830 5
    public static function createFromObjectVars($object): self
1831
    {
1832 5
        return self::create(self::objectToArray($object));
1833
    }
1834
1835
    /**
1836
     * Create an new Arrayy object via string.
1837
     *
1838
     * @param string      $str       <p>The input string.</p>
1839
     * @param string|null $delimiter <p>The boundary string.</p>
1840
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1841
     *                               used.</p>
1842
     *
1843
     * @return static
1844
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1845
     *
1846
     * @psalm-return static<int,string>
1847
     * @psalm-mutation-free
1848
     */
1849 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1850
    {
1851 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1852 1
            \preg_match_all($regEx, $str, $array);
1853
1854 1
            if (!empty($array)) {
1855 1
                $array = $array[0];
1856
            }
1857
        } else {
1858
            /** @noinspection NestedPositiveIfStatementsInspection */
1859 9
            if ($delimiter !== null) {
1860 7
                $array = \explode($delimiter, $str);
1861
            } else {
1862 2
                $array = [$str];
1863
            }
1864
        }
1865
1866
        // trim all string in the array
1867
        /**
1868
         * @psalm-suppress MissingClosureParamType
1869
         */
1870 10
        \array_walk(
1871 10
            $array,
1872
            static function (&$val) {
1873 10
                if ((string) $val === $val) {
1874 10
                    $val = \trim($val);
1875
                }
1876 10
            }
1877
        );
1878
1879 10
        return static::create($array);
1880
    }
1881
1882
    /**
1883
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1884
     *
1885
     * @param \Traversable $traversable
1886
     * @param bool         $use_keys    [optional] <p>
1887
     *                                  Whether to use the iterator element keys as index.
1888
     *                                  </p>
1889
     *
1890
     * @return static
1891
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1892
     *
1893
     * @psalm-param \Traversable<array-key|TKey,T> $traversable
1894
     * @psalm-return static<int|TKey,T>
1895
     * @psalm-mutation-free
1896
     */
1897 1
    public static function createFromTraversableImmutable(\Traversable $traversable, bool $use_keys = true): self
1898
    {
1899 1
        return self::create(\iterator_to_array($traversable, $use_keys));
1900
    }
1901
1902
    /**
1903
     * Create an new instance containing a range of elements.
1904
     *
1905
     * @param float|int|string $low  <p>First value of the sequence.</p>
1906
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1907
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1908
     *
1909
     * @return static
1910
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1911
     *
1912
     * @psalm-return static<int,int|string>
1913
     * @psalm-mutation-free
1914
     */
1915 2
    public static function createWithRange($low, $high, $step = 1): self
1916
    {
1917 2
        return static::create(\range($low, $high, $step));
1918
    }
1919
1920
    /**
1921
     * Gets the element of the array at the current internal iterator position.
1922
     *
1923
     * @return false|mixed
1924
     *
1925
     * @psalm-return false|T
1926
     */
1927
    public function current()
1928
    {
1929
        if ($this->generator) {
1930
            return $this->generator->current();
1931
        }
1932
1933
        return \current($this->array);
1934
    }
1935
1936
    /**
1937
     * Custom sort by index via "uksort".
1938
     *
1939
     * EXAMPLE: <code>
1940
     * $callable = function ($a, $b) {
1941
     *     if ($a == $b) {
1942
     *         return 0;
1943
     *     }
1944
     *     return ($a > $b) ? 1 : -1;
1945
     * };
1946
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1947
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1948
     * </code>
1949
     *
1950
     * @see          http://php.net/manual/en/function.uksort.php
1951
     *
1952
     * @param callable $function
1953
     *
1954
     * @throws \InvalidArgumentException
1955
     *
1956
     * @return $this
1957
     *               <p>(Mutable) Return this Arrayy object.</p>
1958
     *
1959
     * @psalm-return static<TKey,T>
1960
     */
1961 5
    public function customSortKeys(callable $function): self
1962
    {
1963 5
        $this->generatorToArray();
1964
1965 5
        \uksort($this->array, $function);
1966
1967 5
        return $this;
1968
    }
1969
1970
    /**
1971
     * Custom sort by index via "uksort".
1972
     *
1973
     * @see          http://php.net/manual/en/function.uksort.php
1974
     *
1975
     * @param callable $function
1976
     *
1977
     * @throws \InvalidArgumentException
1978
     *
1979
     * @return $this
1980
     *               <p>(Immutable) Return this Arrayy object.</p>
1981
     *
1982
     * @psalm-return static<TKey,T>
1983
     * @psalm-mutation-free
1984
     */
1985 1
    public function customSortKeysImmutable(callable $function): self
1986
    {
1987 1
        $that = clone $this;
1988
1989 1
        $that->generatorToArray();
1990
1991
        /**
1992
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1993
         */
1994 1
        \uksort($that->array, $function);
1995
1996 1
        return $that;
1997
    }
1998
1999
    /**
2000
     * Custom sort by value via "usort".
2001
     *
2002
     * EXAMPLE: <code>
2003
     * $callable = function ($a, $b) {
2004
     *     if ($a == $b) {
2005
     *         return 0;
2006
     *     }
2007
     *     return ($a > $b) ? 1 : -1;
2008
     * };
2009
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
2010
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
2011
     * </code>
2012
     *
2013
     * @see          http://php.net/manual/en/function.usort.php
2014
     *
2015
     * @param callable $function
2016
     *
2017
     * @throws \InvalidArgumentException
2018
     *
2019
     * @return $this
2020
     *               <p>(Mutable) Return this Arrayy object.</p>
2021
     *
2022
     * @psalm-return static<TKey,T>
2023
     */
2024 10 View Code Duplication
    public function customSortValues($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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