Completed
Push — master ( 159245...061f16 )
by Lars
01:34
created

Arrayy::replaceValues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 12
ccs 4
cts 4
cp 1
crap 1
rs 9.8666
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|null,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
    /**
32
     * @var array
33
     *
34
     * @psalm-var array<mixed|TKey,T>
35
     */
36
    protected $array = [];
37
38
    /**
39
     * @var \Arrayy\ArrayyRewindableGenerator|null
40
     *
41
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
42
     */
43
    protected $generator;
44
45
    /**
46
     * @var string
47
     *
48
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
49
     */
50
    protected $iteratorClass = ArrayyIterator::class;
51
52
    /**
53
     * @var string
54
     */
55
    protected $pathSeparator = '.';
56
57
    /**
58
     * @var bool
59
     */
60
    protected $checkPropertyTypes = false;
61
62
    /**
63
     * @var bool
64
     */
65
    protected $checkForMissingPropertiesInConstructor = false;
66
67
    /**
68
     * @var bool
69
     */
70
    protected $checkPropertiesMismatchInConstructor = false;
71
72
    /**
73
     * @var bool
74
     */
75
    protected $checkPropertiesMismatch = true;
76
77
    /**
78
     * @var array<int|string,TypeCheckInterface>|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
79
     */
80
    protected $properties = [];
81
82
    /**
83
     * Initializes
84
     *
85
     * @param mixed  $data                         <p>
86
     *                                             Should be an array or a generator, otherwise it will try
87
     *                                             to convert it into an array.
88
     *                                             </p>
89
     * @param string $iteratorClass                optional <p>
90
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
91
     *                                             need this option.
92
     *                                             </p>
93
     * @param bool   $checkPropertiesInConstructor optional <p>
94
     *                                             You need to extend the "Arrayy"-class and you need to set
95
     *                                             the $checkPropertiesMismatchInConstructor class property
96
     *                                             to
97
     *                                             true, otherwise this option didn't not work anyway.
98
     *                                             </p>
99
     *
100
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
101
     */
102 1100
    public function __construct(
103
        $data = [],
104
        string $iteratorClass = ArrayyIterator::class,
105
        bool $checkPropertiesInConstructor = true
106
    ) {
107 1100
        $data = $this->fallbackForArray($data);
108
109
        // used only for serialize + unserialize, all other methods are overwritten
110 1098
        parent::__construct([], 0, $iteratorClass);
111
112 1098
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
113
114 1091
        $this->setIteratorClass($iteratorClass);
115 1091
    }
116
117
    /**
118
     * @return void
119
     */
120 47
    public function __clone()
121
    {
122 47
        if (!\is_array($this->properties)) {
123
            $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...
124
        }
125
126 47
        if ($this->generator !== null) {
127
            $this->generator = clone $this->generator;
128
        }
129 47
    }
130
131
    /**
132
     * Call object as function.
133
     *
134
     * @param mixed $key
135
     *
136
     * @return mixed
137
     */
138 1
    public function __invoke($key = null)
139
    {
140 1
        if ($key !== null) {
141 1
            $this->generatorToArray();
142
143 1
            return $this->array[$key] ?? false;
144
        }
145
146
        return $this->toArray();
147
    }
148
149
    /**
150
     * Whether or not an element exists by key.
151
     *
152
     * @param mixed $key
153
     *
154
     * @return bool
155
     *              <p>True is the key/index exists, otherwise false.</p>
156
     */
157
    public function __isset($key): bool
158
    {
159
        return $this->offsetExists($key);
160
    }
161
162
    /**
163
     * Assigns a value to the specified element.
164
     *
165
     * @param mixed $key
166
     * @param mixed $value
167
     *
168
     * @return void
169
     */
170 2
    public function __set($key, $value)
171
    {
172 2
        $this->internalSet($key, $value);
173 2
    }
174
175
    /**
176
     * magic to string
177
     *
178
     * @return string
179
     */
180 15
    public function __toString(): string
181
    {
182 15
        return $this->toString();
183
    }
184
185
    /**
186
     * Unset element by key.
187
     *
188
     * @param mixed $key
189
     */
190
    public function __unset($key)
191
    {
192
        $this->internalRemove($key);
193
    }
194
195
    /**
196
     * Get a value by key.
197
     *
198
     * @param mixed $key
199
     *
200
     * @return mixed
201
     *               <p>Get a Value from the current array.</p>
202
     */
203 4
    public function &__get($key)
204
    {
205 4
        $return = $this->get($key);
206
207 4
        if (\is_array($return) === true) {
208
            return static::create($return, $this->iteratorClass, false);
209
        }
210
211 4
        return $return;
212
    }
213
214
    /**
215
     * alias: for "Arrayy->append()"
216
     *
217
     * @param mixed $value
218
     *
219
     * @return static
220
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
221
     *
222
     * @see          Arrayy::append()
223
     *
224
     * @psalm-param  T $value
225
     * @psalm-return static<TKey,T>
226
     */
227 3
    public function add($value)
228
    {
229 3
        return $this->append($value);
230
    }
231
232
    /**
233
     * Append a (key) + value to the current array.
234
     *
235
     * @param mixed $value
236
     * @param mixed $key
237
     *
238
     * @return $this
239
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
240
     *
241
     * @psalm-return static<TKey,T>
242
     */
243 13
    public function append($value, $key = null): self
244
    {
245 13
        $this->generatorToArray();
246
247 13
        if ($this->properties !== []) {
248 4
            $this->checkType($key, $value);
249
        }
250
251 12
        if ($key !== null) {
252
            if (
253
                isset($this->array[$key])
254
                &&
255
                \is_array($this->array[$key]) === true
256
            ) {
257
                $this->array[$key][] = $value;
258
            } else {
259
                $this->array[$key] = $value;
260
            }
261
        } else {
262 12
            $this->array[] = $value;
263
        }
264
265 12
        return $this;
266
    }
267
268
    /**
269
     * Sort the entries by value.
270
     *
271
     * @param int $sort_flags [optional] <p>
272
     *                        You may modify the behavior of the sort using the optional
273
     *                        parameter sort_flags, for details
274
     *                        see sort.
275
     *                        </p>
276
     *
277
     * @return $this
278
     *               <p>(Mutable) Return this Arrayy object.</p>
279
     *
280
     * @psalm-return static<TKey,T>
281
     */
282 4
    public function asort(int $sort_flags = 0): self
283
    {
284 4
        $this->generatorToArray();
285
286 4
        \asort($this->array, $sort_flags);
287
288 4
        return $this;
289
    }
290
291
    /**
292
     * Sort the entries by value.
293
     *
294
     * @param int $sort_flags [optional] <p>
295
     *                        You may modify the behavior of the sort using the optional
296
     *                        parameter sort_flags, for details
297
     *                        see sort.
298
     *                        </p>
299
     *
300
     * @return $this
301
     *               <p>(Immutable) Return this Arrayy object.</p>
302
     *
303
     * @psalm-return static<TKey,T>
304
     * @psalm-mutation-free
305
     */
306 4
    public function asortImmutable(int $sort_flags = 0): self
307
    {
308 4
        $that = clone $this;
309
310 4
        $that->asort($sort_flags);
311
312 4
        return $that;
313
    }
314
315
    /**
316
     * Counts all elements in an array, or something in an object.
317
     *
318
     * <p>
319
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
320
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
321
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
322
     * implemented and used in PHP.
323
     * </p>
324
     *
325
     * @see http://php.net/manual/en/function.count.php
326
     *
327
     * @param int $mode [optional] If the optional mode parameter is set to
328
     *                  COUNT_RECURSIVE (or 1), count
329
     *                  will recursively count the array. This is particularly useful for
330
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
331
     *
332
     * @return int
333
     *             <p>
334
     *             The number of elements in var, which is
335
     *             typically an array, since anything else will have one
336
     *             element.
337
     *             </p>
338
     *             <p>
339
     *             If var is not an array or an object with
340
     *             implemented Countable interface,
341
     *             1 will be returned.
342
     *             There is one exception, if var is &null;,
343
     *             0 will be returned.
344
     *             </p>
345
     *             <p>
346
     *             Caution: count may return 0 for a variable that isn't set,
347
     *             but it may also return 0 for a variable that has been initialized with an
348
     *             empty array. Use isset to test if a variable is set.
349
     *             </p>
350
     * @psalm-mutation-free
351
     */
352 145
    public function count(int $mode = \COUNT_NORMAL): int
353
    {
354
        if (
355 145
            $this->generator
356
            &&
357 145
            $mode === \COUNT_NORMAL
358
        ) {
359 4
            return \iterator_count($this->generator);
360
        }
361
362 141
        return \count($this->toArray(), $mode);
363
    }
364
365
    /**
366
     * Exchange the array for another one.
367
     *
368
     * @param array|static $data
369
     *
370
     * @return array
371
     *
372
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
373
     * @psalm-return array<TKey,T>
374
     */
375 1
    public function exchangeArray($data): array
376
    {
377 1
        $this->array = $this->fallbackForArray($data);
378
379 1
        return $this->array;
380
    }
381
382
    /**
383
     * Creates a copy of the ArrayyObject.
384
     *
385
     * @return array
386
     *
387
     * @psalm-return array<TKey,T>
388
     */
389 5
    public function getArrayCopy(): array
390
    {
391 5
        $this->generatorToArray();
392
393 5
        return $this->array;
394
    }
395
396
    /**
397
     * Returns a new iterator, thus implementing the \Iterator interface.
398
     *
399
     * @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...
400
     *                          <p>An iterator for the values in the array.</p>
401
     */
402 24
    public function getIterator(): \Iterator
403
    {
404 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
405 1
            return $this->generator;
406
        }
407
408 23
        $iterator = $this->getIteratorClass();
409
410 23
        if ($iterator === ArrayyIterator::class) {
411 23
            return new $iterator($this->toArray(), 0, static::class);
412
        }
413
414
        return new $iterator($this->toArray());
415
    }
416
417
    /**
418
     * Gets the iterator classname for the ArrayObject.
419
     *
420
     * @return string
421
     *
422
     * @psalm-return class-string
423
     */
424 23
    public function getIteratorClass(): string
425
    {
426 23
        return $this->iteratorClass;
427
    }
428
429
    /**
430
     * Sort the entries by key.
431
     *
432
     * @param int $sort_flags [optional] <p>
433
     *                        You may modify the behavior of the sort using the optional
434
     *                        parameter sort_flags, for details
435
     *                        see sort.
436
     *                        </p>
437
     *
438
     * @return $this
439
     *               <p>(Mutable) Return this Arrayy object.</p>
440
     *
441
     * @psalm-return static<TKey,T>
442
     */
443 4
    public function ksort(int $sort_flags = 0): self
444
    {
445 4
        $this->generatorToArray();
446
447 4
        \ksort($this->array, $sort_flags);
448
449 4
        return $this;
450
    }
451
452
    /**
453
     * Sort the entries by key.
454
     *
455
     * @param int $sort_flags [optional] <p>
456
     *                        You may modify the behavior of the sort using the optional
457
     *                        parameter sort_flags, for details
458
     *                        see sort.
459
     *                        </p>
460
     *
461
     * @return $this
462
     *               <p>(Immutable) Return this Arrayy object.</p>
463
     *
464
     * @psalm-return static<TKey,T>
465
     */
466 4
    public function ksortImmutable(int $sort_flags = 0): self
467
    {
468 4
        $that = clone $this;
469
470
        /**
471
         * @psalm-suppress ImpureMethodCall - object is already cloned
472
         */
473 4
        $that->ksort($sort_flags);
474
475 4
        return $that;
476
    }
477
478
    /**
479
     * Sort an array using a case insensitive "natural order" algorithm.
480
     *
481
     * @return $this
482
     *               <p>(Mutable) Return this Arrayy object.</p>
483
     *
484
     * @psalm-return static<TKey,T>
485
     */
486 8
    public function natcasesort(): self
487
    {
488 8
        $this->generatorToArray();
489
490 8
        \natcasesort($this->array);
491
492 8
        return $this;
493
    }
494
495
    /**
496
     * Sort an array using a case insensitive "natural order" algorithm.
497
     *
498
     * @return $this
499
     *               <p>(Immutable) Return this Arrayy object.</p>
500
     *
501
     * @psalm-return static<TKey,T>
502
     * @psalm-mutation-free
503
     */
504 4
    public function natcasesortImmutable(): self
505
    {
506 4
        $that = clone $this;
507
508 4
        $that->natcasesort();
509
510 4
        return $that;
511
    }
512
513
    /**
514
     * Sort entries using a "natural order" algorithm.
515
     *
516
     * @return $this
517
     *               <p>(Mutable) Return this Arrayy object.</p>
518
     *
519
     * @psalm-return static<TKey,T>
520
     */
521 9
    public function natsort(): self
522
    {
523 9
        $this->generatorToArray();
524
525 9
        \natsort($this->array);
526
527 9
        return $this;
528
    }
529
530
    /**
531
     * Sort entries using a "natural order" algorithm.
532
     *
533
     * @return $this
534
     *               <p>(Immutable) Return this Arrayy object.</p>
535
     *
536
     * @psalm-return static<TKey,T>
537
     * @psalm-mutation-free
538
     */
539 4
    public function natsortImmutable(): self
540
    {
541 4
        $that = clone $this;
542
543 4
        $that->natsort();
544
545 4
        return $that;
546
    }
547
548
    /**
549
     * Whether or not an offset exists.
550
     *
551
     * @param bool|int|string $offset
552
     *
553
     * @return bool
554
     *
555
     * @noinspection PhpSillyAssignmentInspection
556
     *
557
     * @psalm-mutation-free
558
     */
559 130
    public function offsetExists($offset): bool
560
    {
561 130
        $this->generatorToArray();
562
563 130
        if ($this->array === []) {
564 5
            return false;
565
        }
566
567
        // php cast "bool"-index into "int"-index
568 125
        if ((bool) $offset === $offset) {
569 1
            $offset = (int) $offset;
570
        }
571
572
        /** @var int|string $offset - hint for phpstan */
573 125
        $offset = $offset;
0 ignored issues
show
Bug introduced by
Why assign $offset to itself?

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

This assignement can be removed without consequences.

Loading history...
574
575 125
        $tmpReturn = $this->keyExists($offset);
576
577
        if (
578 125
            $tmpReturn === true
579
            ||
580
            (
581 92
                $tmpReturn === false
582
                &&
583 125
                \strpos((string) $offset, $this->pathSeparator) === false
584
            )
585
        ) {
586 123
            return $tmpReturn;
587
        }
588
589 3
        $offsetExists = false;
590
591
        if (
592 3
            $this->pathSeparator
593
            &&
594 3
            (string) $offset === $offset
595
            &&
596 3
            \strpos($offset, $this->pathSeparator) !== false
597
        ) {
598 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
599 3
            if ($explodedPath !== false) {
600 3
                $lastOffset = \array_pop($explodedPath);
601 3
                if ($lastOffset !== null) {
602 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
603
604 3
                    $this->callAtPath(
605 3
                        $containerPath,
606
                        static function ($container) use ($lastOffset, &$offsetExists) {
607 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
608 3
                        }
609
                    );
610
                }
611
            }
612
        }
613
614 3
        return $offsetExists;
615
    }
616
617
    /**
618
     * Returns the value at specified offset.
619
     *
620
     * @param int|string $offset
621
     *
622
     * @return mixed
623
     *               <p>Will return null if the offset did not exists.</p>
624
     */
625 101
    public function offsetGet($offset)
626
    {
627 101
        return $this->offsetExists($offset) ? $this->get($offset) : null;
628
    }
629
630
    /**
631
     * Assigns a value to the specified offset + check the type.
632
     *
633
     * @param int|string|null $offset
634
     * @param mixed           $value
635
     *
636
     * @return void
637
     */
638 18
    public function offsetSet($offset, $value)
639
    {
640 18
        $this->generatorToArray();
641
642 18
        if ($offset === null) {
643 5
            if ($this->properties !== []) {
644 1
                $this->checkType(null, $value);
645
            }
646
647 4
            $this->array[] = $value;
648
        } else {
649 13
            $this->internalSet(
650 13
                $offset,
651 13
                $value,
652 13
                true
653
            );
654
        }
655 17
    }
656
657
    /**
658
     * Unset an offset.
659
     *
660
     * @param int|string $offset
661
     *
662
     * @return void
663
     */
664 12
    public function offsetUnset($offset)
665
    {
666 12
        $this->generatorToArray();
667
668 12
        if ($this->array === []) {
669 3
            return;
670
        }
671
672 10
        if ($this->keyExists($offset)) {
673 7
            unset($this->array[$offset]);
674
675 7
            return;
676
        }
677
678
        if (
679 5
            $this->pathSeparator
680
            &&
681 5
            (string) $offset === $offset
682
            &&
683 5
            \strpos($offset, $this->pathSeparator) !== false
684
        ) {
685 2
            $path = \explode($this->pathSeparator, (string) $offset);
686
687 2
            if ($path !== false) {
688 2
                $pathToUnset = \array_pop($path);
689
690 2
                $this->callAtPath(
691 2
                    \implode($this->pathSeparator, $path),
692
                    static function (&$offset) use ($pathToUnset) {
693 2
                        unset($offset[$pathToUnset]);
694 2
                    }
695
                );
696
            }
697
        }
698
699 5
        unset($this->array[$offset]);
700 5
    }
701
702
    /**
703
     * Serialize the current "Arrayy"-object.
704
     *
705
     * @return string
706
     */
707 2
    public function serialize(): string
708
    {
709 2
        $this->generatorToArray();
710
711 2
        if (\PHP_VERSION_ID < 70400) {
712 2
            return parent::serialize();
713
        }
714
715
        return \serialize($this);
716
    }
717
718
    /**
719
     * Sets the iterator classname for the current "Arrayy"-object.
720
     *
721
     * @param string $iteratorClass
722
     *
723
     * @throws \InvalidArgumentException
724
     *
725
     * @return void
726
     *
727
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
728
     */
729 1091
    public function setIteratorClass($iteratorClass)
730
    {
731 1091
        if (\class_exists($iteratorClass)) {
732 1091
            $this->iteratorClass = $iteratorClass;
733
734 1091
            return;
735
        }
736
737
        if (\strpos($iteratorClass, '\\') === 0) {
738
            $iteratorClass = '\\' . $iteratorClass;
739
            if (\class_exists($iteratorClass)) {
740
                $this->iteratorClass = $iteratorClass;
741
742
                return;
743
            }
744
        }
745
746
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
747
    }
748
749
    /**
750
     * Sort the entries with a user-defined comparison function and maintain key association.
751
     *
752
     * @param callable $function
753
     *
754
     * @throws \InvalidArgumentException
755
     *
756
     * @return $this
757
     *               <p>(Mutable) Return this Arrayy object.</p>
758
     *
759
     * @psalm-return static<TKey,T>
760
     */
761 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...
762
    {
763 8
        if (!\is_callable($function)) {
764
            throw new \InvalidArgumentException('Passed function must be callable');
765
        }
766
767 8
        $this->generatorToArray();
768
769 8
        \uasort($this->array, $function);
770
771 8
        return $this;
772
    }
773
774
    /**
775
     * Sort the entries with a user-defined comparison function and maintain key association.
776
     *
777
     * @param callable $function
778
     *
779
     * @throws \InvalidArgumentException
780
     *
781
     * @return $this
782
     *               <p>(Immutable) Return this Arrayy object.</p>
783
     *
784
     * @psalm-return static<TKey,T>
785
     * @psalm-mutation-free
786
     */
787 4
    public function uasortImmutable($function): self
788
    {
789 4
        $that = clone $this;
790
791 4
        $that->uasort($function);
792
793 4
        return $that;
794
    }
795
796
    /**
797
     * Sort the entries by keys using a user-defined comparison function.
798
     *
799
     * @param callable $function
800
     *
801
     * @throws \InvalidArgumentException
802
     *
803
     * @return static
804
     *                <p>(Mutable) Return this Arrayy object.</p>
805
     *
806
     * @psalm-return static<TKey,T>
807
     */
808 5
    public function uksort($function): self
809
    {
810 5
        return $this->customSortKeys($function);
811
    }
812
813
    /**
814
     * Sort the entries by keys using a user-defined comparison function.
815
     *
816
     * @param callable $function
817
     *
818
     * @throws \InvalidArgumentException
819
     *
820
     * @return static
821
     *                <p>(Immutable) Return this Arrayy object.</p>
822
     *
823
     * @psalm-return static<TKey,T>
824
     * @psalm-mutation-free
825
     */
826 1
    public function uksortImmutable($function): self
827
    {
828 1
        return $this->customSortKeysImmutable($function);
829
    }
830
831
    /**
832
     * Unserialize an string and return the instance of the "Arrayy"-class.
833
     *
834
     * @param string $string
835
     *
836
     * @return $this
837
     *
838
     * @psalm-return static<TKey,T>
839
     */
840 2
    public function unserialize($string): self
841
    {
842 2
        if (\PHP_VERSION_ID < 70400) {
843 2
            parent::unserialize($string);
844
845 2
            return $this;
846
        }
847
848
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
849
    }
850
851
    /**
852
     * Append a (key) + values to the current array.
853
     *
854
     * @param array $values
855
     * @param mixed $key
856
     *
857
     * @return $this
858
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
859
     *
860
     * @psalm-param  array<mixed,T> $values
861
     * @psalm-param  TKey|null $key
862
     * @psalm-return static<TKey,T>
863
     */
864 1
    public function appendArrayValues(array $values, $key = null)
865
    {
866 1
        $this->generatorToArray();
867
868 1
        if ($key !== null) {
869
            if (
870 1
                isset($this->array[$key])
871
                &&
872 1
                \is_array($this->array[$key]) === true
873
            ) {
874 1
                foreach ($values as $value) {
875 1
                    $this->array[$key][] = $value;
876
                }
877
            } else {
878
                foreach ($values as $value) {
879 1
                    $this->array[$key] = $value;
880
                }
881
            }
882
        } else {
883
            foreach ($values as $value) {
884
                $this->array[] = $value;
885
            }
886
        }
887
888 1
        return $this;
889
    }
890
891
    /**
892
     * Add a suffix to each key.
893
     *
894
     * @param mixed $prefix
895
     *
896
     * @return static
897
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
898
     *
899
     * @psalm-return static<TKey,T>
900
     * @psalm-mutation-free
901
     */
902 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...
903
    {
904
        // init
905 10
        $result = [];
906
907 10
        foreach ($this->getGenerator() as $key => $item) {
908 9
            if ($item instanceof self) {
909
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
910 9
            } elseif (\is_array($item) === true) {
911
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
912
                    ->appendToEachKey($prefix)
913
                    ->toArray();
914
            } else {
915 9
                $result[$prefix . $key] = $item;
916
            }
917
        }
918
919 10
        return self::create($result, $this->iteratorClass, false);
920
    }
921
922
    /**
923
     * Add a prefix to each value.
924
     *
925
     * @param mixed $prefix
926
     *
927
     * @return static
928
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
929
     *
930
     * @psalm-return static<TKey,T>
931
     * @psalm-mutation-free
932
     */
933 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...
934
    {
935
        // init
936 10
        $result = [];
937
938 10
        foreach ($this->getGenerator() as $key => $item) {
939 9
            if ($item instanceof self) {
940
                $result[$key] = $item->appendToEachValue($prefix);
941 9
            } elseif (\is_array($item) === true) {
942
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
943 9
            } elseif (\is_object($item) === true) {
944 1
                $result[$key] = $item;
945
            } else {
946 9
                $result[$key] = $prefix . $item;
947
            }
948
        }
949
950 10
        return self::create($result, $this->iteratorClass, false);
951
    }
952
953
    /**
954
     * Sort an array in reverse order and maintain index association.
955
     *
956
     * @return $this
957
     *               <p>(Mutable) Return this Arrayy object.</p>
958
     *
959
     * @psalm-return static<TKey,T>
960
     */
961 4
    public function arsort(): self
962
    {
963 4
        $this->generatorToArray();
964
965 4
        \arsort($this->array);
966
967 4
        return $this;
968
    }
969
970
    /**
971
     * Sort an array in reverse order and maintain index association.
972
     *
973
     * @return $this
974
     *               <p>(Immutable) Return this Arrayy object.</p>
975
     *
976
     * @psalm-return static<TKey,T>
977
     * @psalm-mutation-free
978
     */
979 10
    public function arsortImmutable(): self
980
    {
981 10
        $that = clone $this;
982
983 10
        $that->generatorToArray();
984
985 10
        \arsort($that->array);
986
987 10
        return $that;
988
    }
989
990
    /**
991
     * Iterate over the current array and execute a callback for each loop.
992
     *
993
     * @param \Closure $closure
994
     *
995
     * @return static
996
     *                <p>(Immutable)</p>
997
     *
998
     * @psalm-return static<TKey,T>
999
     * @psalm-mutation-free
1000
     */
1001 2
    public function at(\Closure $closure): self
1002
    {
1003 2
        $that = clone $this;
1004
1005 2
        foreach ($that->getGenerator() as $key => $value) {
1006 2
            $closure($value, $key);
1007
        }
1008
1009 2
        return static::create(
1010 2
            $that->toArray(),
1011 2
            $this->iteratorClass,
1012 2
            false
1013
        );
1014
    }
1015
1016
    /**
1017
     * Returns the average value of the current array.
1018
     *
1019
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1020
     *
1021
     * @return float|int
1022
     *                   <p>The average value.</p>
1023
     * @psalm-mutation-free
1024
     */
1025 10
    public function average($decimals = 0)
1026
    {
1027 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1028
1029 10
        if (!$count) {
1030 2
            return 0;
1031
        }
1032
1033 8
        if ((int) $decimals !== $decimals) {
1034 3
            $decimals = 0;
1035
        }
1036
1037 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1038
    }
1039
1040
    /**
1041
     * Changes all keys in an array.
1042
     *
1043
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1044
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1045
     *
1046
     * @return static
1047
     *                <p>(Immutable)</p>
1048
     *
1049
     * @psalm-return static<TKey,T>
1050
     * @psalm-mutation-free
1051
     */
1052 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1053
    {
1054
        if (
1055 1
            $case !== \CASE_LOWER
1056
            &&
1057 1
            $case !== \CASE_UPPER
1058
        ) {
1059
            $case = \CASE_LOWER;
1060
        }
1061
1062 1
        $return = [];
1063 1
        foreach ($this->getGenerator() as $key => $value) {
1064 1
            if ($case === \CASE_LOWER) {
1065 1
                $key = \mb_strtolower((string) $key);
1066
            } else {
1067 1
                $key = \mb_strtoupper((string) $key);
1068
            }
1069
1070 1
            $return[$key] = $value;
1071
        }
1072
1073 1
        return static::create(
1074 1
            $return,
1075 1
            $this->iteratorClass,
1076 1
            false
1077
        );
1078
    }
1079
1080
    /**
1081
     * Change the path separator of the array wrapper.
1082
     *
1083
     * By default, the separator is: "."
1084
     *
1085
     * @param string $separator <p>Separator to set.</p>
1086
     *
1087
     * @return $this
1088
     *               <p>(Mutable) Return this Arrayy object.</p>
1089
     *
1090
     * @psalm-return static<TKey,T>
1091
     */
1092 11
    public function changeSeparator($separator): self
1093
    {
1094 11
        $this->pathSeparator = $separator;
1095
1096 11
        return $this;
1097
    }
1098
1099
    /**
1100
     * Create a chunked version of the current array.
1101
     *
1102
     * @param int  $size         <p>Size of each chunk.</p>
1103
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1104
     *
1105
     * @return static
1106
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1107
     *
1108
     * @psalm-return static<TKey,T>
1109
     * @psalm-mutation-free
1110
     */
1111 5
    public function chunk($size, $preserveKeys = false): self
1112
    {
1113 5
        return static::create(
1114 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1115 5
            $this->iteratorClass,
1116 5
            false
1117
        );
1118
    }
1119
1120
    /**
1121
     * Clean all falsy values from the current array.
1122
     *
1123
     * @return static
1124
     *                <p>(Immutable)</p>
1125
     *
1126
     * @psalm-return static<TKey,T>
1127
     * @psalm-mutation-free
1128
     */
1129 8
    public function clean(): self
1130
    {
1131 8
        return $this->filter(
1132
            static function ($value) {
1133 7
                return (bool) $value;
1134 8
            }
1135
        );
1136
    }
1137
1138
    /**
1139
     * WARNING!!! -> Clear the current array.
1140
     *
1141
     * @return $this
1142
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1143
     *
1144
     * @psalm-return static<TKey,T>
1145
     */
1146 5
    public function clear(): self
1147
    {
1148 5
        $this->array = [];
1149 5
        $this->generator = null;
1150
1151 5
        return $this;
1152
    }
1153
1154
    /**
1155
     * Check if an item is in the current array.
1156
     *
1157
     * @param float|int|string $value
1158
     * @param bool             $recursive
1159
     * @param bool             $strict
1160
     *
1161
     * @return bool
1162
     * @psalm-mutation-free
1163
     */
1164 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1165
    {
1166 23
        if ($recursive === true) {
1167 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1168
        }
1169
1170 14
        foreach ($this->getGenerator() as $valueFromArray) {
1171 11
            if ($strict) {
1172 11
                if ($value === $valueFromArray) {
1173 11
                    return true;
1174
                }
1175
            } else {
1176
                /** @noinspection NestedPositiveIfStatementsInspection */
1177
                if ($value == $valueFromArray) {
1178 7
                    return true;
1179
                }
1180
            }
1181
        }
1182
1183 7
        return false;
1184
    }
1185
1186
    /**
1187
     * Check if an (case-insensitive) string is in the current array.
1188
     *
1189
     * @param string $value
1190
     * @param bool   $recursive
1191
     *
1192
     * @return bool
1193
     * @psalm-mutation-free
1194
     */
1195 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1196
    {
1197 26
        if ($recursive === true) {
1198 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
1199 22
                if (\is_array($valueTmp) === true) {
1200 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1201 5
                    if ($return === true) {
1202 5
                        return $return;
1203
                    }
1204 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1205 22
                    return true;
1206
                }
1207
            }
1208
1209 10
            return false;
1210
        }
1211
1212 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
1213 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1214 11
                return true;
1215
            }
1216
        }
1217
1218 5
        return false;
1219
    }
1220
1221
    /**
1222
     * Check if the given key/index exists in the array.
1223
     *
1224
     * @param int|string $key <p>key/index to search for</p>
1225
     *
1226
     * @return bool
1227
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1228
     *
1229
     * @psalm-mutation-free
1230
     */
1231 4
    public function containsKey($key): bool
1232
    {
1233 4
        return $this->offsetExists($key);
1234
    }
1235
1236
    /**
1237
     * Check if all given needles are present in the array as key/index.
1238
     *
1239
     * @param array $needles   <p>The keys you are searching for.</p>
1240
     * @param bool  $recursive
1241
     *
1242
     * @return bool
1243
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1244
     *
1245
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1246
     * @psalm-mutation-free
1247
     */
1248 2
    public function containsKeys(array $needles, $recursive = false): bool
1249
    {
1250 2
        if ($recursive === true) {
1251
            return
1252 2
                \count(
1253 2
                    \array_intersect(
1254 2
                        $needles,
1255 2
                        $this->keys(true)->toArray()
1256
                    ),
1257 2
                    \COUNT_RECURSIVE
1258
                )
1259
                ===
1260 2
                \count(
1261 2
                    $needles,
1262 2
                    \COUNT_RECURSIVE
1263
                );
1264
        }
1265
1266 1
        return \count(
1267 1
            \array_intersect($needles, $this->keys()->toArray()),
1268 1
            \COUNT_NORMAL
1269
        )
1270
               ===
1271 1
               \count(
1272 1
                   $needles,
1273 1
                   \COUNT_NORMAL
1274
               );
1275
    }
1276
1277
    /**
1278
     * Check if all given needles are present in the array as key/index.
1279
     *
1280
     * @param array $needles <p>The keys you are searching for.</p>
1281
     *
1282
     * @return bool
1283
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1284
     *
1285
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1286
     * @psalm-mutation-free
1287
     */
1288 1
    public function containsKeysRecursive(array $needles): bool
1289
    {
1290 1
        return $this->containsKeys($needles, true);
1291
    }
1292
1293
    /**
1294
     * alias: for "Arrayy->contains()"
1295
     *
1296
     * @param float|int|string $value
1297
     *
1298
     * @return bool
1299
     *
1300
     * @see Arrayy::contains()
1301
     * @psalm-mutation-free
1302
     */
1303 9
    public function containsValue($value): bool
1304
    {
1305 9
        return $this->contains($value);
1306
    }
1307
1308
    /**
1309
     * alias: for "Arrayy->contains($value, true)"
1310
     *
1311
     * @param float|int|string $value
1312
     *
1313
     * @return bool
1314
     *
1315
     * @see Arrayy::contains()
1316
     * @psalm-mutation-free
1317
     */
1318 18
    public function containsValueRecursive($value): bool
1319
    {
1320 18
        return $this->contains($value, true);
1321
    }
1322
1323
    /**
1324
     * Check if all given needles are present in the array.
1325
     *
1326
     * @param array $needles
1327
     *
1328
     * @return bool
1329
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1330
     *
1331
     * @psalm-param array<mixed>|array<T> $needles
1332
     * @psalm-mutation-free
1333
     */
1334 1
    public function containsValues(array $needles): bool
1335
    {
1336 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1337
               ===
1338 1
               \count($needles, \COUNT_NORMAL);
1339
    }
1340
1341
    /**
1342
     * Counts all the values of an array
1343
     *
1344
     * @see          http://php.net/manual/en/function.array-count-values.php
1345
     *
1346
     * @return static
1347
     *                <p>
1348
     *                (Immutable)
1349
     *                An associative Arrayy-object of values from input as
1350
     *                keys and their count as value.
1351
     *                </p>
1352
     *
1353
     * @psalm-return static<TKey,T>
1354
     * @psalm-mutation-free
1355
     */
1356 7
    public function countValues(): self
1357
    {
1358 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1359
    }
1360
1361
    /**
1362
     * Creates an Arrayy object.
1363
     *
1364
     * @param mixed  $data
1365
     * @param string $iteratorClass
1366
     * @param bool   $checkPropertiesInConstructor
1367
     *
1368
     * @return static
1369
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1370
     *
1371
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1372
     *
1373
     * @psalm-mutation-free
1374
     */
1375 672
    public static function create(
1376
        $data = [],
1377
        string $iteratorClass = ArrayyIterator::class,
1378
        bool $checkPropertiesInConstructor = true
1379
    ) {
1380 672
        return new static(
1381 672
            $data,
1382 672
            $iteratorClass,
1383 672
            $checkPropertiesInConstructor
1384
        );
1385
    }
1386
1387
    /**
1388
     * WARNING: Creates an Arrayy object by reference.
1389
     *
1390
     * @param array $array
1391
     *
1392
     * @return $this
1393
     *               <p>(Mutable) Return this Arrayy object.</p>
1394
     *
1395
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1396
     */
1397 1
    public function createByReference(array &$array = []): self
1398
    {
1399 1
        $array = $this->fallbackForArray($array);
1400
1401 1
        $this->array = &$array;
1402
1403 1
        return $this;
1404
    }
1405
1406
    /**
1407
     * Create an new instance from a callable function which will return an Generator.
1408
     *
1409
     * @param callable $generatorFunction
1410
     *
1411
     * @return static
1412
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1413
     *
1414
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1415
     *
1416
     * @psalm-mutation-free
1417
     */
1418 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1419
    {
1420 7
        return self::create($generatorFunction);
1421
    }
1422
1423
    /**
1424
     * Create an new instance filled with a copy of values from a "Generator"-object.
1425
     *
1426
     * @param \Generator $generator
1427
     *
1428
     * @return static
1429
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1430
     *
1431
     * @psalm-param \Generator<array-key,mixed> $generator
1432
     *
1433
     * @psalm-mutation-free
1434
     */
1435 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1436
    {
1437 4
        return self::create(\iterator_to_array($generator, true));
1438
    }
1439
1440
    /**
1441
     * Create an new Arrayy object via JSON.
1442
     *
1443
     * @param string $json
1444
     *
1445
     * @return static
1446
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1447
     *
1448
     * @psalm-mutation-free
1449
     */
1450 5
    public static function createFromJson(string $json): self
1451
    {
1452 5
        return static::create(\json_decode($json, true));
1453
    }
1454
1455
    /**
1456
     * Create an new instance filled with values from an object that is iterable.
1457
     *
1458
     * @param \Traversable $object <p>iterable object</p>
1459
     *
1460
     * @return static
1461
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1462
     *
1463
     * @psalm-param \Traversable<array-key,mixed> $object
1464
     *
1465
     * @psalm-mutation-free
1466
     */
1467 4
    public static function createFromObject(\Traversable $object): self
1468
    {
1469
        // init
1470 4
        $arrayy = new static();
1471
1472 4
        if ($object instanceof self) {
1473 4
            $objectArray = $object->getGenerator();
1474
        } else {
1475
            $objectArray = $object;
1476
        }
1477
1478 4
        foreach ($objectArray as $key => $value) {
1479 3
            $arrayy->internalSet($key, $value);
1480
        }
1481
1482 4
        return $arrayy;
1483
    }
1484
1485
    /**
1486
     * Create an new instance filled with values from an object.
1487
     *
1488
     * @param object $object
1489
     *
1490
     * @return static
1491
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1492
     *
1493
     * @psalm-mutation-free
1494
     */
1495 5
    public static function createFromObjectVars($object): self
1496
    {
1497 5
        return self::create(self::objectToArray($object));
1498
    }
1499
1500
    /**
1501
     * Create an new Arrayy object via string.
1502
     *
1503
     * @param string      $str       <p>The input string.</p>
1504
     * @param string|null $delimiter <p>The boundary string.</p>
1505
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1506
     *                               used.</p>
1507
     *
1508
     * @return static
1509
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1510
     *
1511
     * @psalm-mutation-free
1512
     */
1513 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1514
    {
1515 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...
1516 1
            \preg_match_all($regEx, $str, $array);
1517
1518 1
            if (!empty($array)) {
1519 1
                $array = $array[0];
1520
            }
1521
        } else {
1522
            /** @noinspection NestedPositiveIfStatementsInspection */
1523 9
            if ($delimiter !== null) {
1524 7
                $array = \explode($delimiter, $str);
1525
            } else {
1526 2
                $array = [$str];
1527
            }
1528
        }
1529
1530
        // trim all string in the array
1531
        /**
1532
         * @psalm-suppress MissingClosureParamType
1533
         */
1534 10
        \array_walk(
1535 10
            $array,
1536
            static function (&$val) {
1537 10
                if ((string) $val === $val) {
1538 10
                    $val = \trim($val);
1539
                }
1540 10
            }
1541
        );
1542
1543 10
        return static::create($array);
1544
    }
1545
1546
    /**
1547
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1548
     *
1549
     * @param \Traversable $traversable
1550
     *
1551
     * @return static
1552
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1553
     *
1554
     * @psalm-param \Traversable<array-key,mixed> $traversable
1555
     *
1556
     * @psalm-mutation-free
1557
     */
1558 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1559
    {
1560 1
        return self::create(\iterator_to_array($traversable, true));
1561
    }
1562
1563
    /**
1564
     * Create an new instance containing a range of elements.
1565
     *
1566
     * @param float|int|string $low  <p>First value of the sequence.</p>
1567
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1568
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1569
     *
1570
     * @return static
1571
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1572
     *
1573
     * @psalm-mutation-free
1574
     */
1575 2
    public static function createWithRange($low, $high, $step = 1): self
1576
    {
1577 2
        return static::create(\range($low, $high, $step));
1578
    }
1579
1580
    /**
1581
     * Gets the element of the array at the current internal iterator position.
1582
     *
1583
     * @return false|mixed
1584
     */
1585
    public function current()
1586
    {
1587
        return \current($this->array);
1588
    }
1589
1590
    /**
1591
     * Custom sort by index via "uksort".
1592
     *
1593
     * @see          http://php.net/manual/en/function.uksort.php
1594
     *
1595
     * @param callable $function
1596
     *
1597
     * @throws \InvalidArgumentException
1598
     *
1599
     * @return $this
1600
     *               <p>(Mutable) Return this Arrayy object.</p>
1601
     *
1602
     * @psalm-return static<TKey,T>
1603
     */
1604 5
    public function customSortKeys(callable $function): self
1605
    {
1606 5
        $this->generatorToArray();
1607
1608 5
        \uksort($this->array, $function);
1609
1610 5
        return $this;
1611
    }
1612
1613
    /**
1614
     * Custom sort by index via "uksort".
1615
     *
1616
     * @see          http://php.net/manual/en/function.uksort.php
1617
     *
1618
     * @param callable $function
1619
     *
1620
     * @throws \InvalidArgumentException
1621
     *
1622
     * @return $this
1623
     *               <p>(Immutable) Return this Arrayy object.</p>
1624
     *
1625
     * @psalm-return static<TKey,T>
1626
     * @psalm-mutation-free
1627
     */
1628 1
    public function customSortKeysImmutable(callable $function): self
1629
    {
1630 1
        $that = clone $this;
1631
1632 1
        $that->generatorToArray();
1633
1634 1
        \uksort($that->array, $function);
1635
1636 1
        return $that;
1637
    }
1638
1639
    /**
1640
     * Custom sort by value via "usort".
1641
     *
1642
     * @see          http://php.net/manual/en/function.usort.php
1643
     *
1644
     * @param callable $function
1645
     *
1646
     * @throws \InvalidArgumentException
1647
     *
1648
     * @return $this
1649
     *               <p>(Mutable) Return this Arrayy object.</p>
1650
     *
1651
     * @psalm-return static<TKey,T>
1652
     */
1653 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...
1654
    {
1655 10
        if (\is_callable($function) === false) {
1656
            throw new \InvalidArgumentException('Passed function must be callable');
1657
        }
1658
1659 10
        $this->generatorToArray();
1660
1661 10
        \usort($this->array, $function);
1662
1663 10
        return $this;
1664
    }
1665
1666
    /**
1667
     * Custom sort by value via "usort".
1668
     *
1669
     * @see          http://php.net/manual/en/function.usort.php
1670
     *
1671
     * @param callable $function
1672
     *
1673
     * @throws \InvalidArgumentException
1674
     *
1675
     * @return $this
1676
     *               <p>(Immutable) Return this Arrayy object.</p>
1677
     *
1678
     * @psalm-return static<TKey,T>
1679
     * @psalm-mutation-free
1680
     */
1681 4
    public function customSortValuesImmutable($function): self
1682
    {
1683 4
        $that = clone $this;
1684
1685
        /**
1686
         * @psalm-suppress ImpureMethodCall - object is already cloned
1687
         */
1688 4
        $that->customSortValues($function);
1689
1690 4
        return $that;
1691
    }
1692
1693
    /**
1694
     * Delete the given key or keys.
1695
     *
1696
     * @param int|int[]|string|string[] $keyOrKeys
1697
     *
1698
     * @return void
1699
     */
1700 4
    public function delete($keyOrKeys)
1701
    {
1702 4
        $keyOrKeys = (array) $keyOrKeys;
1703
1704 4
        foreach ($keyOrKeys as $key) {
1705 4
            $this->offsetUnset($key);
1706
        }
1707 4
    }
1708
1709
    /**
1710
     * Return values that are only in the current array.
1711
     *
1712
     * @param array ...$array
1713
     *
1714
     * @return static
1715
     *                <p>(Immutable)</p>
1716
     *
1717
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1718
     * @psalm-return static<TKey,T>
1719
     * @psalm-mutation-free
1720
     */
1721 13
    public function diff(...$array): self
1722
    {
1723 13
        return static::create(
1724 13
            \array_diff($this->toArray(), ...$array),
1725 13
            $this->iteratorClass,
1726 13
            false
1727
        );
1728
    }
1729
1730
    /**
1731
     * Return values that are only in the current array.
1732
     *
1733
     * @param array ...$array
1734
     *
1735
     * @return static
1736
     *                <p>(Immutable)</p>
1737
     *
1738
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1739
     * @psalm-return static<TKey,T>
1740
     * @psalm-mutation-free
1741
     */
1742 8
    public function diffKey(...$array): self
1743
    {
1744 8
        return static::create(
1745 8
            \array_diff_key($this->toArray(), ...$array),
1746 8
            $this->iteratorClass,
1747 8
            false
1748
        );
1749
    }
1750
1751
    /**
1752
     * Return values and Keys that are only in the current array.
1753
     *
1754
     * @param array $array
1755
     *
1756
     * @return static
1757
     *                <p>(Immutable)</p>
1758
     *
1759
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1760
     * @psalm-return static<TKey,T>
1761
     * @psalm-mutation-free
1762
     */
1763 8
    public function diffKeyAndValue(array $array = []): self
1764
    {
1765 8
        return static::create(
1766 8
            \array_diff_assoc($this->toArray(), $array),
1767 8
            $this->iteratorClass,
1768 8
            false
1769
        );
1770
    }
1771
1772
    /**
1773
     * Return values that are only in the current multi-dimensional array.
1774
     *
1775
     * @param array                 $array
1776
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
1777
     *
1778
     * @return static
1779
     *                <p>(Immutable)</p>
1780
     *
1781
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1782
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
1783
     * @psalm-return static<TKey,T>
1784
     * @psalm-mutation-free
1785
     */
1786 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1787
    {
1788
        // init
1789 1
        $result = [];
1790
1791
        if (
1792 1
            $helperVariableForRecursion !== null
1793
            &&
1794 1
            \is_array($helperVariableForRecursion) === true
1795
        ) {
1796
            $arrayForTheLoop = $helperVariableForRecursion;
1797
        } else {
1798 1
            $arrayForTheLoop = $this->getGenerator();
1799
        }
1800
1801 1
        foreach ($arrayForTheLoop as $key => $value) {
1802 1
            if ($value instanceof self) {
1803
                $value = $value->toArray();
1804
            }
1805
1806 1
            if (\array_key_exists($key, $array)) {
1807 1
                if ($value !== $array[$key]) {
1808 1
                    $result[$key] = $value;
1809
                }
1810
            } else {
1811 1
                $result[$key] = $value;
1812
            }
1813
        }
1814
1815 1
        return static::create(
1816 1
            $result,
1817 1
            $this->iteratorClass,
1818 1
            false
1819
        );
1820
    }
1821
1822
    /**
1823
     * Return values that are only in the new $array.
1824
     *
1825
     * @param array $array
1826
     *
1827
     * @return static
1828
     *                <p>(Immutable)</p>
1829
     *
1830
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1831
     * @psalm-return static<TKey,T>
1832
     * @psalm-mutation-free
1833
     */
1834 8
    public function diffReverse(array $array = []): self
1835
    {
1836 8
        return static::create(
1837 8
            \array_diff($array, $this->toArray()),
1838 8
            $this->iteratorClass,
1839 8
            false
1840
        );
1841
    }
1842
1843
    /**
1844
     * Divide an array into two arrays. One with keys and the other with values.
1845
     *
1846
     * @return static
1847
     *                <p>(Immutable)</p>
1848
     *
1849
     * @psalm-return static<TKey,T>
1850
     * @psalm-mutation-free
1851
     */
1852 1
    public function divide(): self
1853
    {
1854 1
        return static::create(
1855
            [
1856 1
                $this->keys(),
1857 1
                $this->values(),
1858
            ],
1859 1
            $this->iteratorClass,
1860 1
            false
1861
        );
1862
    }
1863
1864
    /**
1865
     * Iterate over the current array and modify the array's value.
1866
     *
1867
     * @param \Closure $closure
1868
     *
1869
     * @return static
1870
     *                <p>(Immutable)</p>
1871
     *
1872
     * @psalm-return static<TKey,T>
1873
     * @psalm-mutation-free
1874
     */
1875 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...
1876
    {
1877
        // init
1878 5
        $array = [];
1879
1880 5
        foreach ($this->getGenerator() as $key => $value) {
1881 5
            $array[$key] = $closure($value, $key);
1882
        }
1883
1884 5
        return static::create(
1885 5
            $array,
1886 5
            $this->iteratorClass,
1887 5
            false
1888
        );
1889
    }
1890
1891
    /**
1892
     * Sets the internal iterator to the last element in the array and returns this element.
1893
     *
1894
     * @return mixed
1895
     */
1896
    public function end()
1897
    {
1898
        return \end($this->array);
1899
    }
1900
1901
    /**
1902
     * Check if a value is in the current array using a closure.
1903
     *
1904
     * @param \Closure $closure
1905
     *
1906
     * @return bool
1907
     *              <p>Returns true if the given value is found, false otherwise.</p>
1908
     */
1909 4
    public function exists(\Closure $closure): bool
1910
    {
1911
        // init
1912 4
        $isExists = false;
1913
1914 4
        foreach ($this->getGenerator() as $key => $value) {
1915 3
            if ($closure($value, $key)) {
1916 1
                $isExists = true;
1917
1918 3
                break;
1919
            }
1920
        }
1921
1922 4
        return $isExists;
1923
    }
1924
1925
    /**
1926
     * Fill the array until "$num" with "$default" values.
1927
     *
1928
     * @param int   $num
1929
     * @param mixed $default
1930
     *
1931
     * @return static
1932
     *                <p>(Immutable)</p>
1933
     *
1934
     * @psalm-return static<TKey,T>
1935
     * @psalm-mutation-free
1936
     */
1937 8
    public function fillWithDefaults(int $num, $default = null): self
1938
    {
1939 8
        if ($num < 0) {
1940 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1941
        }
1942
1943 7
        $this->generatorToArray();
1944
1945 7
        $tmpArray = $this->array;
1946
1947 7
        $count = \count($tmpArray);
1948
1949 7
        while ($count < $num) {
1950 4
            $tmpArray[] = $default;
1951 4
            ++$count;
1952
        }
1953
1954 7
        return static::create(
1955 7
            $tmpArray,
1956 7
            $this->iteratorClass,
1957 7
            false
1958
        );
1959
    }
1960
1961
    /**
1962
     * Find all items in an array that pass the truth test.
1963
     *
1964
     * @param \Closure|null $closure [optional] <p>
1965
     *                               The callback function to use
1966
     *                               </p>
1967
     *                               <p>
1968
     *                               If no callback is supplied, all entries of
1969
     *                               input equal to false (see
1970
     *                               converting to
1971
     *                               boolean) will be removed.
1972
     *                               </p>
1973
     * @param int           $flag    [optional] <p>
1974
     *                               Flag determining what arguments are sent to <i>callback</i>:
1975
     *                               </p><ul>
1976
     *                               <li>
1977
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1978
     *                               to <i>callback</i> instead of the value</span>
1979
     *                               </li>
1980
     *                               <li>
1981
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1982
     *                               arguments to <i>callback</i> instead of the value</span>
1983
     *                               </li>
1984
     *                               </ul>
1985
     *
1986
     * @return static
1987
     *                <p>(Immutable)</p>
1988
     *
1989
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
1990
     * @psalm-return static<TKey,T>
1991
     * @psalm-mutation-free
1992
     */
1993 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
1994
    {
1995 12
        if (!$closure) {
1996 1
            return $this->clean();
1997
        }
1998
1999 12
        return static::create(
2000 12
            \array_filter($this->toArray(), $closure, $flag),
2001 12
            $this->iteratorClass,
2002 12
            false
2003
        );
2004
    }
2005
2006
    /**
2007
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2008
     * property within that.
2009
     *
2010
     * @param string          $property
2011
     * @param string|string[] $value
2012
     * @param string          $comparisonOp
2013
     *                                      <p>
2014
     *                                      'eq' (equals),<br />
2015
     *                                      'gt' (greater),<br />
2016
     *                                      'gte' || 'ge' (greater or equals),<br />
2017
     *                                      'lt' (less),<br />
2018
     *                                      'lte' || 'le' (less or equals),<br />
2019
     *                                      'ne' (not equals),<br />
2020
     *                                      'contains',<br />
2021
     *                                      'notContains',<br />
2022
     *                                      'newer' (via strtotime),<br />
2023
     *                                      'older' (via strtotime),<br />
2024
     *                                      </p>
2025
     *
2026
     * @return static
2027
     *                <p>(Immutable)</p>
2028
     *
2029
     * @psalm-return static<TKey,T>
2030
     * @psalm-mutation-free
2031
     *
2032
     * @psalm-suppress MissingClosureReturnType
2033
     * @psalm-suppress MissingClosureParamType
2034
     */
2035 1
    public function filterBy(
2036
        string $property,
2037
        $value,
2038
        string $comparisonOp = null
2039
    ): self {
2040 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...
2041 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2042
        }
2043
2044
        $ops = [
2045
            'eq' => static function ($item, $prop, $value): bool {
2046 1
                return $item[$prop] === $value;
2047 1
            },
2048
            'gt' => static function ($item, $prop, $value): bool {
2049
                return $item[$prop] > $value;
2050 1
            },
2051
            'ge' => static function ($item, $prop, $value): bool {
2052
                return $item[$prop] >= $value;
2053 1
            },
2054
            'gte' => static function ($item, $prop, $value): bool {
2055
                return $item[$prop] >= $value;
2056 1
            },
2057
            'lt' => static function ($item, $prop, $value): bool {
2058 1
                return $item[$prop] < $value;
2059 1
            },
2060
            'le' => static function ($item, $prop, $value): bool {
2061
                return $item[$prop] <= $value;
2062 1
            },
2063
            'lte' => static function ($item, $prop, $value): bool {
2064
                return $item[$prop] <= $value;
2065 1
            },
2066
            'ne' => static function ($item, $prop, $value): bool {
2067
                return $item[$prop] !== $value;
2068 1
            },
2069
            'contains' => static function ($item, $prop, $value): bool {
2070 1
                return \in_array($item[$prop], (array) $value, true);
2071 1
            },
2072
            'notContains' => static function ($item, $prop, $value): bool {
2073
                return !\in_array($item[$prop], (array) $value, true);
2074 1
            },
2075
            'newer' => static function ($item, $prop, $value): bool {
2076
                return \strtotime($item[$prop]) > \strtotime($value);
2077 1
            },
2078
            'older' => static function ($item, $prop, $value): bool {
2079
                return \strtotime($item[$prop]) < \strtotime($value);
2080 1
            },
2081
        ];
2082
2083 1
        $result = \array_values(
2084 1
            \array_filter(
2085 1
                $this->toArray(false, true),
2086
                static function ($item) use (
2087 1
                    $property,
2088 1
                    $value,
2089 1
                    $ops,
2090 1
                    $comparisonOp
2091
                ) {
2092 1
                    $item = (array) $item;
2093 1
                    $itemArrayy = static::create($item);
2094 1
                    $item[$property] = $itemArrayy->get($property, []);
2095
2096 1
                    return $ops[$comparisonOp]($item, $property, $value);
2097 1
                }
2098
            )
2099
        );
2100
2101 1
        return static::create(
2102 1
            $result,
2103 1
            $this->iteratorClass,
2104 1
            false
2105
        );
2106
    }
2107
2108
    /**
2109
     * Find the first item in an array that passes the truth test,
2110
     *  otherwise return false
2111
     *
2112
     * @param \Closure $closure
2113
     *
2114
     * @return false|mixed
2115
     *                     <p>Return false if we did not find the value.</p>
2116
     */
2117 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...
2118
    {
2119 8
        foreach ($this->getGenerator() as $key => $value) {
2120 6
            if ($closure($value, $key)) {
2121 6
                return $value;
2122
            }
2123
        }
2124
2125 3
        return false;
2126
    }
2127
2128
    /**
2129
     * find by ...
2130
     *
2131
     * @param string          $property
2132
     * @param string|string[] $value
2133
     * @param string          $comparisonOp
2134
     *
2135
     * @return static
2136
     *                <p>(Immutable)</p>
2137
     *
2138
     * @psalm-return static<TKey,T>
2139
     * @psalm-mutation-free
2140
     */
2141 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2142
    {
2143 1
        return $this->filterBy($property, $value, $comparisonOp);
2144
    }
2145
2146
    /**
2147
     * Get the first value from the current array.
2148
     *
2149
     * @return mixed
2150
     *               <p>Return null if there wasn't a element.</p>
2151
     */
2152 21
    public function first()
2153
    {
2154 21
        $key_first = $this->firstKey();
2155 21
        if ($key_first === null) {
2156 3
            return null;
2157
        }
2158
2159 18
        return $this->get($key_first);
2160
    }
2161
2162
    /**
2163
     * Get the first key from the current array.
2164
     *
2165
     * @return mixed
2166
     *               <p>Return null if there wasn't a element.</p>
2167
     * @psalm-mutation-free
2168
     */
2169 28
    public function firstKey()
2170
    {
2171 28
        $this->generatorToArray();
2172
2173 28
        return \array_key_first($this->array);
2174
    }
2175
2176
    /**
2177
     * Get the first value(s) from the current array.
2178
     * And will return an empty array if there was no first entry.
2179
     *
2180
     * @param int|null $number <p>How many values you will take?</p>
2181
     *
2182
     * @return static
2183
     *                <p>(Immutable)</p>
2184
     *
2185
     * @psalm-return static<TKey,T>
2186
     * @psalm-mutation-free
2187
     */
2188 37 View Code Duplication
    public function firstsImmutable(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...
2189
    {
2190 37
        $arrayTmp = $this->toArray();
2191
2192 37
        if ($number === null) {
2193 14
            $array = (array) \array_shift($arrayTmp);
2194
        } else {
2195 23
            $number = (int) $number;
2196 23
            $array = \array_splice($arrayTmp, 0, $number);
2197
        }
2198
2199 37
        return static::create(
2200 37
            $array,
2201 37
            $this->iteratorClass,
2202 37
            false
2203
        );
2204
    }
2205
2206
    /**
2207
     * Get the first value(s) from the current array.
2208
     * And will return an empty array if there was no first entry.
2209
     *
2210
     * @param int|null $number <p>How many values you will take?</p>
2211
     *
2212
     * @return static
2213
     *                <p>(Immutable)</p>
2214
     *
2215
     * @psalm-return static<TKey,T>
2216
     * @psalm-mutation-free
2217
     */
2218 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...
2219
    {
2220 3
        $arrayTmp = $this->keys()->toArray();
2221
2222 3
        if ($number === null) {
2223
            $array = (array) \array_shift($arrayTmp);
2224
        } else {
2225 3
            $number = (int) $number;
2226 3
            $array = \array_splice($arrayTmp, 0, $number);
2227
        }
2228
2229 3
        return static::create(
2230 3
            $array,
2231 3
            $this->iteratorClass,
2232 3
            false
2233
        );
2234
    }
2235
2236
    /**
2237
     * Get and rmove the first value(s) from the current array.
2238
     * And will return an empty array if there was no first entry.
2239
     *
2240
     * @param int|null $number <p>How many values you will take?</p>
2241
     *
2242
     * @return $this
2243
     *               <p>(Mutable)</p>
2244
     *
2245
     * @psalm-return static<TKey,T>
2246
     */
2247 34
    public function firstsMutable(int $number = null): self
2248
    {
2249 34
        $this->generatorToArray();
2250
2251 34
        if ($number === null) {
2252 19
            $this->array = (array) \array_shift($this->array);
2253
        } else {
2254 15
            $number = (int) $number;
2255 15
            $this->array = \array_splice($this->array, 0, $number);
2256
        }
2257
2258 34
        return $this;
2259
    }
2260
2261
    /**
2262
     * Exchanges all keys with their associated values in an array.
2263
     *
2264
     * @return static
2265
     *                <p>(Immutable)</p>
2266
     *
2267
     * @psalm-return static<TKey,T>
2268
     * @psalm-mutation-free
2269
     */
2270 1
    public function flip(): self
2271
    {
2272 1
        return static::create(
2273 1
            \array_flip($this->toArray()),
2274 1
            $this->iteratorClass,
2275 1
            false
2276
        );
2277
    }
2278
2279
    /**
2280
     * Get a value from an array (optional using dot-notation).
2281
     *
2282
     * @param mixed $key      <p>The key to look for.</p>
2283
     * @param mixed $fallback <p>Value to fallback to.</p>
2284
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
2285
     *                        class.</p>
2286
     *
2287
     * @return mixed|static
2288
     *
2289
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2290
     * @psalm-mutation-free
2291
     */
2292 172
    public function get($key, $fallback = null, array $array = null)
2293
    {
2294 172
        if ($array !== null) {
2295 4
            $usedArray = $array;
2296
        } else {
2297 169
            $this->generatorToArray();
2298
2299 169
            $usedArray = $this->array;
2300
        }
2301
2302 172
        if ($key === null) {
2303 1
            return static::create(
2304 1
                $usedArray,
2305 1
                $this->iteratorClass,
2306 1
                false
2307
            );
2308
        }
2309
2310
        // php cast "bool"-index into "int"-index
2311 172
        if ((bool) $key === $key) {
2312 3
            $key = (int) $key;
2313
        }
2314
2315 172
        if (\array_key_exists($key, $usedArray) === true) {
2316 162
            if (\is_array($usedArray[$key]) === true) {
2317 11
                return static::create(
2318 11
                    $usedArray[$key],
2319 11
                    $this->iteratorClass,
2320 11
                    false
2321
                );
2322
            }
2323
2324 154
            return $usedArray[$key];
2325
        }
2326
2327
        // crawl through array, get key according to object or not
2328 24
        $usePath = false;
2329
        if (
2330 24
            $this->pathSeparator
2331
            &&
2332 24
            (string) $key === $key
2333
            &&
2334 24
            \strpos($key, $this->pathSeparator) !== false
2335
        ) {
2336 7
            $segments = \explode($this->pathSeparator, (string) $key);
2337 7
            if ($segments !== false) {
2338 7
                $usePath = true;
2339
2340 7
                foreach ($segments as $segment) {
2341
                    if (
2342
                        (
2343 7
                            \is_array($usedArray) === true
2344
                            ||
2345 7
                            $usedArray instanceof \ArrayAccess
2346
                        )
2347
                        &&
2348 7
                        isset($usedArray[$segment])
2349
                    ) {
2350 7
                        $usedArray = $usedArray[$segment];
2351
2352 7
                        continue;
2353
                    }
2354
2355
                    if (
2356 6
                        \is_object($usedArray) === true
2357
                        &&
2358 6
                        \property_exists($usedArray, $segment)
2359
                    ) {
2360 1
                        $usedArray = $usedArray->{$segment};
2361
2362 1
                        continue;
2363
                    }
2364
2365 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2366
                }
2367
            }
2368
        }
2369
2370 24
        if (!$usePath && !isset($usedArray[$key])) {
2371 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2372
        }
2373
2374 7
        if (\is_array($usedArray) === true) {
2375 1
            return static::create(
2376 1
                $usedArray,
2377 1
                $this->iteratorClass,
2378 1
                false
2379
            );
2380
        }
2381
2382 7
        return $usedArray;
2383
    }
2384
2385
    /**
2386
     * alias: for "Arrayy->toArray()"
2387
     *
2388
     * @return array
2389
     *
2390
     * @see          Arrayy::getArray()
2391
     *
2392
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2393
     */
2394 1
    public function getAll(): array
2395
    {
2396 1
        return $this->toArray();
2397
    }
2398
2399
    /**
2400
     * Get the current array from the "Arrayy"-object.
2401
     *
2402
     * alias for "toArray()"
2403
     *
2404
     * @param bool $convertAllArrayyElements <p>
2405
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2406
     *                                       </p>
2407
     * @param bool $preserveKeys             <p>
2408
     *                                       e.g.: A generator maybe return the same key more then once,
2409
     *                                       so maybe you will ignore the keys.
2410
     *                                       </p>
2411
     *
2412
     * @return array
2413
     *
2414
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2415
     * @psalm-mutation-free
2416
     *
2417
     * @see Arrayy::toArray()
2418
     */
2419 481
    public function getArray(
2420
        bool $convertAllArrayyElements = false,
2421
        bool $preserveKeys = true
2422
    ): array {
2423 481
        return $this->toArray(
2424 481
            $convertAllArrayyElements,
2425 481
            $preserveKeys
2426
        );
2427
    }
2428
2429
    /**
2430
     * Get the current array from the "Arrayy"-object as list.
2431
     *
2432
     * alias for "toList()"
2433
     *
2434
     * @param bool $convertAllArrayyElements <p>
2435
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2436
     *                                       </p>
2437
     *
2438
     * @return array
2439
     *
2440
     * @psalm-return array<int,mixed>|array<int,T>
2441
     * @psalm-mutation-free
2442
     *
2443
     * @see Arrayy::toList()
2444
     */
2445 1
    public function getList(bool $convertAllArrayyElements = false): array
2446
    {
2447 1
        return $this->toList($convertAllArrayyElements);
2448
    }
2449
2450
    /**
2451
     * Returns the values from a single column of the input array, identified by
2452
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2453
     *
2454
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2455
     * array by the values from the $indexKey column in the input array.
2456
     *
2457
     * @param mixed $columnKey
2458
     * @param mixed $indexKey
2459
     *
2460
     * @return static
2461
     *                <p>(Immutable)</p>
2462
     *
2463
     * @psalm-return static<TKey,T>
2464
     * @psalm-mutation-free
2465
     */
2466 1
    public function getColumn($columnKey = null, $indexKey = null): self
2467
    {
2468 1
        return static::create(
2469 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2470 1
            $this->iteratorClass,
2471 1
            false
2472
        );
2473
    }
2474
2475
    /**
2476
     * Get the current array from the "Arrayy"-object as generator.
2477
     *
2478
     * @return \Generator
2479
     *
2480
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2481
     * @psalm-mutation-free
2482
     */
2483 976
    public function getGenerator(): \Generator
2484
    {
2485 976
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2486 42
            yield from $this->generator;
2487
        }
2488
2489 976
        yield from $this->array;
2490 932
    }
2491
2492
    /**
2493
     * alias: for "Arrayy->keys()"
2494
     *
2495
     * @return static
2496
     *                <p>(Immutable)</p>
2497
     *
2498
     * @see          Arrayy::keys()
2499
     *
2500
     * @psalm-return static<array-key,TKey>
2501
     * @psalm-mutation-free
2502
     */
2503 2
    public function getKeys()
2504
    {
2505 2
        return $this->keys();
2506
    }
2507
2508
    /**
2509
     * Get the current array from the "Arrayy"-object as object.
2510
     *
2511
     * @return \stdClass
2512
     */
2513 4
    public function getObject(): \stdClass
2514
    {
2515 4
        return self::arrayToObject($this->toArray());
2516
    }
2517
2518
    /**
2519
     * alias: for "Arrayy->randomImmutable()"
2520
     *
2521
     * @return static
2522
     *                <p>(Immutable)</p>
2523
     *
2524
     * @see          Arrayy::randomImmutable()
2525
     *
2526
     * @psalm-return static<int|array-key,T>
2527
     */
2528 4
    public function getRandom(): self
2529
    {
2530 4
        return $this->randomImmutable();
2531
    }
2532
2533
    /**
2534
     * alias: for "Arrayy->randomKey()"
2535
     *
2536
     * @return mixed
2537
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2538
     *
2539
     * @see Arrayy::randomKey()
2540
     */
2541 3
    public function getRandomKey()
2542
    {
2543 3
        return $this->randomKey();
2544
    }
2545
2546
    /**
2547
     * alias: for "Arrayy->randomKeys()"
2548
     *
2549
     * @param int $number
2550
     *
2551
     * @return static
2552
     *                <p>(Immutable)</p>
2553
     *
2554
     * @see          Arrayy::randomKeys()
2555
     *
2556
     * @psalm-return static<TKey,T>
2557
     */
2558 8
    public function getRandomKeys(int $number): self
2559
    {
2560 8
        return $this->randomKeys($number);
2561
    }
2562
2563
    /**
2564
     * alias: for "Arrayy->randomValue()"
2565
     *
2566
     * @return mixed
2567
     *               <p>Get a random value or null if there wasn't a value.</p>
2568
     *
2569
     * @see Arrayy::randomValue()
2570
     */
2571 3
    public function getRandomValue()
2572
    {
2573 3
        return $this->randomValue();
2574
    }
2575
2576
    /**
2577
     * alias: for "Arrayy->randomValues()"
2578
     *
2579
     * @param int $number
2580
     *
2581
     * @return static
2582
     *                <p>(Immutable)</p>
2583
     *
2584
     * @see          Arrayy::randomValues()
2585
     *
2586
     * @psalm-return static<TKey,T>
2587
     */
2588 6
    public function getRandomValues(int $number): self
2589
    {
2590 6
        return $this->randomValues($number);
2591
    }
2592
2593
    /**
2594
     * Gets all values.
2595
     *
2596
     * @return static
2597
     *                <p>The values of all elements in this array, in the order they
2598
     *                appear in the array.</p>
2599
     *
2600
     * @psalm-return static<TKey,T>
2601
     */
2602 4
    public function getValues()
2603
    {
2604 4
        $this->generatorToArray(false);
2605
2606 4
        return static::create(
2607 4
            \array_values($this->array),
2608 4
            $this->iteratorClass,
2609 4
            false
2610
        );
2611
    }
2612
2613
    /**
2614
     * Gets all values via Generator.
2615
     *
2616
     * @return \Generator
2617
     *                    <p>The values of all elements in this array, in the order they
2618
     *                    appear in the array as Generator.</p>
2619
     *
2620
     * @psalm-return \Generator<TKey,T>
2621
     */
2622 4
    public function getValuesYield(): \Generator
2623
    {
2624 4
        yield from $this->getGenerator();
2625 4
    }
2626
2627
    /**
2628
     * Group values from a array according to the results of a closure.
2629
     *
2630
     * @param callable|string $grouper  <p>A callable function name.</p>
2631
     * @param bool            $saveKeys
2632
     *
2633
     * @return static
2634
     *                <p>(Immutable)</p>
2635
     *
2636
     * @psalm-return static<TKey,T>
2637
     * @psalm-mutation-free
2638
     */
2639 4
    public function group($grouper, bool $saveKeys = false): self
2640
    {
2641
        // init
2642 4
        $result = [];
2643
2644
        // Iterate over values, group by property/results from closure.
2645 4
        foreach ($this->getGenerator() as $key => $value) {
2646 4
            if (\is_callable($grouper) === true) {
2647 3
                $groupKey = $grouper($value, $key);
2648
            } else {
2649 1
                $groupKey = $this->get($grouper);
2650
            }
2651
2652 4
            $newValue = $this->get($groupKey, null, $result);
2653
2654 4
            if ($groupKey instanceof self) {
2655
                $groupKey = $groupKey->toArray();
2656
            }
2657
2658 4
            if ($newValue instanceof self) {
2659 4
                $newValue = $newValue->toArray();
2660
            }
2661
2662
            // Add to results.
2663 4
            if ($groupKey !== null) {
2664 3
                if ($saveKeys) {
2665 2
                    $result[$groupKey] = $newValue;
2666 2
                    $result[$groupKey][$key] = $value;
2667
                } else {
2668 1
                    $result[$groupKey] = $newValue;
2669 4
                    $result[$groupKey][] = $value;
2670
                }
2671
            }
2672
        }
2673
2674 4
        return static::create(
2675 4
            $result,
2676 4
            $this->iteratorClass,
2677 4
            false
2678
        );
2679
    }
2680
2681
    /**
2682
     * Check if an array has a given key.
2683
     *
2684
     * @param mixed $key
2685
     *
2686
     * @return bool
2687
     */
2688 23
    public function has($key): bool
2689
    {
2690 23
        static $UN_FOUND = null;
2691
2692 23
        if ($UN_FOUND === null) {
2693
            // Generate unique string to use as marker.
2694 1
            $UN_FOUND = \uniqid('arrayy', true);
2695
        }
2696
2697 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2698
    }
2699
2700
    /**
2701
     * Check if an array has a given value.
2702
     *
2703
     * INFO: if you need to search recursive please use ```contains()```
2704
     *
2705
     * @param mixed $value
2706
     *
2707
     * @return bool
2708
     */
2709 1
    public function hasValue($value): bool
2710
    {
2711 1
        return $this->contains($value);
2712
    }
2713
2714
    /**
2715
     * Implodes the values of this array.
2716
     *
2717
     * @param string $glue
2718
     *
2719
     * @return string
2720
     */
2721 28
    public function implode(string $glue = ''): string
2722
    {
2723 28
        return $this->implode_recursive($glue, $this->toArray(), false);
2724
    }
2725
2726
    /**
2727
     * Implodes the keys of this array.
2728
     *
2729
     * @param string $glue
2730
     *
2731
     * @return string
2732
     */
2733 8
    public function implodeKeys(string $glue = ''): string
2734
    {
2735 8
        return $this->implode_recursive($glue, $this->toArray(), true);
2736
    }
2737
2738
    /**
2739
     * Given a list and an iterate-function that returns
2740
     * a key for each element in the list (or a property name),
2741
     * returns an object with an index of each item.
2742
     *
2743
     * @param mixed $key
2744
     *
2745
     * @return static
2746
     *                <p>(Immutable)</p>
2747
     *
2748
     * @psalm-return static<TKey,T>
2749
     * @psalm-mutation-free
2750
     */
2751 4
    public function indexBy($key): self
2752
    {
2753
        // init
2754 4
        $results = [];
2755
2756 4
        foreach ($this->getGenerator() as $a) {
2757 4
            if (\array_key_exists($key, $a) === true) {
2758 4
                $results[$a[$key]] = $a;
2759
            }
2760
        }
2761
2762 4
        return static::create(
2763 4
            $results,
2764 4
            $this->iteratorClass,
2765 4
            false
2766
        );
2767
    }
2768
2769
    /**
2770
     * alias: for "Arrayy->searchIndex()"
2771
     *
2772
     * @param mixed $value <p>The value to search for.</p>
2773
     *
2774
     * @return false|mixed
2775
     *
2776
     * @see Arrayy::searchIndex()
2777
     */
2778 4
    public function indexOf($value)
2779
    {
2780 4
        return $this->searchIndex($value);
2781
    }
2782
2783
    /**
2784
     * Get everything but the last..$to items.
2785
     *
2786
     * @param int $to
2787
     *
2788
     * @return static
2789
     *                <p>(Immutable)</p>
2790
     *
2791
     * @psalm-return static<TKey,T>
2792
     * @psalm-mutation-free
2793
     */
2794 12
    public function initial(int $to = 1): self
2795
    {
2796 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
2797
    }
2798
2799
    /**
2800
     * Return an array with all elements found in input array.
2801
     *
2802
     * @param array $search
2803
     * @param bool  $keepKeys
2804
     *
2805
     * @return static
2806
     *                <p>(Immutable)</p>
2807
     *
2808
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
2809
     * @psalm-return static<TKey,T>
2810
     * @psalm-mutation-free
2811
     */
2812 4
    public function intersection(array $search, bool $keepKeys = false): self
2813
    {
2814 4
        if ($keepKeys) {
2815
            /**
2816
             * @psalm-suppress MissingClosureReturnType
2817
             * @psalm-suppress MissingClosureParamType
2818
             */
2819 1
            return static::create(
2820 1
                \array_uintersect(
2821 1
                    $this->toArray(),
2822 1
                    $search,
2823
                    static function ($a, $b) {
2824 1
                        return $a === $b ? 0 : -1;
2825 1
                    }
2826
                ),
2827 1
                $this->iteratorClass,
2828 1
                false
2829
            );
2830
        }
2831
2832 3
        return static::create(
2833 3
            \array_values(\array_intersect($this->toArray(), $search)),
2834 3
            $this->iteratorClass,
2835 3
            false
2836
        );
2837
    }
2838
2839
    /**
2840
     * Return an array with all elements found in input array.
2841
     *
2842
     * @param array ...$array
2843
     *
2844
     * @return static
2845
     *                <p>(Immutable)</p>
2846
     *
2847
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2848
     * @psalm-return static<TKey,T>
2849
     * @psalm-mutation-free
2850
     */
2851 1
    public function intersectionMulti(...$array): self
2852
    {
2853 1
        return static::create(
2854 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
2855 1
            $this->iteratorClass,
2856 1
            false
2857
        );
2858
    }
2859
2860
    /**
2861
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2862
     *
2863
     * @param array $search
2864
     *
2865
     * @return bool
2866
     *
2867
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
2868
     */
2869 1
    public function intersects(array $search): bool
2870
    {
2871 1
        return $this->intersection($search)->count() > 0;
2872
    }
2873
2874
    /**
2875
     * Invoke a function on all of an array's values.
2876
     *
2877
     * @param callable $callable
2878
     * @param mixed    $arguments
2879
     *
2880
     * @return static
2881
     *                <p>(Immutable)</p>
2882
     *
2883
     * @psalm-param  callable(T=,mixed):mixed $callable
2884
     * @psalm-return static<TKey,T>
2885
     * @psalm-mutation-free
2886
     */
2887 1
    public function invoke($callable, $arguments = []): self
2888
    {
2889
        // If one argument given for each iteration, create an array for it.
2890 1
        if (\is_array($arguments) === false) {
2891 1
            $arguments = \array_fill(
2892 1
                0,
2893 1
                $this->count(),
2894 1
                $arguments
2895
            );
2896
        }
2897
2898
        // If the callable has arguments, pass them.
2899 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...
2900 1
            $array = \array_map($callable, $this->toArray(), $arguments);
2901
        } else {
2902 1
            $array = $this->map($callable);
2903
        }
2904
2905 1
        return static::create(
2906 1
            $array,
2907 1
            $this->iteratorClass,
2908 1
            false
2909
        );
2910
    }
2911
2912
    /**
2913
     * Check whether array is associative or not.
2914
     *
2915
     * @param bool $recursive
2916
     *
2917
     * @return bool
2918
     *              <p>Returns true if associative, false otherwise.</p>
2919
     */
2920 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...
2921
    {
2922 15
        if ($this->isEmpty()) {
2923 3
            return false;
2924
        }
2925
2926 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2927 13
            if ((string) $key !== $key) {
2928 13
                return false;
2929
            }
2930
        }
2931
2932 3
        return true;
2933
    }
2934
2935
    /**
2936
     * Check if a given key or keys are empty.
2937
     *
2938
     * @param int|int[]|string|string[]|null $keys
2939
     *
2940
     * @return bool
2941
     *              <p>Returns true if empty, false otherwise.</p>
2942
     * @psalm-mutation-free
2943
     */
2944 38
    public function isEmpty($keys = null): bool
2945
    {
2946 38
        if ($this->generator) {
2947
            return $this->toArray() === [];
2948
        }
2949
2950 38
        if ($keys === null) {
2951 38
            return $this->array === [];
2952
        }
2953
2954
        foreach ((array) $keys as $key) {
2955
            if (!empty($this->get($key))) {
2956
                return false;
2957
            }
2958
        }
2959
2960
        return true;
2961
    }
2962
2963
    /**
2964
     * Check if the current array is equal to the given "$array" or not.
2965
     *
2966
     * @param array $array
2967
     *
2968
     * @return bool
2969
     *
2970
     * @psalm-param array<mixed,mixed> $array
2971
     */
2972 1
    public function isEqual(array $array): bool
2973
    {
2974 1
        return $this->toArray() === $array;
2975
    }
2976
2977
    /**
2978
     * Check if the current array is a multi-array.
2979
     *
2980
     * @return bool
2981
     */
2982 22
    public function isMultiArray(): bool
2983
    {
2984
        return !(
2985 22
            \count($this->toArray(), \COUNT_NORMAL)
2986
            ===
2987 22
            \count($this->toArray(), \COUNT_RECURSIVE)
2988
        );
2989
    }
2990
2991
    /**
2992
     * Check whether array is numeric or not.
2993
     *
2994
     * @return bool
2995
     *              <p>Returns true if numeric, false otherwise.</p>
2996
     */
2997 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...
2998
    {
2999 5
        if ($this->isEmpty()) {
3000 2
            return false;
3001
        }
3002
3003 4
        foreach ($this->keys()->getGenerator() as $key) {
3004 4
            if ((int) $key !== $key) {
3005 4
                return false;
3006
            }
3007
        }
3008
3009 2
        return true;
3010
    }
3011
3012
    /**
3013
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3014
     *
3015
     * @param bool $recursive
3016
     *
3017
     * @return bool
3018
     * @psalm-mutation-free
3019
     */
3020 9
    public function isSequential(bool $recursive = false): bool
3021
    {
3022
3023
        // recursive
3024
3025 9
        if ($recursive === true) {
3026
            return $this->array_keys_recursive($this->toArray())
3027
                   ===
3028
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3029
        }
3030
3031
        // non recursive
3032
3033 9
        return \array_keys($this->toArray())
3034
               ===
3035 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3036
    }
3037
3038
    /**
3039
     * @return array
3040
     *
3041
     * @psalm-return array<TKey,T>
3042
     */
3043
    public function jsonSerialize(): array
3044
    {
3045
        return $this->toArray();
3046
    }
3047
3048
    /**
3049
     * Gets the key/index of the element at the current internal iterator position.
3050
     *
3051
     * @return int|string|null
3052
     */
3053
    public function key()
3054
    {
3055
        return \key($this->array);
3056
    }
3057
3058
    /**
3059
     * Checks if the given key exists in the provided array.
3060
     *
3061
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3062
     *       then you need to use "Arrayy->offsetExists()".
3063
     *
3064
     * @param int|string $key the key to look for
3065
     *
3066
     * @return bool
3067
     */
3068 127
    public function keyExists($key): bool
3069
    {
3070 127
        return \array_key_exists($key, $this->array);
3071
    }
3072
3073
    /**
3074
     * Get all keys from the current array.
3075
     *
3076
     * @param bool       $recursive     [optional] <p>
3077
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3078
     *                                  </p>
3079
     * @param mixed|null $search_values [optional] <p>
3080
     *                                  If specified, then only keys containing these values are returned.
3081
     *                                  </p>
3082
     * @param bool       $strict        [optional] <p>
3083
     *                                  Determines if strict comparison (===) should be used during the search.
3084
     *                                  </p>
3085
     *
3086
     * @return static
3087
     *                <p>(Immutable) An array of all the keys in input.</p>
3088
     *
3089
     * @psalm-return static<array-key,TKey>
3090
     * @psalm-mutation-free
3091
     */
3092 29
    public function keys(
3093
        bool $recursive = false,
3094
        $search_values = null,
3095
        bool $strict = true
3096
    ): self {
3097
3098
        // recursive
3099
3100 29
        if ($recursive === true) {
3101 4
            $array = $this->array_keys_recursive(
3102 4
                null,
3103 4
                $search_values,
3104 4
                $strict
3105
            );
3106
3107 4
            return static::create(
3108 4
                $array,
3109 4
                $this->iteratorClass,
3110 4
                false
3111
            );
3112
        }
3113
3114
        // non recursive
3115
3116 28
        if ($search_values === null) {
3117
            $arrayFunction = function (): \Generator {
3118 28
                foreach ($this->getGenerator() as $key => $value) {
3119 26
                    yield $key;
3120
                }
3121 28
            };
3122
        } else {
3123
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3124 1
                $is_array_tmp = \is_array($search_values);
3125
3126 1
                foreach ($this->getGenerator() as $key => $value) {
3127 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...
3128
                        (
3129 1
                            $is_array_tmp === false
3130
                            &&
3131 1
                            $strict === true
3132
                            &&
3133 1
                            $search_values === $value
3134
                        )
3135
                        ||
3136
                        (
3137 1
                            $is_array_tmp === false
3138
                            &&
3139 1
                            $strict === false
3140
                            &&
3141 1
                            $search_values == $value
3142
                        )
3143
                        ||
3144
                        (
3145 1
                            $is_array_tmp === true
3146
                            &&
3147 1
                            \in_array($value, $search_values, $strict)
3148
                        )
3149
                    ) {
3150 1
                        yield $key;
3151
                    }
3152
                }
3153 1
            };
3154
        }
3155
3156 28
        return static::create(
3157 28
            $arrayFunction,
3158 28
            $this->iteratorClass,
3159 28
            false
3160
        );
3161
    }
3162
3163
    /**
3164
     * Sort an array by key in reverse order.
3165
     *
3166
     * @param int $sort_flags [optional] <p>
3167
     *                        You may modify the behavior of the sort using the optional
3168
     *                        parameter sort_flags, for details
3169
     *                        see sort.
3170
     *                        </p>
3171
     *
3172
     * @return $this
3173
     *               <p>(Mutable) Return this Arrayy object.</p>
3174
     *
3175
     * @psalm-return static<TKey,T>
3176
     */
3177 4
    public function krsort(int $sort_flags = 0): self
3178
    {
3179 4
        $this->generatorToArray();
3180
3181 4
        \krsort($this->array, $sort_flags);
3182
3183 4
        return $this;
3184
    }
3185
3186
    /**
3187
     * Sort an array by key in reverse order.
3188
     *
3189
     * @param int $sort_flags [optional] <p>
3190
     *                        You may modify the behavior of the sort using the optional
3191
     *                        parameter sort_flags, for details
3192
     *                        see sort.
3193
     *                        </p>
3194
     *
3195
     * @return $this
3196
     *               <p>(Immutable) Return this Arrayy object.</p>
3197
     *
3198
     * @psalm-return static<TKey,T>
3199
     * @psalm-mutation-free
3200
     */
3201 4
    public function krsortImmutable(int $sort_flags = 0): self
3202
    {
3203 4
        $that = clone $this;
3204
3205
        /**
3206
         * @psalm-suppress ImpureMethodCall - object is already cloned
3207
         */
3208 4
        $that->krsort($sort_flags);
3209
3210 4
        return $that;
3211
    }
3212
3213
    /**
3214
     * Get the last value from the current array.
3215
     *
3216
     * @return mixed|null
3217
     *                    <p>Return null if there wasn't a element.</p>
3218
     * @psalm-mutation-free
3219
     */
3220 17
    public function last()
3221
    {
3222 17
        $key_last = $this->lastKey();
3223 17
        if ($key_last === null) {
3224 2
            return null;
3225
        }
3226
3227 15
        return $this->get($key_last);
3228
    }
3229
3230
    /**
3231
     * Get the last key from the current array.
3232
     *
3233
     * @return mixed|null
3234
     *                    <p>Return null if there wasn't a element.</p>
3235
     * @psalm-mutation-free
3236
     */
3237 21
    public function lastKey()
3238
    {
3239 21
        $this->generatorToArray();
3240
3241 21
        return \array_key_last($this->array);
3242
    }
3243
3244
    /**
3245
     * Get the last value(s) from the current array.
3246
     *
3247
     * @param int|null $number
3248
     *
3249
     * @return static
3250
     *                <p>(Immutable)</p>
3251
     *
3252
     * @psalm-return static<TKey,T>
3253
     * @psalm-mutation-free
3254
     */
3255 13
    public function lastsImmutable(int $number = null): self
3256
    {
3257 13
        if ($this->isEmpty()) {
3258 1
            return static::create(
3259 1
                [],
3260 1
                $this->iteratorClass,
3261 1
                false
3262
            );
3263
        }
3264
3265 12
        if ($number === null) {
3266 8
            $poppedValue = $this->last();
3267
3268 8
            if ($poppedValue === null) {
3269 1
                $poppedValue = [$poppedValue];
3270
            } else {
3271 7
                $poppedValue = (array) $poppedValue;
3272
            }
3273
3274 8
            $arrayy = static::create(
3275 8
                $poppedValue,
3276 8
                $this->iteratorClass,
3277 8
                false
3278
            );
3279
        } else {
3280 4
            $number = (int) $number;
3281 4
            $arrayy = $this->rest(-$number);
3282
        }
3283
3284 12
        return $arrayy;
3285
    }
3286
3287
    /**
3288
     * Get the last value(s) from the current array.
3289
     *
3290
     * @param int|null $number
3291
     *
3292
     * @return $this
3293
     *               <p>(Mutable)</p>
3294
     *
3295
     * @psalm-return static<TKey,T>
3296
     */
3297 13
    public function lastsMutable(int $number = null): self
3298
    {
3299 13
        if ($this->isEmpty()) {
3300 1
            return $this;
3301
        }
3302
3303 12
        if ($number === null) {
3304 8
            $poppedValue = $this->last();
3305
3306 8
            if ($poppedValue === null) {
3307 1
                $poppedValue = [$poppedValue];
3308
            } else {
3309 7
                $poppedValue = (array) $poppedValue;
3310
            }
3311
3312 8
            $this->array = static::create(
3313 8
                $poppedValue,
3314 8
                $this->iteratorClass,
3315 8
                false
3316 8
            )->toArray();
3317
        } else {
3318 4
            $number = (int) $number;
3319 4
            $this->array = $this->rest(-$number)->toArray();
3320
        }
3321
3322 12
        $this->generator = null;
3323
3324 12
        return $this;
3325
    }
3326
3327
    /**
3328
     * Count the values from the current array.
3329
     *
3330
     * alias: for "Arrayy->count()"
3331
     *
3332
     * @param int $mode
3333
     *
3334
     * @return int
3335
     *
3336
     * @see Arrayy::count()
3337
     */
3338 20
    public function length(int $mode = \COUNT_NORMAL): int
3339
    {
3340 20
        return $this->count($mode);
3341
    }
3342
3343
    /**
3344
     * Apply the given function to the every element of the array,
3345
     * collecting the results.
3346
     *
3347
     * @param callable $callable
3348
     * @param bool     $useKeyAsSecondParameter
3349
     * @param mixed    ...$arguments
3350
     *
3351
     * @return static
3352
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3353
     *
3354
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3355
     * @psalm-return static<TKey,T>
3356
     * @psalm-mutation-free
3357
     */
3358 5
    public function map(
3359
        callable $callable,
3360
        bool $useKeyAsSecondParameter = false,
3361
        ...$arguments
3362
    ) {
3363
        /**
3364
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3365
         */
3366 5
        $useArguments = \func_num_args() > 2;
3367
3368 5
        return static::create(
3369
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3370 5
                foreach ($this->getGenerator() as $key => $value) {
3371 4
                    if ($useArguments) {
3372 3
                        if ($useKeyAsSecondParameter) {
3373
                            yield $key => $callable($value, $key, ...$arguments);
3374
                        } else {
3375 3
                            yield $key => $callable($value, ...$arguments);
3376
                        }
3377
                    } else {
3378
                        /** @noinspection NestedPositiveIfStatementsInspection */
3379 4
                        if ($useKeyAsSecondParameter) {
3380
                            yield $key => $callable($value, $key);
3381
                        } else {
3382 4
                            yield $key => $callable($value);
3383
                        }
3384
                    }
3385
                }
3386 5
            },
3387 5
            $this->iteratorClass,
3388 5
            false
3389
        );
3390
    }
3391
3392
    /**
3393
     * Check if all items in current array match a truth test.
3394
     *
3395
     * @param \Closure $closure
3396
     *
3397
     * @return bool
3398
     */
3399 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...
3400
    {
3401 15
        if ($this->count() === 0) {
3402 2
            return false;
3403
        }
3404
3405 13
        foreach ($this->getGenerator() as $key => $value) {
3406 13
            $value = $closure($value, $key);
3407
3408 13
            if ($value === false) {
3409 13
                return false;
3410
            }
3411
        }
3412
3413 7
        return true;
3414
    }
3415
3416
    /**
3417
     * Check if any item in the current array matches a truth test.
3418
     *
3419
     * @param \Closure $closure
3420
     *
3421
     * @return bool
3422
     */
3423 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...
3424
    {
3425 14
        if ($this->count() === 0) {
3426 2
            return false;
3427
        }
3428
3429 12
        foreach ($this->getGenerator() as $key => $value) {
3430 12
            $value = $closure($value, $key);
3431
3432 12
            if ($value === true) {
3433 12
                return true;
3434
            }
3435
        }
3436
3437 4
        return false;
3438
    }
3439
3440
    /**
3441
     * Get the max value from an array.
3442
     *
3443
     * @return false|mixed
3444
     *                     <p>Will return false if there are no values.</p>
3445
     */
3446 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...
3447
    {
3448 10
        if ($this->count() === 0) {
3449 1
            return false;
3450
        }
3451
3452 9
        $max = false;
3453 9
        foreach ($this->getGenerator() as $value) {
3454
            if (
3455 9
                $max === false
3456
                ||
3457 9
                $value > $max
3458
            ) {
3459 9
                $max = $value;
3460
            }
3461
        }
3462
3463 9
        return $max;
3464
    }
3465
3466
    /**
3467
     * Merge the new $array into the current array.
3468
     *
3469
     * - keep key,value from the current array, also if the index is in the new $array
3470
     *
3471
     * @param array $array
3472
     * @param bool  $recursive
3473
     *
3474
     * @return static
3475
     *                <p>(Immutable)</p>
3476
     *
3477
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3478
     * @psalm-return static<int|TKey,T>
3479
     * @psalm-mutation-free
3480
     */
3481 25 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...
3482
    {
3483 25
        if ($recursive === true) {
3484 4
            $result = \array_replace_recursive($this->toArray(), $array);
3485
        } else {
3486 21
            $result = \array_replace($this->toArray(), $array);
3487
        }
3488
3489 25
        return static::create(
3490 25
            $result,
3491 25
            $this->iteratorClass,
3492 25
            false
3493
        );
3494
    }
3495
3496
    /**
3497
     * Merge the new $array into the current array.
3498
     *
3499
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3500
     * - create new indexes
3501
     *
3502
     * @param array $array
3503
     * @param bool  $recursive
3504
     *
3505
     * @return static
3506
     *                <p>(Immutable)</p>
3507
     *
3508
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3509
     * @psalm-return static<TKey,T>
3510
     * @psalm-mutation-free
3511
     */
3512 16 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...
3513
    {
3514 16
        if ($recursive === true) {
3515 4
            $result = \array_merge_recursive($this->toArray(), $array);
3516
        } else {
3517 12
            $result = \array_merge($this->toArray(), $array);
3518
        }
3519
3520 16
        return static::create(
3521 16
            $result,
3522 16
            $this->iteratorClass,
3523 16
            false
3524
        );
3525
    }
3526
3527
    /**
3528
     * Merge the the current array into the $array.
3529
     *
3530
     * - use key,value from the new $array, also if the index is in the current array
3531
     *
3532
     * @param array $array
3533
     * @param bool  $recursive
3534
     *
3535
     * @return static
3536
     *                <p>(Immutable)</p>
3537
     *
3538
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3539
     * @psalm-return static<TKey,T>
3540
     * @psalm-mutation-free
3541
     */
3542 16 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...
3543
    {
3544 16
        if ($recursive === true) {
3545 4
            $result = \array_replace_recursive($array, $this->toArray());
3546
        } else {
3547 12
            $result = \array_replace($array, $this->toArray());
3548
        }
3549
3550 16
        return static::create(
3551 16
            $result,
3552 16
            $this->iteratorClass,
3553 16
            false
3554
        );
3555
    }
3556
3557
    /**
3558
     * Merge the current array into the new $array.
3559
     *
3560
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3561
     * - create new indexes
3562
     *
3563
     * @param array $array
3564
     * @param bool  $recursive
3565
     *
3566
     * @return static
3567
     *                <p>(Immutable)</p>
3568
     *
3569
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3570
     * @psalm-return static<TKey,T>
3571
     * @psalm-mutation-free
3572
     */
3573 17 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...
3574
    {
3575 17
        if ($recursive === true) {
3576 4
            $result = \array_merge_recursive($array, $this->toArray());
3577
        } else {
3578 13
            $result = \array_merge($array, $this->toArray());
3579
        }
3580
3581 17
        return static::create(
3582 17
            $result,
3583 17
            $this->iteratorClass,
3584 17
            false
3585
        );
3586
    }
3587
3588
    /**
3589
     * @return ArrayyMeta|static
3590
     */
3591 15
    public static function meta()
3592
    {
3593 15
        return (new ArrayyMeta())->getMetaObject(static::class);
3594
    }
3595
3596
    /**
3597
     * Get the min value from an array.
3598
     *
3599
     * @return false|mixed
3600
     *                     <p>Will return false if there are no values.</p>
3601
     */
3602 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...
3603
    {
3604 10
        if ($this->count() === 0) {
3605 1
            return false;
3606
        }
3607
3608 9
        $min = false;
3609 9
        foreach ($this->getGenerator() as $value) {
3610
            if (
3611 9
                $min === false
3612
                ||
3613 9
                $value < $min
3614
            ) {
3615 9
                $min = $value;
3616
            }
3617
        }
3618
3619 9
        return $min;
3620
    }
3621
3622
    /**
3623
     * Get the most used value from the array.
3624
     *
3625
     * @return mixed|null
3626
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
3627
     * @psalm-mutation-free
3628
     */
3629 3
    public function mostUsedValue()
3630
    {
3631 3
        return $this->countValues()->arsortImmutable()->firstKey();
3632
    }
3633
3634
    /**
3635
     * Get the most used value from the array.
3636
     *
3637
     * @param int|null $number <p>How many values you will take?</p>
3638
     *
3639
     * @return static
3640
     *                <p>(Immutable)</p>
3641
     *
3642
     * @psalm-return static<TKey,T>
3643
     * @psalm-mutation-free
3644
     */
3645 3
    public function mostUsedValues(int $number = null): self
3646
    {
3647 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
3648
    }
3649
3650
    /**
3651
     * Move an array element to a new index.
3652
     *
3653
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3654
     *
3655
     * @param int|string $from
3656
     * @param int        $to
3657
     *
3658
     * @return static
3659
     *                <p>(Immutable)</p>
3660
     *
3661
     * @psalm-return static<TKey,T>
3662
     * @psalm-mutation-free
3663
     */
3664 1
    public function moveElement($from, $to): self
3665
    {
3666 1
        $array = $this->toArray();
3667
3668 1
        if ((int) $from === $from) {
3669 1
            $tmp = \array_splice($array, $from, 1);
3670 1
            \array_splice($array, (int) $to, 0, $tmp);
3671 1
            $output = $array;
3672 1
        } elseif ((string) $from === $from) {
3673 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3674 1
            $itemToMove = $array[$from];
3675 1
            if ($indexToMove !== false) {
3676 1
                \array_splice($array, $indexToMove, 1);
3677
            }
3678 1
            $i = 0;
3679 1
            $output = [];
3680 1
            foreach ($array as $key => $item) {
3681 1
                if ($i === $to) {
3682 1
                    $output[$from] = $itemToMove;
3683
                }
3684 1
                $output[$key] = $item;
3685 1
                ++$i;
3686
            }
3687
        } else {
3688
            $output = [];
3689
        }
3690
3691 1
        return static::create(
3692 1
            $output,
3693 1
            $this->iteratorClass,
3694 1
            false
3695
        );
3696
    }
3697
3698
    /**
3699
     * Move an array element to the first place.
3700
     *
3701
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3702
     *       loss the keys of an indexed array.
3703
     *
3704
     * @param int|string $key
3705
     *
3706
     * @return static
3707
     *                <p>(Immutable)</p>
3708
     *
3709
     * @psalm-return static<TKey,T>
3710
     * @psalm-mutation-free
3711
     */
3712 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...
3713
    {
3714 1
        $array = $this->toArray();
3715
3716 1
        if ($this->offsetExists($key)) {
3717 1
            $tmpValue = $this->get($key);
3718 1
            unset($array[$key]);
3719 1
            $array = [$key => $tmpValue] + $array;
3720
        }
3721
3722 1
        return static::create(
3723 1
            $array,
3724 1
            $this->iteratorClass,
3725 1
            false
3726
        );
3727
    }
3728
3729
    /**
3730
     * Move an array element to the last place.
3731
     *
3732
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3733
     *       loss the keys of an indexed array.
3734
     *
3735
     * @param int|string $key
3736
     *
3737
     * @return static
3738
     *                <p>(Immutable)</p>
3739
     *
3740
     * @psalm-return static<TKey,T>
3741
     * @psalm-mutation-free
3742
     */
3743 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...
3744
    {
3745 1
        $array = $this->toArray();
3746
3747 1
        if ($this->offsetExists($key)) {
3748 1
            $tmpValue = $this->get($key);
3749 1
            unset($array[$key]);
3750 1
            $array += [$key => $tmpValue];
3751
        }
3752
3753 1
        return static::create(
3754 1
            $array,
3755 1
            $this->iteratorClass,
3756 1
            false
3757
        );
3758
    }
3759
3760
    /**
3761
     * Moves the internal iterator position to the next element and returns this element.
3762
     *
3763
     * @return false|mixed
3764
     *                     <p>(Mutable) Will return false if there are no values.</p>
3765
     */
3766
    public function next()
3767
    {
3768
        return \next($this->array);
3769
    }
3770
3771
    /**
3772
     * Get the next nth keys and values from the array.
3773
     *
3774
     * @param int $step
3775
     * @param int $offset
3776
     *
3777
     * @return static
3778
     *                <p>(Immutable)</p>
3779
     *
3780
     * @psalm-return static<TKey,T>
3781
     * @psalm-mutation-free
3782
     */
3783 1
    public function nth(int $step, int $offset = 0): self
3784
    {
3785
        $arrayFunction = function () use ($step, $offset): \Generator {
3786 1
            $position = 0;
3787 1
            foreach ($this->getGenerator() as $key => $value) {
3788 1
                if ($position++ % $step !== $offset) {
3789 1
                    continue;
3790
                }
3791
3792 1
                yield $key => $value;
3793
            }
3794 1
        };
3795
3796 1
        return static::create(
3797 1
            $arrayFunction,
3798 1
            $this->iteratorClass,
3799 1
            false
3800
        );
3801
    }
3802
3803
    /**
3804
     * Get a subset of the items from the given array.
3805
     *
3806
     * @param mixed[] $keys
3807
     *
3808
     * @return static
3809
     *                <p>(Immutable)</p>
3810
     *
3811
     * @psalm-return static<TKey,T>
3812
     * @psalm-mutation-free
3813
     */
3814 1
    public function only(array $keys): self
3815
    {
3816 1
        $array = $this->toArray();
3817
3818 1
        return static::create(
3819 1
            \array_intersect_key($array, \array_flip($keys)),
3820 1
            $this->iteratorClass,
3821 1
            false
3822
        );
3823
    }
3824
3825
    /**
3826
     * Pad array to the specified size with a given value.
3827
     *
3828
     * @param int   $size  <p>Size of the result array.</p>
3829
     * @param mixed $value <p>Empty value by default.</p>
3830
     *
3831
     * @return static
3832
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3833
     *
3834
     * @psalm-return static<TKey,T>
3835
     * @psalm-mutation-free
3836
     */
3837 5
    public function pad(int $size, $value): self
3838
    {
3839 5
        return static::create(
3840 5
            \array_pad($this->toArray(), $size, $value),
3841 5
            $this->iteratorClass,
3842 5
            false
3843
        );
3844
    }
3845
3846
    /**
3847
     * Partitions this array in two array according to a predicate.
3848
     * Keys are preserved in the resulting array.
3849
     *
3850
     * @param \Closure $closure
3851
     *                          <p>The predicate on which to partition.</p>
3852
     *
3853
     * @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...
3854
     *                    <p>An array with two elements. The first element contains the array
3855
     *                    of elements where the predicate returned TRUE, the second element
3856
     *                    contains the array of elements where the predicate returned FALSE.</p>
3857
     *
3858
     * @psalm-return array<int, static<TKey,T>>
3859
     */
3860 1
    public function partition(\Closure $closure): array
3861
    {
3862
        // init
3863 1
        $matches = [];
3864 1
        $noMatches = [];
3865
3866 1
        foreach ($this->array as $key => $value) {
3867 1
            if ($closure($value, $key)) {
3868 1
                $matches[$key] = $value;
3869
            } else {
3870 1
                $noMatches[$key] = $value;
3871
            }
3872
        }
3873
3874 1
        return [self::create($matches), self::create($noMatches)];
3875
    }
3876
3877
    /**
3878
     * Pop a specified value off the end of the current array.
3879
     *
3880
     * @return mixed|null
3881
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
3882
     */
3883 5
    public function pop()
3884
    {
3885 5
        $this->generatorToArray();
3886
3887 5
        return \array_pop($this->array);
3888
    }
3889
3890
    /**
3891
     * Prepend a (key) + value to the current array.
3892
     *
3893
     * @param mixed $value
3894
     * @param mixed $key
3895
     *
3896
     * @return $this
3897
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3898
     *
3899
     * @psalm-return static<TKey,T>
3900
     */
3901 11
    public function prepend($value, $key = null)
3902
    {
3903 11
        $this->generatorToArray();
3904
3905 11
        if ($this->properties !== []) {
3906 3
            $this->checkType($key, $value);
3907
        }
3908
3909 9
        if ($key === null) {
3910 8
            \array_unshift($this->array, $value);
3911
        } else {
3912 2
            $this->array = [$key => $value] + $this->array;
3913
        }
3914
3915 9
        return $this;
3916
    }
3917
3918
    /**
3919
     * Add a suffix to each key.
3920
     *
3921
     * @param mixed $suffix
3922
     *
3923
     * @return static
3924
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3925
     *
3926
     * @psalm-return static<TKey,T>
3927
     * @psalm-mutation-free
3928
     */
3929 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...
3930
    {
3931
        // init
3932 10
        $result = [];
3933
3934 10
        foreach ($this->getGenerator() as $key => $item) {
3935 9
            if ($item instanceof self) {
3936
                $result[$key] = $item->prependToEachKey($suffix);
3937 9
            } elseif (\is_array($item) === true) {
3938
                $result[$key] = self::create(
3939
                    $item,
3940
                    $this->iteratorClass,
3941
                    false
3942
                )->prependToEachKey($suffix)
3943
                    ->toArray();
3944
            } else {
3945 9
                $result[$key . $suffix] = $item;
3946
            }
3947
        }
3948
3949 10
        return self::create(
3950 10
            $result,
3951 10
            $this->iteratorClass,
3952 10
            false
3953
        );
3954
    }
3955
3956
    /**
3957
     * Add a suffix to each value.
3958
     *
3959
     * @param mixed $suffix
3960
     *
3961
     * @return static
3962
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3963
     *
3964
     * @psalm-return static<TKey,T>
3965
     * @psalm-mutation-free
3966
     */
3967 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...
3968
    {
3969
        // init
3970 10
        $result = [];
3971
3972 10
        foreach ($this->getGenerator() as $key => $item) {
3973 9
            if ($item instanceof self) {
3974
                $result[$key] = $item->prependToEachValue($suffix);
3975 9
            } elseif (\is_array($item) === true) {
3976
                $result[$key] = self::create(
3977
                    $item,
3978
                    $this->iteratorClass,
3979
                    false
3980
                )->prependToEachValue($suffix)
3981
                    ->toArray();
3982 9
            } elseif (\is_object($item) === true) {
3983 1
                $result[$key] = $item;
3984
            } else {
3985 9
                $result[$key] = $item . $suffix;
3986
            }
3987
        }
3988
3989 10
        return self::create(
3990 10
            $result,
3991 10
            $this->iteratorClass,
3992 10
            false
3993
        );
3994
    }
3995
3996
    /**
3997
     * Return the value of a given key and
3998
     * delete the key.
3999
     *
4000
     * @param int|int[]|string|string[]|null $keyOrKeys
4001
     * @param mixed                          $fallback
4002
     *
4003
     * @return mixed
4004
     */
4005 1
    public function pull($keyOrKeys = null, $fallback = null)
4006
    {
4007 1
        if ($keyOrKeys === null) {
4008
            $array = $this->toArray();
4009
            $this->clear();
4010
4011
            return $array;
4012
        }
4013
4014 1
        if (\is_array($keyOrKeys) === true) {
4015 1
            $valueOrValues = [];
4016 1
            foreach ($keyOrKeys as $key) {
4017 1
                $valueOrValues[] = $this->get($key, $fallback);
4018 1
                $this->offsetUnset($key);
4019
            }
4020
        } else {
4021 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4022 1
            $this->offsetUnset($keyOrKeys);
4023
        }
4024
4025 1
        return $valueOrValues;
4026
    }
4027
4028
    /**
4029
     * Push one or more values onto the end of array at once.
4030
     *
4031
     * @param array ...$args
4032
     *
4033
     * @return $this
4034
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4035
     *
4036
     * @noinspection ReturnTypeCanBeDeclaredInspection
4037
     *
4038
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4039
     * @psalm-return static<TKey,T>
4040
     */
4041 5
    public function push(...$args)
4042
    {
4043 5
        $this->generatorToArray();
4044
4045
        if (
4046 5
            $this->checkPropertyTypes
4047
            &&
4048 5
            $this->properties !== []
4049
        ) {
4050 1
            foreach ($args as $key => $value) {
4051 1
                $this->checkType($key, $value);
4052
            }
4053
        }
4054
4055 5
        \array_push($this->array, ...$args);
4056
4057 5
        return $this;
4058
    }
4059
4060
    /**
4061
     * Get a random value from the current array.
4062
     *
4063
     * @param int|null $number <p>How many values you will take?</p>
4064
     *
4065
     * @return static
4066
     *                <p>(Immutable)</p>
4067
     *
4068
     * @psalm-return static<int|array-key,T>
4069
     */
4070 19
    public function randomImmutable(int $number = null): self
4071
    {
4072 19
        $this->generatorToArray();
4073
4074 19
        if ($this->count() === 0) {
4075 1
            return static::create(
4076 1
                [],
4077 1
                $this->iteratorClass,
4078 1
                false
4079
            );
4080
        }
4081
4082 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...
4083
            /** @noinspection NonSecureArrayRandUsageInspection */
4084 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4085
4086 13
            return static::create(
4087 13
                $arrayRandValue,
4088 13
                $this->iteratorClass,
4089 13
                false
4090
            );
4091
        }
4092
4093 6
        $arrayTmp = $this->array;
4094
        /** @noinspection NonSecureShuffleUsageInspection */
4095 6
        \shuffle($arrayTmp);
4096
4097 6
        return static::create(
4098 6
            $arrayTmp,
4099 6
            $this->iteratorClass,
4100 6
            false
4101 6
        )->firstsImmutable($number);
4102
    }
4103
4104
    /**
4105
     * Pick a random key/index from the keys of this array.
4106
     *
4107
     * @throws \RangeException If array is empty
4108
     *
4109
     * @return mixed
4110
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4111
     */
4112 4
    public function randomKey()
4113
    {
4114 4
        $result = $this->randomKeys(1);
4115
4116 4
        if (!isset($result[0])) {
4117
            $result[0] = null;
4118
        }
4119
4120 4
        return $result[0];
4121
    }
4122
4123
    /**
4124
     * Pick a given number of random keys/indexes out of this array.
4125
     *
4126
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4127
     *
4128
     * @throws \RangeException If array is empty
4129
     *
4130
     * @return static
4131
     *                <p>(Immutable)</p>
4132
     *
4133
     * @psalm-return static<TKey,T>
4134
     */
4135 13
    public function randomKeys(int $number): self
4136
    {
4137 13
        $this->generatorToArray();
4138
4139 13
        $count = $this->count();
4140
4141
        if (
4142 13
            $number === 0
4143
            ||
4144 13
            $number > $count
4145
        ) {
4146 2
            throw new \RangeException(
4147 2
                \sprintf(
4148 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4149 2
                    $number,
4150 2
                    $count
4151
                )
4152
            );
4153
        }
4154
4155 11
        $result = (array) \array_rand($this->array, $number);
4156
4157 11
        return static::create(
4158 11
            $result,
4159 11
            $this->iteratorClass,
4160 11
            false
4161
        );
4162
    }
4163
4164
    /**
4165
     * Get a random value from the current array.
4166
     *
4167
     * @param int|null $number <p>How many values you will take?</p>
4168
     *
4169
     * @return $this
4170
     *               <p>(Mutable) Return this Arrayy object.</p>
4171
     *
4172
     * @psalm-return static<TKey,T>
4173
     */
4174 17
    public function randomMutable(int $number = null): self
4175
    {
4176 17
        $this->generatorToArray();
4177
4178 17
        if ($this->count() === 0) {
4179
            return static::create(
4180
                [],
4181
                $this->iteratorClass,
4182
                false
4183
            );
4184
        }
4185
4186 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...
4187
            /** @noinspection NonSecureArrayRandUsageInspection */
4188 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4189 7
            $this->array = $arrayRandValue;
4190
4191 7
            return $this;
4192
        }
4193
4194
        /** @noinspection NonSecureShuffleUsageInspection */
4195 11
        \shuffle($this->array);
4196
4197 11
        return $this->firstsMutable($number);
4198
    }
4199
4200
    /**
4201
     * Pick a random value from the values of this array.
4202
     *
4203
     * @return mixed
4204
     *               <p>Get a random value or null if there wasn't a value.</p>
4205
     */
4206 4
    public function randomValue()
4207
    {
4208 4
        $result = $this->randomImmutable();
4209
4210 4
        if (!isset($result[0])) {
4211
            $result[0] = null;
4212
        }
4213
4214 4
        return $result[0];
4215
    }
4216
4217
    /**
4218
     * Pick a given number of random values out of this array.
4219
     *
4220
     * @param int $number
4221
     *
4222
     * @return static
4223
     *                <p>(Mutable)</p>
4224
     *
4225
     * @psalm-return static<TKey,T>
4226
     */
4227 7
    public function randomValues(int $number): self
4228
    {
4229 7
        return $this->randomMutable($number);
4230
    }
4231
4232
    /**
4233
     * Get a random value from an array, with the ability to skew the results.
4234
     *
4235
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4236
     *
4237
     * @param array    $array
4238
     * @param int|null $number <p>How many values you will take?</p>
4239
     *
4240
     * @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...
4241
     *                           <p>(Immutable)</p>
4242
     *
4243
     * @psalm-param  array<mixed,mixed> $array
4244
     * @psalm-return static<int|array-key,T>
4245
     */
4246 9
    public function randomWeighted(array $array, int $number = null): self
4247
    {
4248
        // init
4249 9
        $options = [];
4250
4251 9
        foreach ($array as $option => $weight) {
4252 9
            if ($this->searchIndex($option) !== false) {
4253 9
                for ($i = 0; $i < $weight; ++$i) {
4254 1
                    $options[] = $option;
4255
                }
4256
            }
4257
        }
4258
4259 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4260
    }
4261
4262
    /**
4263
     * Reduce the current array via callable e.g. anonymous-function.
4264
     *
4265
     * @param callable $callable
4266
     * @param mixed    $init
4267
     *
4268
     * @return static
4269
     *                <p>(Immutable)</p>
4270
     *
4271
     * @psalm-return static<TKey,T>
4272
     * @psalm-mutation-free
4273
     */
4274 18
    public function reduce($callable, $init = []): self
4275
    {
4276 18
        if ($this->generator) {
4277 1
            $result = $init;
4278
4279 1
            foreach ($this->getGenerator() as $value) {
4280 1
                $result = $callable($result, $value);
4281
            }
4282
4283 1
            return static::create(
4284 1
                $result,
4285 1
                $this->iteratorClass,
4286 1
                false
4287
            );
4288
        }
4289
4290 18
        $result = \array_reduce($this->array, $callable, $init);
4291
4292 18
        if ($result === null) {
4293
            $this->array = [];
4294
        } else {
4295 18
            $this->array = (array) $result;
4296
        }
4297
4298 18
        return static::create(
4299 18
            $this->array,
4300 18
            $this->iteratorClass,
4301 18
            false
4302
        );
4303
    }
4304
4305
    /**
4306
     * @param bool $unique
4307
     *
4308
     * @return static
4309
     *                <p>(Immutable)</p>
4310
     *
4311
     * @psalm-return static<TKey,T>
4312
     * @psalm-mutation-free
4313
     */
4314 14
    public function reduce_dimension(bool $unique = true): self
4315
    {
4316
        // init
4317 14
        $result = [];
4318
4319 14
        foreach ($this->getGenerator() as $val) {
4320 12
            if (\is_array($val) === true) {
4321 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4322
            } else {
4323 12
                $result[] = [$val];
4324
            }
4325
        }
4326
4327 14
        $result = $result === [] ? [] : \array_merge(...$result);
4328
4329 14
        $resultArrayy = new static($result);
4330
4331
        /**
4332
         * @psalm-suppress ImpureMethodCall - object is already re-created
4333
         * @psalm-suppress InvalidReturnStatement - why?
4334
         */
4335 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4336
    }
4337
4338
    /**
4339
     * Create a numerically re-indexed Arrayy object.
4340
     *
4341
     * @return $this
4342
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4343
     *
4344
     * @psalm-return static<TKey,T>
4345
     */
4346 9
    public function reindex(): self
4347
    {
4348 9
        $this->generatorToArray(false);
4349
4350 9
        $this->array = \array_values($this->array);
4351
4352 9
        return $this;
4353
    }
4354
4355
    /**
4356
     * Return all items that fail the truth test.
4357
     *
4358
     * @param \Closure $closure
4359
     *
4360
     * @return static
4361
     *                <p>(Immutable)</p>
4362
     *
4363
     * @psalm-return static<TKey,T>
4364
     * @psalm-mutation-free
4365
     */
4366 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...
4367
    {
4368
        // init
4369 1
        $filtered = [];
4370
4371 1
        foreach ($this->getGenerator() as $key => $value) {
4372 1
            if (!$closure($value, $key)) {
4373 1
                $filtered[$key] = $value;
4374
            }
4375
        }
4376
4377 1
        return static::create(
4378 1
            $filtered,
4379 1
            $this->iteratorClass,
4380 1
            false
4381
        );
4382
    }
4383
4384
    /**
4385
     * Remove a value from the current array (optional using dot-notation).
4386
     *
4387
     * @param mixed $key
4388
     *
4389
     * @return static
4390
     *                <p>(Mutable)</p>
4391
     *
4392
     * @psalm-param  TKey $key
4393
     * @psalm-return static<TKey,T>
4394
     */
4395 18
    public function remove($key)
4396
    {
4397
        // recursive call
4398 18
        if (\is_array($key) === true) {
4399
            foreach ($key as $k) {
4400
                $this->internalRemove($k);
4401
            }
4402
4403
            return static::create(
4404
                $this->toArray(),
4405
                $this->iteratorClass,
4406
                false
4407
            );
4408
        }
4409
4410 18
        $this->internalRemove($key);
4411
4412 18
        return static::create(
4413 18
            $this->toArray(),
4414 18
            $this->iteratorClass,
4415 18
            false
4416
        );
4417
    }
4418
4419
    /**
4420
     * alias: for "Arrayy->removeValue()"
4421
     *
4422
     * @param mixed $element
4423
     *
4424
     * @return static
4425
     *                <p>(Immutable)</p>
4426
     *
4427
     * @psalm-param  T $element
4428
     * @psalm-return static<TKey,T>
4429
     * @psalm-mutation-free
4430
     */
4431 8
    public function removeElement($element)
4432
    {
4433 8
        return $this->removeValue($element);
4434
    }
4435
4436
    /**
4437
     * Remove the first value from the current array.
4438
     *
4439
     * @return static
4440
     *                <p>(Immutable)</p>
4441
     *
4442
     * @psalm-return static<TKey,T>
4443
     * @psalm-mutation-free
4444
     */
4445 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...
4446
    {
4447 7
        $tmpArray = $this->toArray();
4448
4449 7
        \array_shift($tmpArray);
4450
4451 7
        return static::create(
4452 7
            $tmpArray,
4453 7
            $this->iteratorClass,
4454 7
            false
4455
        );
4456
    }
4457
4458
    /**
4459
     * Remove the last value from the current array.
4460
     *
4461
     * @return static
4462
     *                <p>(Immutable)</p>
4463
     *
4464
     * @psalm-return static<TKey,T>
4465
     * @psalm-mutation-free
4466
     */
4467 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...
4468
    {
4469 7
        $tmpArray = $this->toArray();
4470
4471 7
        \array_pop($tmpArray);
4472
4473 7
        return static::create(
4474 7
            $tmpArray,
4475 7
            $this->iteratorClass,
4476 7
            false
4477
        );
4478
    }
4479
4480
    /**
4481
     * Removes a particular value from an array (numeric or associative).
4482
     *
4483
     * @param mixed $value
4484
     *
4485
     * @return static
4486
     *                <p>(Immutable)</p>
4487
     *
4488
     * @psalm-param  T $value
4489
     * @psalm-return static<TKey,T>
4490
     * @psalm-mutation-free
4491
     */
4492 8
    public function removeValue($value): self
4493
    {
4494 8
        $this->generatorToArray();
4495
4496
        // init
4497 8
        $isSequentialArray = $this->isSequential();
4498
4499 8
        foreach ($this->array as $key => $item) {
4500 7
            if ($item === $value) {
4501 7
                unset($this->array[$key]);
4502
            }
4503
        }
4504
4505 8
        if ($isSequentialArray) {
4506 6
            $this->array = \array_values($this->array);
4507
        }
4508
4509 8
        return static::create(
4510 8
            $this->array,
4511 8
            $this->iteratorClass,
4512 8
            false
4513
        );
4514
    }
4515
4516
    /**
4517
     * Generate array of repeated arrays.
4518
     *
4519
     * @param int $times <p>How many times has to be repeated.</p>
4520
     *
4521
     * @return static
4522
     *                <p>(Immutable)</p>
4523
     *
4524
     * @psalm-return static<TKey,T>
4525
     * @psalm-mutation-free
4526
     */
4527 1
    public function repeat($times): self
4528
    {
4529 1
        if ($times === 0) {
4530 1
            return static::create([], $this->iteratorClass);
4531
        }
4532
4533 1
        return static::create(
4534 1
            \array_fill(0, (int) $times, $this->toArray()),
4535 1
            $this->iteratorClass,
4536 1
            false
4537
        );
4538
    }
4539
4540
    /**
4541
     * Replace a key with a new key/value pair.
4542
     *
4543
     * @param mixed $replace
4544
     * @param mixed $key
4545
     * @param mixed $value
4546
     *
4547
     * @return static
4548
     *                <p>(Immutable)</p>
4549
     *
4550
     * @psalm-return static<TKey,T>
4551
     * @psalm-mutation-free
4552
     */
4553 2
    public function replace($replace, $key, $value): self
4554
    {
4555 2
        $that = clone $this;
4556
4557
        /**
4558
         * @psalm-suppress ImpureMethodCall - object is already cloned
4559
         */
4560 2
        return $that->remove($replace)
4561 2
            ->set($key, $value);
4562
    }
4563
4564
    /**
4565
     * Create an array using the current array as values and the other array as keys.
4566
     *
4567
     * @param array $keys <p>An array of keys.</p>
4568
     *
4569
     * @return static
4570
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4571
     *
4572
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4573
     * @psalm-return static<TKey,T>
4574
     * @psalm-mutation-free
4575
     */
4576 2
    public function replaceAllKeys(array $keys): self
4577
    {
4578 2
        return static::create(
4579 2
            \array_combine($keys, $this->toArray()),
4580 2
            $this->iteratorClass,
4581 2
            false
4582
        );
4583
    }
4584
4585
    /**
4586
     * Create an array using the current array as keys and the other array as values.
4587
     *
4588
     * @param array $array <p>An array o values.</p>
4589
     *
4590
     * @return static
4591
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4592
     *
4593
     * @psalm-param  array<mixed,T> $array
4594
     * @psalm-return static<TKey,T>
4595
     * @psalm-mutation-free
4596
     */
4597 2
    public function replaceAllValues(array $array): self
4598
    {
4599 2
        return static::create(
4600 2
            \array_combine($this->array, $array),
4601 2
            $this->iteratorClass,
4602 2
            false
4603
        );
4604
    }
4605
4606
    /**
4607
     * Replace the keys in an array with another set.
4608
     *
4609
     * @param array $keys <p>An array of keys matching the array's size</p>
4610
     *
4611
     * @return static
4612
     *                <p>(Immutable)</p>
4613
     *
4614
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4615
     * @psalm-return static<TKey,T>
4616
     * @psalm-mutation-free
4617
     */
4618 1
    public function replaceKeys(array $keys): self
4619
    {
4620 1
        $values = \array_values($this->toArray());
4621 1
        $result = \array_combine($keys, $values);
4622
4623 1
        return static::create(
4624 1
            $result,
4625 1
            $this->iteratorClass,
4626 1
            false
4627
        );
4628
    }
4629
4630
    /**
4631
     * Replace the first matched value in an array.
4632
     *
4633
     * @param mixed $search      <p>The value to replace.</p>
4634
     * @param mixed $replacement <p>The value to replace.</p>
4635
     *
4636
     * @return static
4637
     *                <p>(Immutable)</p>
4638
     *
4639
     * @psalm-return static<TKey,T>
4640
     * @psalm-mutation-free
4641
     */
4642 3
    public function replaceOneValue($search, $replacement = ''): self
4643
    {
4644 3
        $array = $this->toArray();
4645 3
        $key = \array_search($search, $array, true);
4646
4647 3
        if ($key !== false) {
4648 3
            $array[$key] = $replacement;
4649
        }
4650
4651 3
        return static::create(
4652 3
            $array,
4653 3
            $this->iteratorClass,
4654 3
            false
4655
        );
4656
    }
4657
4658
    /**
4659
     * Replace values in the current array.
4660
     *
4661
     * @param mixed $search      <p>The value to replace.</p>
4662
     * @param mixed $replacement <p>What to replace it with.</p>
4663
     *
4664
     * @return static
4665
     *                <p>(Immutable)</p>
4666
     *
4667
     * @psalm-return static<TKey,T>
4668
     * @psalm-mutation-free
4669
     */
4670 1
    public function replaceValues($search, $replacement = ''): self
4671
    {
4672
        /**
4673
         * @psalm-suppress MissingClosureReturnType
4674
         * @psalm-suppress MissingClosureParamType
4675
         */
4676 1
        return $this->each(
4677
            static function ($value) use ($search, $replacement) {
4678 1
                return \str_replace($search, $replacement, $value);
4679 1
            }
4680
        );
4681
    }
4682
4683
    /**
4684
     * Get the last elements from index $from until the end of this array.
4685
     *
4686
     * @param int $from
4687
     *
4688
     * @return static
4689
     *                <p>(Immutable)</p>
4690
     *
4691
     * @psalm-return static<TKey,T>
4692
     * @psalm-mutation-free
4693
     */
4694 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...
4695
    {
4696 15
        $tmpArray = $this->toArray();
4697
4698 15
        return static::create(
4699 15
            \array_splice($tmpArray, $from),
4700 15
            $this->iteratorClass,
4701 15
            false
4702
        );
4703
    }
4704
4705
    /**
4706
     * Return the array in the reverse order.
4707
     *
4708
     * @return $this
4709
     *               <p>(Mutable) Return this Arrayy object.</p>
4710
     *
4711
     * @psalm-return static<TKey,T>
4712
     */
4713 9
    public function reverse(): self
4714
    {
4715 9
        $this->generatorToArray();
4716
4717 9
        $this->array = \array_reverse($this->array);
4718
4719 9
        return $this;
4720
    }
4721
4722
    /**
4723
     * Sort an array in reverse order.
4724
     *
4725
     * @param int $sort_flags [optional] <p>
4726
     *                        You may modify the behavior of the sort using the optional
4727
     *                        parameter sort_flags, for details
4728
     *                        see sort.
4729
     *                        </p>
4730
     *
4731
     * @return $this
4732
     *               <p>(Mutable) Return this Arrayy object.</p>
4733
     *
4734
     * @psalm-return static<TKey,T>
4735
     */
4736 4
    public function rsort(int $sort_flags = 0): self
4737
    {
4738 4
        $this->generatorToArray();
4739
4740 4
        \rsort($this->array, $sort_flags);
4741
4742 4
        return $this;
4743
    }
4744
4745
    /**
4746
     * Sort an array in reverse order.
4747
     *
4748
     * @param int $sort_flags [optional] <p>
4749
     *                        You may modify the behavior of the sort using the optional
4750
     *                        parameter sort_flags, for details
4751
     *                        see sort.
4752
     *                        </p>
4753
     *
4754
     * @return $this
4755
     *               <p>(Immutable) Return this Arrayy object.</p>
4756
     *
4757
     * @psalm-return static<TKey,T>
4758
     * @psalm-mutation-free
4759
     */
4760 4
    public function rsortImmutable(int $sort_flags = 0): self
4761
    {
4762 4
        $that = clone $this;
4763
4764
        /**
4765
         * @psalm-suppress ImpureMethodCall - object is already cloned
4766
         */
4767 4
        $that->rsort($sort_flags);
4768
4769 4
        return $that;
4770
    }
4771
4772
    /**
4773
     * Search for the first index of the current array via $value.
4774
     *
4775
     * @param mixed $value
4776
     *
4777
     * @return false|float|int|string
4778
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4779
     * @psalm-mutation-free
4780
     */
4781 21
    public function searchIndex($value)
4782
    {
4783 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
4784 20
            if ($value === $valueFromArray) {
4785 20
                return $keyFromArray;
4786
            }
4787
        }
4788
4789 11
        return false;
4790
    }
4791
4792
    /**
4793
     * Search for the value of the current array via $index.
4794
     *
4795
     * @param mixed $index
4796
     *
4797
     * @return static
4798
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
4799
     *
4800
     * @psalm-return static<TKey,T>
4801
     * @psalm-mutation-free
4802
     */
4803 9
    public function searchValue($index): self
4804
    {
4805 9
        $this->generatorToArray();
4806
4807
        // init
4808 9
        $return = [];
4809
4810 9
        if ($this->array === []) {
4811
            return static::create(
4812
                [],
4813
                $this->iteratorClass,
4814
                false
4815
            );
4816
        }
4817
4818
        // php cast "bool"-index into "int"-index
4819 9
        if ((bool) $index === $index) {
4820 1
            $index = (int) $index;
4821
        }
4822
4823 9
        if ($this->offsetExists($index)) {
4824 7
            $return = [$this->array[$index]];
4825
        }
4826
4827 9
        return static::create(
4828 9
            $return,
4829 9
            $this->iteratorClass,
4830 9
            false
4831
        );
4832
    }
4833
4834
    /**
4835
     * Set a value for the current array (optional using dot-notation).
4836
     *
4837
     * @param string $key   <p>The key to set.</p>
4838
     * @param mixed  $value <p>Its value.</p>
4839
     *
4840
     * @return $this
4841
     *               <p>(Mutable) Return this Arrayy object.</p>
4842
     *
4843
     * @psalm-param  TKey $key
4844
     * @psalm-param  T $value
4845
     * @psalm-return static<TKey,T>
4846
     */
4847 18
    public function set($key, $value): self
4848
    {
4849 18
        $this->internalSet($key, $value);
4850
4851 18
        return $this;
4852
    }
4853
4854
    /**
4855
     * Get a value from a array and set it if it was not.
4856
     *
4857
     * WARNING: this method only set the value, if the $key is not already set
4858
     *
4859
     * @param mixed $key      <p>The key</p>
4860
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
4861
     *
4862
     * @return mixed
4863
     *               <p>(Mutable)</p>
4864
     */
4865 11
    public function setAndGet($key, $fallback = null)
4866
    {
4867 11
        $this->generatorToArray();
4868
4869
        // If the key doesn't exist, set it.
4870 11
        if (!$this->has($key)) {
4871 4
            $this->array = $this->set($key, $fallback)->toArray();
4872
        }
4873
4874 11
        return $this->get($key);
4875
    }
4876
4877
    /**
4878
     * Shifts a specified value off the beginning of array.
4879
     *
4880
     * @return mixed
4881
     *               <p>(Mutable) A shifted element from the current array.</p>
4882
     */
4883 5
    public function shift()
4884
    {
4885 5
        $this->generatorToArray();
4886
4887 5
        return \array_shift($this->array);
4888
    }
4889
4890
    /**
4891
     * Shuffle the current array.
4892
     *
4893
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
4894
     * @param array $array  [optional]
4895
     *
4896
     * @return static
4897
     *                <p>(Immutable)</p>
4898
     *
4899
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4900
     * @psalm-return static<TKey,T>
4901
     *
4902
     * @noinspection BadExceptionsProcessingInspection
4903
     * @noinspection RandomApiMigrationInspection
4904
     * @noinspection NonSecureShuffleUsageInspection
4905
     */
4906 2
    public function shuffle(bool $secure = false, array $array = null): self
4907
    {
4908 2
        if ($array === null) {
4909 2
            $array = $this->toArray(false);
4910
        }
4911
4912 2
        if ($secure !== true) {
4913 2
            \shuffle($array);
4914
        } else {
4915 1
            $size = \count($array, \COUNT_NORMAL);
4916 1
            $keys = \array_keys($array);
4917 1
            for ($i = $size - 1; $i > 0; --$i) {
4918
                try {
4919 1
                    $r = \random_int(0, $i);
4920
                } catch (\Exception $e) {
4921
                    $r = \mt_rand(0, $i);
4922
                }
4923 1
                if ($r !== $i) {
4924
                    $temp = $array[$keys[$r]];
4925
                    $array[$keys[$r]] = $array[$keys[$i]];
4926
                    $array[$keys[$i]] = $temp;
4927
                }
4928
            }
4929
        }
4930
4931 2
        foreach ($array as $key => $value) {
4932
            // check if recursive is needed
4933 2
            if (\is_array($value) === true) {
4934 2
                $array[$key] = $this->shuffle($secure, $value);
4935
            }
4936
        }
4937
4938 2
        return static::create(
4939 2
            $array,
4940 2
            $this->iteratorClass,
4941 2
            false
4942
        );
4943
    }
4944
4945
    /**
4946
     * Count the values from the current array.
4947
     *
4948
     * alias: for "Arrayy->count()"
4949
     *
4950
     * @param int $mode
4951
     *
4952
     * @return int
4953
     */
4954 20
    public function size(int $mode = \COUNT_NORMAL): int
4955
    {
4956 20
        return $this->count($mode);
4957
    }
4958
4959
    /**
4960
     * Checks whether array has exactly $size items.
4961
     *
4962
     * @param int $size
4963
     *
4964
     * @return bool
4965
     */
4966 1 View Code Duplication
    public function sizeIs(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...
4967
    {
4968
        // init
4969 1
        $itemsTempCount = 0;
4970
4971 1
        foreach ($this->getGenerator() as $key => $value) {
4972 1
            ++$itemsTempCount;
4973 1
            if ($itemsTempCount > $size) {
4974 1
                return false;
4975
            }
4976
        }
4977
4978 1
        return $itemsTempCount === $size;
4979
    }
4980
4981
    /**
4982
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4983
     * smaller than $fromSize.
4984
     *
4985
     * @param int $fromSize
4986
     * @param int $toSize
4987
     *
4988
     * @return bool
4989
     */
4990 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4991
    {
4992 1
        if ($fromSize > $toSize) {
4993 1
            $tmp = $toSize;
4994 1
            $toSize = $fromSize;
4995 1
            $fromSize = $tmp;
4996
        }
4997
4998
        // init
4999 1
        $itemsTempCount = 0;
5000
5001 1
        foreach ($this->getGenerator() as $key => $value) {
5002 1
            ++$itemsTempCount;
5003 1
            if ($itemsTempCount > $toSize) {
5004 1
                return false;
5005
            }
5006
        }
5007
5008 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5009
    }
5010
5011
    /**
5012
     * Checks whether array has more than $size items.
5013
     *
5014
     * @param int $size
5015
     *
5016
     * @return bool
5017
     */
5018 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...
5019
    {
5020
        // init
5021 1
        $itemsTempCount = 0;
5022
5023 1
        foreach ($this->getGenerator() as $key => $value) {
5024 1
            ++$itemsTempCount;
5025 1
            if ($itemsTempCount > $size) {
5026 1
                return true;
5027
            }
5028
        }
5029
5030 1
        return $itemsTempCount > $size;
5031
    }
5032
5033
    /**
5034
     * Checks whether array has less than $size items.
5035
     *
5036
     * @param int $size
5037
     *
5038
     * @return bool
5039
     */
5040 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...
5041
    {
5042
        // init
5043 1
        $itemsTempCount = 0;
5044
5045 1
        foreach ($this->getGenerator() as $key => $value) {
5046 1
            ++$itemsTempCount;
5047 1
            if ($itemsTempCount > $size) {
5048 1
                return false;
5049
            }
5050
        }
5051
5052 1
        return $itemsTempCount < $size;
5053
    }
5054
5055
    /**
5056
     * Counts all elements in an array, or something in an object.
5057
     *
5058
     * <p>
5059
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5060
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5061
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5062
     * implemented and used in PHP.
5063
     * </p>
5064
     *
5065
     * @return int
5066
     *             <p>
5067
     *             The number of elements in var, which is
5068
     *             typically an array, since anything else will have one
5069
     *             element.
5070
     *             </p>
5071
     *             <p>
5072
     *             If var is not an array or an object with
5073
     *             implemented Countable interface,
5074
     *             1 will be returned.
5075
     *             There is one exception, if var is &null;,
5076
     *             0 will be returned.
5077
     *             </p>
5078
     *             <p>
5079
     *             Caution: count may return 0 for a variable that isn't set,
5080
     *             but it may also return 0 for a variable that has been initialized with an
5081
     *             empty array. Use isset to test if a variable is set.
5082
     *             </p>
5083
     */
5084 10
    public function sizeRecursive(): int
5085
    {
5086 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5087
    }
5088
5089
    /**
5090
     * Extract a slice of the array.
5091
     *
5092
     * @param int      $offset       <p>Slice begin index.</p>
5093
     * @param int|null $length       <p>Length of the slice.</p>
5094
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5095
     *
5096
     * @return static
5097
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5098
     *
5099
     * @psalm-return static<TKey,T>
5100
     * @psalm-mutation-free
5101
     */
5102 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5103
    {
5104 5
        return static::create(
5105 5
            \array_slice(
5106 5
                $this->toArray(),
5107 5
                $offset,
5108 5
                $length,
5109 5
                $preserveKeys
5110
            ),
5111 5
            $this->iteratorClass,
5112 5
            false
5113
        );
5114
    }
5115
5116
    /**
5117
     * Sort the current array and optional you can keep the keys.
5118
     *
5119
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5120
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5121
     *                              <strong>SORT_NATURAL</strong></p>
5122
     * @param bool       $keepKeys
5123
     *
5124
     * @return static
5125
     *                <p>(Mutable) Return this Arrayy object.</p>
5126
     *
5127
     * @psalm-return static<TKey,T>
5128
     */
5129 20
    public function sort(
5130
        $direction = \SORT_ASC,
5131
        int $strategy = \SORT_REGULAR,
5132
        bool $keepKeys = false
5133
    ): self {
5134 20
        $this->generatorToArray();
5135
5136 20
        return $this->sorting(
5137 20
            $this->array,
5138 20
            $direction,
5139 20
            $strategy,
5140 20
            $keepKeys
5141
        );
5142
    }
5143
5144
    /**
5145
     * Sort the current array and optional you can keep the keys.
5146
     *
5147
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5148
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5149
     *                              <strong>SORT_NATURAL</strong></p>
5150
     * @param bool       $keepKeys
5151
     *
5152
     * @return static
5153
     *                <p>(Immutable) Return this Arrayy object.</p>
5154
     *
5155
     * @psalm-return static<TKey,T>
5156
     */
5157 12
    public function sortImmutable(
5158
        $direction = \SORT_ASC,
5159
        int $strategy = \SORT_REGULAR,
5160
        bool $keepKeys = false
5161
    ): self {
5162 12
        $that = clone $this;
5163
5164 12
        $that->generatorToArray();
5165
5166 12
        return $that->sorting(
5167 12
            $that->array,
5168 12
            $direction,
5169 12
            $strategy,
5170 12
            $keepKeys
5171
        );
5172
    }
5173
5174
    /**
5175
     * Sort the current array by key.
5176
     *
5177
     * @see          http://php.net/manual/en/function.ksort.php
5178
     * @see          http://php.net/manual/en/function.krsort.php
5179
     *
5180
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5181
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5182
     *                              <strong>SORT_NATURAL</strong></p>
5183
     *
5184
     * @return $this
5185
     *               <p>(Mutable) Return this Arrayy object.</p>
5186
     *
5187
     * @psalm-return static<TKey,T>
5188
     */
5189 18
    public function sortKeys(
5190
        $direction = \SORT_ASC,
5191
        int $strategy = \SORT_REGULAR
5192
    ): self {
5193 18
        $this->generatorToArray();
5194
5195 18
        $this->sorterKeys($this->array, $direction, $strategy);
5196
5197 18
        return $this;
5198
    }
5199
5200
    /**
5201
     * Sort the current array by key.
5202
     *
5203
     * @see          http://php.net/manual/en/function.ksort.php
5204
     * @see          http://php.net/manual/en/function.krsort.php
5205
     *
5206
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5207
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5208
     *                              <strong>SORT_NATURAL</strong></p>
5209
     *
5210
     * @return $this
5211
     *               <p>(Immutable) Return this Arrayy object.</p>
5212
     *
5213
     * @psalm-return static<TKey,T>
5214
     * @psalm-mutation-free
5215
     */
5216 8
    public function sortKeysImmutable(
5217
        $direction = \SORT_ASC,
5218
        int $strategy = \SORT_REGULAR
5219
    ): self {
5220 8
        $that = clone $this;
5221
5222
        /**
5223
         * @psalm-suppress ImpureMethodCall - object is already cloned
5224
         */
5225 8
        $that->sortKeys($direction, $strategy);
5226
5227 8
        return $that;
5228
    }
5229
5230
    /**
5231
     * Sort the current array by value.
5232
     *
5233
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5234
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5235
     *                              <strong>SORT_NATURAL</strong></p>
5236
     *
5237
     * @return static
5238
     *                <p>(Mutable)</p>
5239
     *
5240
     * @psalm-return static<TKey,T>
5241
     */
5242 1
    public function sortValueKeepIndex(
5243
        $direction = \SORT_ASC,
5244
        int $strategy = \SORT_REGULAR
5245
    ): self {
5246 1
        return $this->sort($direction, $strategy, true);
5247
    }
5248
5249
    /**
5250
     * Sort the current array by value.
5251
     *
5252
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5253
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5254
     *                              <strong>SORT_NATURAL</strong></p>
5255
     *
5256
     * @return static
5257
     *                <p>(Mutable)</p>
5258
     *
5259
     * @psalm-return static<TKey,T>
5260
     */
5261 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5262
    {
5263 1
        return $this->sort($direction, $strategy, false);
5264
    }
5265
5266
    /**
5267
     * Sort a array by value, by a closure or by a property.
5268
     *
5269
     * - If the sorter is null, the array is sorted naturally.
5270
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5271
     *
5272
     * @param callable|string|null $sorter
5273
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5274
     *                                        <strong>SORT_DESC</strong></p>
5275
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5276
     *                                        <strong>SORT_NATURAL</strong></p>
5277
     *
5278
     * @return static
5279
     *                <p>(Immutable)</p>
5280
     *
5281
     * @psalm-return static<TKey,T>
5282
     * @psalm-mutation-free
5283
     */
5284 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5285
    {
5286 1
        $array = $this->toArray();
5287 1
        $direction = $this->getDirection($direction);
5288
5289
        // Transform all values into their results.
5290 1
        if ($sorter) {
5291 1
            $arrayy = static::create(
5292 1
                $array,
5293 1
                $this->iteratorClass,
5294 1
                false
5295
            );
5296
5297
            /**
5298
             * @psalm-suppress MissingClosureReturnType
5299
             * @psalm-suppress MissingClosureParamType
5300
             */
5301 1
            $results = $arrayy->each(
5302
                function ($value) use ($sorter) {
5303 1
                    if (\is_callable($sorter) === true) {
5304 1
                        return $sorter($value);
5305
                    }
5306
5307 1
                    return $this->get($sorter);
5308 1
                }
5309
            );
5310
5311 1
            $results = $results->toArray();
5312
        } else {
5313 1
            $results = $array;
5314
        }
5315
5316
        // Sort by the results and replace by original values
5317 1
        \array_multisort($results, $direction, $strategy, $array);
5318
5319 1
        return static::create(
5320 1
            $array,
5321 1
            $this->iteratorClass,
5322 1
            false
5323
        );
5324
    }
5325
5326
    /**
5327
     * @param int      $offset
5328
     * @param int|null $length
5329
     * @param array    $replacement
5330
     *
5331
     * @return static
5332
     *                <p>(Immutable)</p>
5333
     *
5334
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5335
     * @psalm-return static<TKey,T>
5336
     * @psalm-mutation-free
5337
     */
5338 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5339
    {
5340 1
        $tmpArray = $this->toArray();
5341
5342 1
        \array_splice(
5343 1
            $tmpArray,
5344 1
            $offset,
5345 1
            $length ?? $this->count(),
5346 1
            $replacement
5347
        );
5348
5349 1
        return static::create(
5350 1
            $tmpArray,
5351 1
            $this->iteratorClass,
5352 1
            false
5353
        );
5354
    }
5355
5356
    /**
5357
     * Split an array in the given amount of pieces.
5358
     *
5359
     * @param int  $numberOfPieces
5360
     * @param bool $keepKeys
5361
     *
5362
     * @return static
5363
     *                <p>(Immutable)</p>
5364
     *
5365
     * @psalm-return static<TKey,T>
5366
     * @psalm-mutation-free
5367
     */
5368 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5369
    {
5370 1
        $this->generatorToArray();
5371
5372 1
        $count = $this->count();
5373
5374 1
        if ($count === 0) {
5375 1
            $result = [];
5376
        } else {
5377 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5378 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5379
        }
5380
5381 1
        return static::create(
5382 1
            $result,
5383 1
            $this->iteratorClass,
5384 1
            false
5385
        );
5386
    }
5387
5388
    /**
5389
     * Stripe all empty items.
5390
     *
5391
     * @return static
5392
     *                <p>(Immutable)</p>
5393
     *
5394
     * @psalm-return static<TKey,T>
5395
     * @psalm-mutation-free
5396
     */
5397 1
    public function stripEmpty(): self
5398
    {
5399 1
        return $this->filter(
5400
            static function ($item) {
5401 1
                if ($item === null) {
5402 1
                    return false;
5403
                }
5404
5405 1
                return (bool) \trim((string) $item);
5406 1
            }
5407
        );
5408
    }
5409
5410
    /**
5411
     * Swap two values between positions by key.
5412
     *
5413
     * @param int|string $swapA <p>a key in the array</p>
5414
     * @param int|string $swapB <p>a key in the array</p>
5415
     *
5416
     * @return static
5417
     *                <p>(Immutable)</p>
5418
     *
5419
     * @psalm-return static<TKey,T>
5420
     * @psalm-mutation-free
5421
     */
5422 1
    public function swap($swapA, $swapB): self
5423
    {
5424 1
        $array = $this->toArray();
5425
5426 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5427
5428 1
        return static::create(
5429 1
            $array,
5430 1
            $this->iteratorClass,
5431 1
            false
5432
        );
5433
    }
5434
5435
    /**
5436
     * Get the current array from the "Arrayy"-object.
5437
     * alias for "getArray()"
5438
     *
5439
     * @param bool $convertAllArrayyElements <p>
5440
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5441
     *                                       </p>
5442
     * @param bool $preserveKeys             <p>
5443
     *                                       e.g.: A generator maybe return the same key more then once,
5444
     *                                       so maybe you will ignore the keys.
5445
     *                                       </p>
5446
     *
5447
     * @return array
5448
     *
5449
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5450
     * @psalm-mutation-free
5451
     */
5452 893
    public function toArray(
5453
        bool $convertAllArrayyElements = false,
5454
        bool $preserveKeys = true
5455
    ): array {
5456
        // init
5457 893
        $array = [];
5458
5459 893
        if ($convertAllArrayyElements) {
5460 2
            foreach ($this->getGenerator() as $key => $value) {
5461 2
                if ($value instanceof self) {
5462 1
                    $value = $value->toArray(true);
5463
                }
5464
5465 2
                if ($preserveKeys) {
5466 1
                    $array[$key] = $value;
5467
                } else {
5468 2
                    $array[] = $value;
5469
                }
5470
            }
5471
        } else {
5472 893
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5473
        }
5474
5475 893
        return $array;
5476
    }
5477
5478
    /**
5479
     * Get the current array from the "Arrayy"-object as list.
5480
     *
5481
     * @param bool $convertAllArrayyElements <p>
5482
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5483
     *                                       </p>
5484
     *
5485
     * @return array
5486
     *
5487
     * @psalm-return array<int,mixed>|array<int,T>
5488
     * @psalm-mutation-free
5489
     */
5490 1
    public function toList(bool $convertAllArrayyElements = false): array
5491
    {
5492 1
        return $this->toArray(
5493 1
            $convertAllArrayyElements,
5494 1
            false
5495
        );
5496
    }
5497
5498
    /**
5499
     * Convert the current array to JSON.
5500
     *
5501
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
5502
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
5503
     *
5504
     * @return string
5505
     */
5506 7
    public function toJson(int $options = 0, int $depth = 512): string
5507
    {
5508 7
        $return = \json_encode($this->toArray(), $options, $depth);
5509 7
        if ($return === false) {
5510
            return '';
5511
        }
5512
5513 7
        return $return;
5514
    }
5515
5516
    /**
5517
     * @param string[]|null $items
5518
     * @param string[]      $helper
5519
     *
5520
     * @return static
5521
     *
5522
     * @psalm-return static<TKey,T>
5523
     */
5524 1
    public function toPermutation(array $items = null, array $helper = []): self
5525
    {
5526
        // init
5527 1
        $return = [];
5528
5529 1
        if ($items === null) {
5530 1
            $items = $this->toArray();
5531
        }
5532
5533 1
        if (empty($items)) {
5534 1
            $return[] = $helper;
5535
        } else {
5536 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5537 1
                $new_items = $items;
5538 1
                $new_helper = $helper;
5539 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5540
                /** @noinspection PhpSillyAssignmentInspection */
5541
                /** @var string[] $new_items */
5542 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...
5543 1
                \array_unshift($new_helper, $tmp_helper);
5544
                /** @noinspection SlowArrayOperationsInLoopInspection */
5545 1
                $return = \array_merge(
5546 1
                    $return,
5547 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5548
                );
5549
            }
5550
        }
5551
5552 1
        return static::create(
5553 1
            $return,
5554 1
            $this->iteratorClass,
5555 1
            false
5556
        );
5557
    }
5558
5559
    /**
5560
     * Implodes array to a string with specified separator.
5561
     *
5562
     * @param string $separator [optional] <p>The element's separator.</p>
5563
     *
5564
     * @return string
5565
     *                <p>The string representation of array, separated by ",".</p>
5566
     */
5567 19
    public function toString(string $separator = ','): string
5568
    {
5569 19
        return $this->implode($separator);
5570
    }
5571
5572
    /**
5573
     * Return a duplicate free copy of the current array.
5574
     *
5575
     * @return $this
5576
     *               <p>(Mutable)</p>
5577
     *
5578
     * @psalm-return static<TKey,T>
5579
     */
5580 13
    public function unique(): self
5581
    {
5582
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5583
5584
        /**
5585
         * @psalm-suppress MissingClosureReturnType
5586
         * @psalm-suppress MissingClosureParamType
5587
         */
5588 13
        $this->array = $this->reduce(
5589
            static function ($resultArray, $value) {
5590 12
                if (!\in_array($value, $resultArray, true)) {
5591 12
                    $resultArray[] = $value;
5592
                }
5593
5594 12
                return $resultArray;
5595 13
            },
5596 13
            []
5597 13
        )->toArray();
5598 13
        $this->generator = null;
5599
5600 13
        return $this;
5601
    }
5602
5603
    /**
5604
     * Return a duplicate free copy of the current array. (with the old keys)
5605
     *
5606
     * @return $this
5607
     *               <p>(Mutable)</p>
5608
     *
5609
     * @psalm-return static<TKey,T>
5610
     */
5611 11
    public function uniqueKeepIndex(): self
5612
    {
5613
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5614
5615
        // init
5616 11
        $array = $this->toArray();
5617
5618
        /**
5619
         * @psalm-suppress MissingClosureReturnType
5620
         * @psalm-suppress MissingClosureParamType
5621
         */
5622 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...
5623 11
            \array_keys($array),
5624
            static function ($resultArray, $key) use ($array) {
5625 10
                if (!\in_array($array[$key], $resultArray, true)) {
5626 10
                    $resultArray[$key] = $array[$key];
5627
                }
5628
5629 10
                return $resultArray;
5630 11
            },
5631 11
            []
5632
        );
5633 11
        $this->generator = null;
5634
5635 11
        return $this;
5636
    }
5637
5638
    /**
5639
     * alias: for "Arrayy->unique()"
5640
     *
5641
     * @return static
5642
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
5643
     *
5644
     * @see          Arrayy::unique()
5645
     *
5646
     * @psalm-return static<TKey,T>
5647
     */
5648 10
    public function uniqueNewIndex(): self
5649
    {
5650 10
        return $this->unique();
5651
    }
5652
5653
    /**
5654
     * Prepends one or more values to the beginning of array at once.
5655
     *
5656
     * @param array ...$args
5657
     *
5658
     * @return $this
5659
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
5660
     *
5661
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
5662
     * @psalm-return static<TKey,T>
5663
     */
5664 4
    public function unshift(...$args): self
5665
    {
5666 4
        $this->generatorToArray();
5667
5668 4
        \array_unshift($this->array, ...$args);
5669
5670 4
        return $this;
5671
    }
5672
5673
    /**
5674
     * Tests whether the given closure return something valid for all elements of this array.
5675
     *
5676
     * @param \Closure $closure the predicate
5677
     *
5678
     * @return bool
5679
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
5680
     */
5681 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...
5682
    {
5683 1
        foreach ($this->getGenerator() as $key => $value) {
5684 1
            if (!$closure($value, $key)) {
5685 1
                return false;
5686
            }
5687
        }
5688
5689 1
        return true;
5690
    }
5691
5692
    /**
5693
     * Get all values from a array.
5694
     *
5695
     * @return static
5696
     *                <p>(Immutable)</p>
5697
     *
5698
     * @psalm-return static<TKey,T>
5699
     * @psalm-mutation-free
5700
     */
5701 2
    public function values(): self
5702
    {
5703 2
        return static::create(
5704
            function () {
5705
                /** @noinspection YieldFromCanBeUsedInspection */
5706 2
                foreach ($this->getGenerator() as $value) {
5707 2
                    yield $value;
5708
                }
5709 2
            },
5710 2
            $this->iteratorClass,
5711 2
            false
5712
        );
5713
    }
5714
5715
    /**
5716
     * Apply the given function to every element in the array, discarding the results.
5717
     *
5718
     * @param callable $callable
5719
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
5720
     *
5721
     * @return $this
5722
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
5723
     *
5724
     * @psalm-return static<TKey,T>
5725
     */
5726 12
    public function walk($callable, bool $recursive = false): self
5727
    {
5728 12
        $this->generatorToArray();
5729
5730 12
        if ($recursive === true) {
5731 6
            \array_walk_recursive($this->array, $callable);
5732
        } else {
5733 6
            \array_walk($this->array, $callable);
5734
        }
5735
5736 12
        return $this;
5737
    }
5738
5739
    /**
5740
     * Returns a collection of matching items.
5741
     *
5742
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
5743
     * @param mixed  $value                 the value to match
5744
     *
5745
     * @throws \InvalidArgumentException if property or method is not defined
5746
     *
5747
     * @return static
5748
     *
5749
     * @psalm-param  T $value
5750
     * @psalm-return static<TKey,T>
5751
     */
5752 1
    public function where(string $keyOrPropertyOrMethod, $value): self
5753
    {
5754 1
        return $this->filter(
5755
            function ($item) use ($keyOrPropertyOrMethod, $value) {
5756 1
                $accessorValue = $this->extractValue(
5757 1
                    $item,
5758 1
                    $keyOrPropertyOrMethod
5759
                );
5760
5761 1
                return $accessorValue === $value;
5762 1
            }
5763
        );
5764
    }
5765
5766
    /**
5767
     * Convert an array into a object.
5768
     *
5769
     * @param array $array
5770
     *
5771
     * @return \stdClass
5772
     *
5773
     * @psalm-param array<mixed,mixed> $array
5774
     */
5775 4
    final protected static function arrayToObject(array $array = []): \stdClass
5776
    {
5777
        // init
5778 4
        $object = new \stdClass();
5779
5780 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
5781 1
            return $object;
5782
        }
5783
5784 3
        foreach ($array as $name => $value) {
5785 3
            if (\is_array($value) === true) {
5786 1
                $object->{$name} = static::arrayToObject($value);
5787
            } else {
5788 3
                $object->{$name} = $value;
5789
            }
5790
        }
5791
5792 3
        return $object;
5793
    }
5794
5795
    /**
5796
     * @param array|\Generator|null $input         <p>
5797
     *                                             An array containing keys to return.
5798
     *                                             </p>
5799
     * @param mixed|null            $search_values [optional] <p>
5800
     *                                             If specified, then only keys containing these values are returned.
5801
     *                                             </p>
5802
     * @param bool                  $strict        [optional] <p>
5803
     *                                             Determines if strict comparison (===) should be used during the
5804
     *                                             search.
5805
     *                                             </p>
5806
     *
5807
     * @return array
5808
     *               <p>an array of all the keys in input</p>
5809
     *
5810
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
5811
     * @psalm-return array<TKey|mixed>
5812
     * @psalm-mutation-free
5813
     */
5814 11
    protected function array_keys_recursive(
5815
        $input = null,
5816
        $search_values = null,
5817
        bool $strict = true
5818
    ): array {
5819
        // init
5820 11
        $keys = [];
5821 11
        $keysTmp = [];
5822
5823 11
        if ($input === null) {
5824 4
            $input = $this->getGenerator();
5825
        }
5826
5827 11
        if ($search_values === null) {
5828 11
            foreach ($input as $key => $value) {
5829 11
                $keys[] = $key;
5830
5831
                // check if recursive is needed
5832 11
                if (\is_array($value) === true) {
5833 11
                    $keysTmp[] = $this->array_keys_recursive($value);
5834
                }
5835
            }
5836
        } else {
5837 1
            $is_array_tmp = \is_array($search_values);
5838
5839 1
            foreach ($input as $key => $value) {
5840 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...
5841
                    (
5842 1
                        $is_array_tmp === false
5843
                        &&
5844 1
                        $strict === true
5845
                        &&
5846 1
                        $search_values === $value
5847
                    )
5848
                    ||
5849
                    (
5850 1
                        $is_array_tmp === false
5851
                        &&
5852 1
                        $strict === false
5853
                        &&
5854 1
                        $search_values == $value
5855
                    )
5856
                    ||
5857
                    (
5858 1
                        $is_array_tmp === true
5859
                        &&
5860 1
                        \in_array($value, $search_values, $strict)
5861
                    )
5862
                ) {
5863 1
                    $keys[] = $key;
5864
                }
5865
5866
                // check if recursive is needed
5867 1
                if (\is_array($value) === true) {
5868 1
                    $keysTmp[] = $this->array_keys_recursive($value);
5869
                }
5870
            }
5871
        }
5872
5873 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
5874
    }
5875
5876
    /**
5877
     * @param mixed      $path
5878
     * @param callable   $callable
5879
     * @param array|null $currentOffset
5880
     *
5881
     * @return void
5882
     *
5883
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
5884
     */
5885 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
5886
    {
5887 4
        $this->generatorToArray();
5888
5889 4
        if ($currentOffset === null) {
5890 4
            $currentOffset = &$this->array;
5891
        }
5892
5893 4
        $explodedPath = \explode($this->pathSeparator, $path);
5894 4
        if ($explodedPath === false) {
5895
            return;
5896
        }
5897
5898 4
        $nextPath = \array_shift($explodedPath);
5899
5900 4
        if (!isset($currentOffset[$nextPath])) {
5901
            return;
5902
        }
5903
5904 4
        if (!empty($explodedPath)) {
5905 1
            $this->callAtPath(
5906 1
                \implode($this->pathSeparator, $explodedPath),
5907 1
                $callable,
5908 1
                $currentOffset[$nextPath]
5909
            );
5910
        } else {
5911 4
            $callable($currentOffset[$nextPath]);
5912
        }
5913 4
    }
5914
5915
    /**
5916
     * Extracts the value of the given property or method from the object.
5917
     *
5918
     * @param static $object                <p>The object to extract the value from.</p>
5919
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
5920
     *                                      value should be extracted.</p>
5921
     *
5922
     * @throws \InvalidArgumentException if the method or property is not defined
5923
     *
5924
     * @return mixed
5925
     *               <p>The value extracted from the specified property or method.</p>
5926
     *
5927
     * @psalm-param self<TKey,T> $object
5928
     */
5929 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
5930
    {
5931 2
        if (isset($object[$keyOrPropertyOrMethod])) {
5932 2
            $return = $object->get($keyOrPropertyOrMethod);
5933
5934 2
            if ($return instanceof self) {
5935 1
                return $return->toArray();
5936
            }
5937
5938 1
            return $return;
5939
        }
5940
5941
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
5942
            return $object->{$keyOrPropertyOrMethod};
5943
        }
5944
5945
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
5946
            return $object->{$keyOrPropertyOrMethod}();
5947
        }
5948
5949
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
5950
    }
5951
5952
    /**
5953
     * create a fallback for array
5954
     *
5955
     * 1. use the current array, if it's a array
5956
     * 2. fallback to empty array, if there is nothing
5957
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
5958
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
5959
     * 5. call "__toArray()" on object, if the method exists
5960
     * 6. cast a string or object with "__toString()" into an array
5961
     * 7. throw a "InvalidArgumentException"-Exception
5962
     *
5963
     * @param mixed $data
5964
     *
5965
     * @throws \InvalidArgumentException
5966
     *
5967
     * @return array
5968
     *
5969
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5970
     */
5971 1100
    protected function fallbackForArray(&$data): array
5972
    {
5973 1100
        $data = $this->internalGetArray($data);
5974
5975 1100
        if ($data === null) {
5976 2
            throw new \InvalidArgumentException('Passed value should be a array');
5977
        }
5978
5979 1098
        return $data;
5980
    }
5981
5982
    /**
5983
     * @param bool $preserveKeys <p>
5984
     *                           e.g.: A generator maybe return the same key more then once,
5985
     *                           so maybe you will ignore the keys.
5986
     *                           </p>
5987
     *
5988
     * @return bool
5989
     *
5990
     * @noinspection ReturnTypeCanBeDeclaredInspection
5991
     * @psalm-mutation-free :/
5992
     */
5993 1014
    protected function generatorToArray(bool $preserveKeys = true)
5994
    {
5995 1014
        if ($this->generator) {
5996 2
            $this->array = $this->toArray(false, $preserveKeys);
5997 2
            $this->generator = null;
5998
5999 2
            return true;
6000
        }
6001
6002 1014
        return false;
6003
    }
6004
6005
    /**
6006
     * Get correct PHP constant for direction.
6007
     *
6008
     * @param int|string $direction
6009
     *
6010
     * @return int
6011
     * @psalm-mutation-free
6012
     */
6013 43
    protected function getDirection($direction): int
6014
    {
6015 43
        if ((string) $direction === $direction) {
6016 10
            $direction = \strtolower($direction);
6017
6018 10
            if ($direction === 'desc') {
6019 2
                $direction = \SORT_DESC;
6020
            } else {
6021 8
                $direction = \SORT_ASC;
6022
            }
6023
        }
6024
6025
        if (
6026 43
            $direction !== \SORT_DESC
6027
            &&
6028 43
            $direction !== \SORT_ASC
6029
        ) {
6030
            $direction = \SORT_ASC;
6031
        }
6032
6033 43
        return $direction;
6034
    }
6035
6036
    /**
6037
     * @return TypeCheckPhpDoc[]
6038
     *
6039
     * @noinspection ReturnTypeCanBeDeclaredInspection
6040
     */
6041 16
    protected function getPropertiesFromPhpDoc()
6042
    {
6043 16
        static $PROPERTY_CACHE = [];
6044 16
        $cacheKey = 'Class::' . static::class;
6045
6046 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6047 15
            return $PROPERTY_CACHE[$cacheKey];
6048
        }
6049
6050
        // init
6051 2
        $properties = [];
6052
6053 2
        $reflector = new \ReflectionClass($this);
6054 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6055 2
        $docComment = $reflector->getDocComment();
6056 2
        if ($docComment) {
6057 2
            $docblock = $factory->create($docComment);
6058
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6059 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6060 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
6061
            }
6062
        }
6063
6064 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6065
    }
6066
6067
    /**
6068
     * @param mixed $glue
6069
     * @param mixed $pieces
6070
     * @param bool  $useKeys
6071
     *
6072
     * @return string
6073
     */
6074 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
6075
    {
6076 36
        if ($pieces instanceof self) {
6077 1
            $pieces = $pieces->toArray();
6078
        }
6079
6080 36
        if (\is_array($pieces) === true) {
6081 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6082 36
            $pieces_count_not_zero = $pieces_count > 0;
6083
6084 36
            return \implode(
6085 36
                $glue,
6086 36
                \array_map(
6087 36
                    [$this, 'implode_recursive'],
6088 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6089 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6090
                )
6091
            );
6092
        }
6093
6094
        if (
6095 36
            \is_scalar($pieces) === true
6096
            ||
6097 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6098
        ) {
6099 32
            return (string) $pieces;
6100
        }
6101
6102 8
        return '';
6103
    }
6104
6105
    /**
6106
     * @param mixed                 $needle   <p>
6107
     *                                        The searched value.
6108
     *                                        </p>
6109
     *                                        <p>
6110
     *                                        If needle is a string, the comparison is done
6111
     *                                        in a case-sensitive manner.
6112
     *                                        </p>
6113
     * @param array|\Generator|null $haystack <p>
6114
     *                                        The array.
6115
     *                                        </p>
6116
     * @param bool                  $strict   [optional] <p>
6117
     *                                        If the third parameter strict is set to true
6118
     *                                        then the in_array function will also check the
6119
     *                                        types of the
6120
     *                                        needle in the haystack.
6121
     *                                        </p>
6122
     *
6123
     * @return bool
6124
     *              <p>true if needle is found in the array, false otherwise</p>
6125
     *
6126
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6127
     */
6128 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6129
    {
6130 18
        if ($haystack === null) {
6131
            $haystack = $this->getGenerator();
6132
        }
6133
6134 18
        foreach ($haystack as $item) {
6135 14
            if (\is_array($item) === true) {
6136 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6137
            } else {
6138
                /** @noinspection NestedPositiveIfStatementsInspection */
6139 14
                if ($strict === true) {
6140 14
                    $returnTmp = $item === $needle;
6141
                } else {
6142
                    $returnTmp = $item == $needle;
6143
                }
6144
            }
6145
6146 14
            if ($returnTmp === true) {
6147 14
                return true;
6148
            }
6149
        }
6150
6151 8
        return false;
6152
    }
6153
6154
    /**
6155
     * @param mixed $data
6156
     *
6157
     * @return array|null
6158
     *
6159
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6160
     */
6161 1100
    protected function internalGetArray(&$data)
6162
    {
6163 1100
        if (\is_array($data) === true) {
6164 1095
            return $data;
6165
        }
6166
6167 54
        if (!$data) {
6168 6
            return [];
6169
        }
6170
6171 53
        if (\is_object($data) === true) {
6172 48
            if ($data instanceof \ArrayObject) {
6173 4
                return $data->getArrayCopy();
6174
            }
6175
6176 45
            if ($data instanceof \Generator) {
6177
                return static::createFromGeneratorImmutable($data)->toArray();
6178
            }
6179
6180 45
            if ($data instanceof \Traversable) {
6181
                return static::createFromObject($data)->toArray();
6182
            }
6183
6184 45
            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...
6185
                return (array) $data->jsonSerialize();
6186
            }
6187
6188 45
            if (\method_exists($data, '__toArray')) {
6189
                return (array) $data->__toArray();
6190
            }
6191
6192 45
            if (\method_exists($data, '__toString')) {
6193
                return [(string) $data];
6194
            }
6195
        }
6196
6197 50
        if (\is_callable($data)) {
6198
            /**
6199
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6200
             */
6201 43
            $this->generator = new ArrayyRewindableGenerator($data);
6202
6203 43
            return [];
6204
        }
6205
6206 9
        if (\is_scalar($data)) {
6207 7
            return [$data];
6208
        }
6209
6210 2
        return null;
6211
    }
6212
6213
    /**
6214
     * Internal mechanics of remove method.
6215
     *
6216
     * @param mixed $key
6217
     *
6218
     * @return bool
6219
     */
6220 18
    protected function internalRemove($key): bool
6221
    {
6222 18
        $this->generatorToArray();
6223
6224
        if (
6225 18
            $this->pathSeparator
6226
            &&
6227 18
            (string) $key === $key
6228
            &&
6229 18
            \strpos($key, $this->pathSeparator) !== false
6230
        ) {
6231
            $path = \explode($this->pathSeparator, (string) $key);
6232
6233
            if ($path !== false) {
6234
                // crawl though the keys
6235
                while (\count($path, \COUNT_NORMAL) > 1) {
6236
                    $key = \array_shift($path);
6237
6238
                    if (!$this->has($key)) {
6239
                        return false;
6240
                    }
6241
6242
                    $this->array = &$this->array[$key];
6243
                }
6244
6245
                $key = \array_shift($path);
6246
            }
6247
        }
6248
6249 18
        unset($this->array[$key]);
6250
6251 18
        return true;
6252
    }
6253
6254
    /**
6255
     * Internal mechanic of set method.
6256
     *
6257
     * @param int|string|null $key
6258
     * @param mixed           $value
6259
     * @param bool            $checkProperties
6260
     *
6261
     * @return bool
6262
     */
6263 962
    protected function internalSet(
6264
        $key,
6265
        &$value,
6266
        bool $checkProperties = true
6267
    ): bool {
6268
        if (
6269 962
            $checkProperties === true
6270
            &&
6271 962
            $this->properties !== []
6272
        ) {
6273 87
            $this->checkType($key, $value);
6274
        }
6275
6276 960
        if ($key === null) {
6277
            return false;
6278
        }
6279
6280 960
        $this->generatorToArray();
6281
6282
        /** @var array<int|string,mixed> $array */
6283 960
        $array = &$this->array;
6284
6285
        /**
6286
         * https://github.com/vimeo/psalm/issues/2536
6287
         *
6288
         * @psalm-suppress PossiblyInvalidArgument
6289
         * @psalm-suppress InvalidScalarArgument
6290
         */
6291
        if (
6292 960
            $this->pathSeparator
6293
            &&
6294 960
            (string) $key === $key
6295
            &&
6296 960
            \strpos($key, $this->pathSeparator) !== false
6297
        ) {
6298 3
            $path = \explode($this->pathSeparator, (string) $key);
6299
6300 3
            if ($path !== false) {
6301
                // crawl through the keys
6302 3
                while (\count($path, \COUNT_NORMAL) > 1) {
6303 3
                    $key = \array_shift($path);
6304
6305 3
                    $array = &$array[$key];
6306
                }
6307
6308 3
                $key = \array_shift($path);
6309
            }
6310
        }
6311
6312 960
        $array[$key] = $value;
6313
6314 960
        return true;
6315
    }
6316
6317
    /**
6318
     * Convert a object into an array.
6319
     *
6320
     * @param mixed|object $object
6321
     *
6322
     * @return array|mixed
6323
     *
6324
     * @psalm-mutation-free
6325
     */
6326 5
    protected static function objectToArray($object)
6327
    {
6328 5
        if (!\is_object($object)) {
6329 4
            return $object;
6330
        }
6331
6332 5
        $object = \get_object_vars($object);
6333
6334
        /**
6335
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6336
         */
6337 5
        return \array_map(['static', 'objectToArray'], $object);
6338
    }
6339
6340
    /**
6341
     * @param array $data
6342
     * @param bool  $checkPropertiesInConstructor
6343
     *
6344
     * @return void
6345
     *
6346
     * @psalm-param array<mixed,T> $data
6347
     */
6348 1098
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6349
    {
6350 1098
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6351
                                        &&
6352 1098
                                        $checkPropertiesInConstructor === true;
6353
6354 1098
        if ($this->properties !== []) {
6355 78
            foreach ($data as $key => &$valueInner) {
6356 78
                $this->internalSet(
6357 78
                    $key,
6358 78
                    $valueInner,
6359 78
                    $checkPropertiesInConstructor
6360
                );
6361
            }
6362
        } else {
6363
            if (
6364 1031
                $this->checkPropertyTypes === true
6365
                ||
6366 1031
                $checkPropertiesInConstructor === true
6367
            ) {
6368 16
                $this->properties = $this->getPropertiesFromPhpDoc();
6369
            }
6370
6371
            /** @var TypeCheckInterface[] $properties */
6372 1031
            $properties = $this->properties;
6373
6374
            if (
6375 1031
                $this->checkPropertiesMismatchInConstructor === true
6376
                &&
6377 1031
                \count($data) !== 0
6378
                &&
6379 1031
                \count(\array_diff_key($properties, $data)) > 0
6380
            ) {
6381 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...
6382
            }
6383
6384 1030
            foreach ($data as $key => &$valueInner) {
6385 888
                $this->internalSet(
6386 888
                    $key,
6387 888
                    $valueInner,
6388 888
                    $checkPropertiesInConstructor
6389
                );
6390
            }
6391
        }
6392 1091
    }
6393
6394
    /**
6395
     * sorting keys
6396
     *
6397
     * @param array      $elements
6398
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6399
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6400
     *                              <strong>SORT_NATURAL</strong></p>
6401
     *
6402
     * @return $this
6403
     *               <p>(Mutable) Return this Arrayy object.</p>
6404
     *
6405
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6406
     * @psalm-return static<TKey,T>
6407
     */
6408 18
    protected function sorterKeys(
6409
        array &$elements,
6410
        $direction = \SORT_ASC,
6411
        int $strategy = \SORT_REGULAR
6412
    ): self {
6413 18
        $direction = $this->getDirection($direction);
6414
6415
        switch ($direction) {
6416 18
            case 'desc':
6417 18
            case \SORT_DESC:
6418 6
                \krsort($elements, $strategy);
6419
6420 6
                break;
6421 13
            case 'asc':
6422 13
            case \SORT_ASC:
6423
            default:
6424 13
                \ksort($elements, $strategy);
6425
        }
6426
6427 18
        return $this;
6428
    }
6429
6430
    /**
6431
     * @param array      $elements  <p>Warning: used as reference</p>
6432
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6433
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6434
     *                              <strong>SORT_NATURAL</strong></p>
6435
     * @param bool       $keepKeys
6436
     *
6437
     * @return $this
6438
     *               <p>(Mutable) Return this Arrayy object.</p>
6439
     *
6440
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6441
     * @psalm-return static<TKey,T>
6442
     */
6443 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6444
    {
6445 24
        $direction = $this->getDirection($direction);
6446
6447 24
        if (!$strategy) {
6448 24
            $strategy = \SORT_REGULAR;
6449
        }
6450
6451
        switch ($direction) {
6452 24
            case 'desc':
6453 24
            case \SORT_DESC:
6454 13
                if ($keepKeys) {
6455 9
                    \arsort($elements, $strategy);
6456
                } else {
6457 4
                    \rsort($elements, $strategy);
6458
                }
6459
6460 13
                break;
6461 11
            case 'asc':
6462 11
            case \SORT_ASC:
6463
            default:
6464 11
                if ($keepKeys) {
6465 4
                    \asort($elements, $strategy);
6466
                } else {
6467 7
                    \sort($elements, $strategy);
6468
                }
6469
        }
6470
6471 24
        return $this;
6472
    }
6473
6474
    /**
6475
     * @param int|string|null $key
6476
     * @param mixed           $value
6477
     *
6478
     * @return void
6479
     */
6480 87
    private function checkType($key, $value)
6481
    {
6482
        if (
6483 87
            $key !== null
6484
            &&
6485 87
            isset($this->properties[$key]) === false
6486
            &&
6487 87
            $this->checkPropertiesMismatch === true
6488
        ) {
6489
            throw new \TypeError('The key ' . $key . ' does not exists in "properties". Maybe because @property was not used for the class (' . \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 . ' do...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...
6490
        }
6491
6492 87
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6493 76
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6494 17
        } elseif ($key !== null && isset($this->properties[$key])) {
6495 17
            $this->properties[$key]->checkType($value);
6496
        }
6497 85
    }
6498
}
6499