Completed
Push — master ( e9d864...431eae )
by Lars
01:57 queued 12s
created

Arrayy::fillWithDefaults()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 0
loc 23
ccs 13
cts 13
cp 1
crap 3
rs 9.552
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
    const ARRAYY_HELPER_WALK = '!!!!Arrayy_Helper_Walk!!!!';
32
33
    /**
34
     * @var array
35
     *
36
     * @psalm-var array<mixed,mixed>|array<TKey,T>
37
     */
38
    protected $array = [];
39
40
    /**
41
     * @var \Arrayy\ArrayyRewindableGenerator|null
42
     *
43
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
44
     */
45
    protected $generator;
46
47
    /**
48
     * @var string
49
     *
50
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
51
     */
52
    protected $iteratorClass = ArrayyIterator::class;
53
54
    /**
55
     * @var string
56
     */
57
    protected $pathSeparator = '.';
58
59
    /**
60
     * @var bool
61
     */
62
    protected $checkPropertyTypes = false;
63
64
    /**
65
     * @var bool
66
     */
67
    protected $checkForMissingPropertiesInConstructor = false;
68
69
    /**
70
     * @var bool
71
     */
72
    protected $checkPropertiesMismatchInConstructor = false;
73
74
    /**
75
     * @var bool
76
     */
77
    protected $checkPropertiesMismatch = true;
78
79
    /**
80
     * @var array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
81
     */
82
    protected $properties = [];
83
84
    /**
85
     * Initializes
86
     *
87
     * @param mixed  $data                         <p>
88
     *                                             Should be an array or a generator, otherwise it will try
89
     *                                             to convert it into an array.
90
     *                                             </p>
91
     * @param string $iteratorClass                optional <p>
92
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
93
     *                                             need this option.
94
     *                                             </p>
95
     * @param bool   $checkPropertiesInConstructor optional <p>
96
     *                                             You need to extend the "Arrayy"-class and you need to set
97
     *                                             the $checkPropertiesMismatchInConstructor class property
98
     *                                             to
99
     *                                             true, otherwise this option didn't not work anyway.
100
     *                                             </p>
101
     *
102
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
103
     */
104 1206
    public function __construct(
105
        $data = [],
106
        string $iteratorClass = ArrayyIterator::class,
107
        bool $checkPropertiesInConstructor = true
108
    ) {
109 1206
        $data = $this->fallbackForArray($data);
110
111
        // used only for serialize + unserialize, all other methods are overwritten
112
        /**
113
         * @psalm-suppress InvalidArgument - why?
114
         */
115 1204
        parent::__construct([], 0, $iteratorClass);
116
117 1204
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
118
119 1196
        $this->setIteratorClass($iteratorClass);
120 1196
    }
121
122
    /**
123
     * @return void
124
     */
125 51
    public function __clone()
126
    {
127 51
        if (!\is_array($this->properties)) {
128
            $this->properties = clone $this->properties;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->properties of type object is incompatible with the declared type array of property $properties.

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

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

Loading history...
129
        }
130
131 51
        if ($this->generator !== null) {
132
            $this->generator = clone $this->generator;
133
        }
134 51
    }
135
136
    /**
137
     * Call object as function.
138
     *
139
     * @param mixed $key
140
     *
141
     * @return mixed
142
     */
143 1
    public function __invoke($key = null)
144
    {
145 1
        if ($key !== null) {
146 1
            $this->generatorToArray();
147
148 1
            return $this->array[$key] ?? false;
149
        }
150
151
        return $this->toArray();
152
    }
153
154
    /**
155
     * Whether or not an element exists by key.
156
     *
157
     * @param mixed $key
158
     *
159
     * @return bool
160
     *              <p>True is the key/index exists, otherwise false.</p>
161
     */
162
    public function __isset($key): bool
163
    {
164
        return $this->offsetExists($key);
165
    }
166
167
    /**
168
     * Assigns a value to the specified element.
169
     *
170
     * @param mixed $key
171
     * @param mixed $value
172
     *
173
     * @return void
174
     */
175 3
    public function __set($key, $value)
176
    {
177 3
        $this->internalSet($key, $value);
178 3
    }
179
180
    /**
181
     * magic to string
182
     *
183
     * @return string
184
     */
185 15
    public function __toString(): string
186
    {
187 15
        return $this->toString();
188
    }
189
190
    /**
191
     * Unset element by key.
192
     *
193
     * @param mixed $key
194
     */
195
    public function __unset($key)
196
    {
197
        $this->internalRemove($key);
198
    }
199
200
    /**
201
     * Get a value by key.
202
     *
203
     * @param mixed $key
204
     *
205
     * @return mixed
206
     *               <p>Get a Value from the current array.</p>
207
     */
208 128
    public function &__get($key)
209
    {
210 128
        $return = $this->get($key, null, null, true);
211
212 128
        if (\is_array($return) === true) {
213
            $return = static::create($return, $this->iteratorClass, false);
214
        }
215
216 128
        return $return;
217
    }
218
219
    /**
220
     * Add new values (optional using dot-notation).
221
     *
222
     * @param mixed           $value
223
     * @param int|string|null $key
224
     *
225
     * @return static
226
     *                <p>(Immutable) Return this Arrayy object, with the appended values.</p>
227
     *
228
     * @psalm-param  T $value
229
     * @psalm-return static<TKey,T>
230
     *
231
     * @psalm-mutation-free
232
     */
233 13
    public function add($value, $key = null)
234
    {
235 13
        if ($key !== null) {
236 5
            $get = $this->get($key);
237 5
            if ($get !== null) {
238 1
                $value = \array_merge_recursive(
239 1
                    !$get instanceof self ? [$get] : $get->getArray(),
240 1
                    !\is_array($value) ? [$value] : $value
241
                );
242
            }
243
244 5
            $this->internalSet($key, $value);
245
246 4
            return $this;
247
        }
248
249 8
        return $this->append($value);
250
    }
251
252
    /**
253
     * Append a (key) + value to the current array.
254
     *
255
     * EXAMPLE: <code>
256
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
257
     * </code>
258
     *
259
     * @param mixed $value
260
     * @param mixed $key
261
     *
262
     * @return $this
263
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
264
     *
265
     * @psalm-return static<TKey,T>
266
     */
267 20
    public function append($value, $key = null): self
268
    {
269 20
        $this->generatorToArray();
270
271 20
        if ($this->properties !== []) {
272 6
            $this->checkType($key, $value);
273
        }
274
275 19
        if ($key !== null) {
276
            if (
277 2
                isset($this->array[$key])
278
                &&
279 2
                \is_array($this->array[$key])
280
            ) {
281
                $this->array[$key][] = $value;
282
            } else {
283 2
                $this->array[$key] = $value;
284
            }
285
        } else {
286 17
            $this->array[] = $value;
287
        }
288
289 19
        return $this;
290
    }
291
292
    /**
293
     * Append a (key) + value to the current array.
294
     *
295
     * EXAMPLE: <code>
296
     * a(['fòô' => 'bàř'])->appendImmutable('foo')->getArray(); // ['fòô' => 'bàř', 0 => 'foo']
297
     * </code>
298
     *
299
     * @param mixed $value
300
     * @param mixed $key
301
     *
302
     * @return $this
303
     *               <p>(Immutable) Return this Arrayy object, with the appended values.</p>
304
     *
305
     * @psalm-return static<TKey,T>
306
     * @psalm-mutation-free
307
     */
308 1 View Code Duplication
    public function appendImmutable($value, $key = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

Loading history...
2835
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2836
            }
2837
2838 28
            if (\is_array($usedArrayTmp)) {
2839 6
                return static::create(
2840 6
                    [],
2841 6
                    $this->iteratorClass,
2842 6
                    false
2843 6
                )->createByReference($usedArrayTmp);
2844
            }
2845
2846 28
            return $usedArrayTmp;
2847
        }
2848
2849 30 View Code Duplication
        if (!$usePath && !isset($usedArray[$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2850 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2851
        }
2852
2853
        return static::create(
2854
            [],
2855
            $this->iteratorClass,
2856
            false
2857
        )->createByReference($usedArray);
2858
    }
2859
2860
    /**
2861
     * alias: for "Arrayy->toArray()"
2862
     *
2863
     * @return array
2864
     *
2865
     * @see          Arrayy::getArray()
2866
     *
2867
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2868
     */
2869 15
    public function getAll(): array
2870
    {
2871 15
        return $this->toArray();
2872
    }
2873
2874
    /**
2875
     * Get the current array from the "Arrayy"-object.
2876
     *
2877
     * alias for "toArray()"
2878
     *
2879
     * @param bool $convertAllArrayyElements <p>
2880
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2881
     *                                       </p>
2882
     * @param bool $preserveKeys             <p>
2883
     *                                       e.g.: A generator maybe return the same key more then once,
2884
     *                                       so maybe you will ignore the keys.
2885
     *                                       </p>
2886
     *
2887
     * @return array
2888
     *
2889
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2890
     * @psalm-mutation-free
2891
     *
2892
     * @see Arrayy::toArray()
2893
     */
2894 501
    public function getArray(
2895
        bool $convertAllArrayyElements = false,
2896
        bool $preserveKeys = true
2897
    ): array {
2898 501
        return $this->toArray(
2899 501
            $convertAllArrayyElements,
2900 501
            $preserveKeys
2901
        );
2902
    }
2903
2904
    /**
2905
     * @param string $json
2906
     *
2907
     * @return $this
2908
     */
2909 3
    public static function createFromJsonMapper(string $json)
2910
    {
2911
        // init
2912 3
        $class = static::create();
2913
2914 3
        $jsonObject = \json_decode($json, false);
2915
2916 3
        $mapper = new \Arrayy\Mapper\Json();
2917 View Code Duplication
        $mapper->undefinedPropertyHandler = static function ($object, $key, $jsonValue) use ($class) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2918
            if ($class->checkPropertiesMismatchInConstructor) {
2919
                throw new \TypeError('Property mismatch - input: ' . \print_r(['key' => $key, 'jsonValue' => $jsonValue], true) . ' for object: ' . \get_class($object));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...' . \get_class($object).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2920
            }
2921
        };
2922
2923 3
        return $mapper->map($jsonObject, $class);
2924
    }
2925
2926
    /**
2927
     * @return array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
0 ignored issues
show
Documentation introduced by
The doc-type array<int|string,TypeChe...nterface>|TypeInterface could not be parsed: Expected "|" or "end of type", but got "<" at position 57. (view supported doc-types)

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

Loading history...
2928
     *
2929
     * @internal
2930
     */
2931 6
    public function getPhpDocPropertiesFromClass()
2932
    {
2933 6
        if ($this->properties === []) {
2934 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2935
        }
2936
2937 6
        return $this->properties;
2938
    }
2939
2940
    /**
2941
     * Get the current array from the "Arrayy"-object as list.
2942
     *
2943
     * alias for "toList()"
2944
     *
2945
     * @param bool $convertAllArrayyElements <p>
2946
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2947
     *                                       </p>
2948
     *
2949
     * @return array
2950
     *
2951
     * @psalm-return array<int,mixed>|array<int,T>
2952
     * @psalm-mutation-free
2953
     *
2954
     * @see Arrayy::toList()
2955
     */
2956 1
    public function getList(bool $convertAllArrayyElements = false): array
2957
    {
2958 1
        return $this->toList($convertAllArrayyElements);
2959
    }
2960
2961
    /**
2962
     * Returns the values from a single column of the input array, identified by
2963
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2964
     *
2965
     * EXAMPLE: <code>
2966
     * a([['foo' => 'bar', 'id' => 1], ['foo => 'lall', 'id' => 2]])->getColumn('foo', 'id'); // Arrayy[1 => 'bar', 2 => 'lall']
2967
     * </code>
2968
     *
2969
     * INFO: Optionally, you may provide an $indexKey to index the values in the returned
2970
     *       array by the values from the $indexKey column in the input array.
2971
     *
2972
     * @param mixed $columnKey
2973
     * @param mixed $indexKey
2974
     *
2975
     * @return static
2976
     *                <p>(Immutable)</p>
2977
     *
2978
     * @psalm-return static<TKey,T>
2979
     * @psalm-mutation-free
2980
     */
2981 1
    public function getColumn($columnKey = null, $indexKey = null): self
2982
    {
2983 1
        return static::create(
2984 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2985 1
            $this->iteratorClass,
2986 1
            false
2987
        );
2988
    }
2989
2990
    /**
2991
     * Get the current array from the "Arrayy"-object as generator by reference.
2992
     *
2993
     * @return \Generator
2994
     *
2995
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2996
     */
2997 75
    public function &getGeneratorByReference(): \Generator
2998
    {
2999 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3000
            // -> false-positive -> see "&" from method
3001
            /** @noinspection YieldFromCanBeUsedInspection */
3002 17
            foreach ($this->generator as $key => $value) {
3003 17
                yield $key => $value;
3004
            }
3005
3006 5
            return;
3007
        }
3008
3009
        // -> false-positive -> see "&$value"
3010
        /** @noinspection YieldFromCanBeUsedInspection */
3011
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3012 59
        foreach ($this->array as $key => &$value) {
3013 54
            yield $key => $value;
3014
        }
3015 35
    }
3016
3017
    /**
3018
     * Get the current array from the "Arrayy"-object as generator.
3019
     *
3020
     * @return \Generator
3021
     *
3022
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
3023
     * @psalm-mutation-free
3024
     */
3025 1000
    public function getGenerator(): \Generator
3026
    {
3027 1000
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3028 41
            yield from $this->generator;
3029
3030 41
            return;
3031
        }
3032
3033 998
        yield from $this->array;
3034 968
    }
3035
3036
    /**
3037
     * alias: for "Arrayy->keys()"
3038
     *
3039
     * @return static
3040
     *                <p>(Immutable)</p>
3041
     *
3042
     * @see          Arrayy::keys()
3043
     *
3044
     * @psalm-return static<array-key,TKey>
3045
     * @psalm-mutation-free
3046
     */
3047 2
    public function getKeys()
3048
    {
3049 2
        return $this->keys();
3050
    }
3051
3052
    /**
3053
     * Get the current array from the "Arrayy"-object as object.
3054
     *
3055
     * @return \stdClass
3056
     */
3057 4
    public function getObject(): \stdClass
3058
    {
3059 4
        return self::arrayToObject($this->toArray());
3060
    }
3061
3062
    /**
3063
     * alias: for "Arrayy->randomImmutable()"
3064
     *
3065
     * @return static
3066
     *                <p>(Immutable)</p>
3067
     *
3068
     * @see          Arrayy::randomImmutable()
3069
     *
3070
     * @psalm-return static<int|array-key,T>
3071
     */
3072 4
    public function getRandom(): self
3073
    {
3074 4
        return $this->randomImmutable();
3075
    }
3076
3077
    /**
3078
     * alias: for "Arrayy->randomKey()"
3079
     *
3080
     * @return mixed
3081
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3082
     *
3083
     * @see Arrayy::randomKey()
3084
     */
3085 3
    public function getRandomKey()
3086
    {
3087 3
        return $this->randomKey();
3088
    }
3089
3090
    /**
3091
     * alias: for "Arrayy->randomKeys()"
3092
     *
3093
     * @param int $number
3094
     *
3095
     * @return static
3096
     *                <p>(Immutable)</p>
3097
     *
3098
     * @see          Arrayy::randomKeys()
3099
     *
3100
     * @psalm-return static<TKey,T>
3101
     */
3102 8
    public function getRandomKeys(int $number): self
3103
    {
3104 8
        return $this->randomKeys($number);
3105
    }
3106
3107
    /**
3108
     * alias: for "Arrayy->randomValue()"
3109
     *
3110
     * @return mixed
3111
     *               <p>Get a random value or null if there wasn't a value.</p>
3112
     *
3113
     * @see Arrayy::randomValue()
3114
     */
3115 3
    public function getRandomValue()
3116
    {
3117 3
        return $this->randomValue();
3118
    }
3119
3120
    /**
3121
     * alias: for "Arrayy->randomValues()"
3122
     *
3123
     * @param int $number
3124
     *
3125
     * @return static
3126
     *                <p>(Immutable)</p>
3127
     *
3128
     * @see          Arrayy::randomValues()
3129
     *
3130
     * @psalm-return static<TKey,T>
3131
     */
3132 6
    public function getRandomValues(int $number): self
3133
    {
3134 6
        return $this->randomValues($number);
3135
    }
3136
3137
    /**
3138
     * Gets all values.
3139
     *
3140
     * @return static
3141
     *                <p>The values of all elements in this array, in the order they
3142
     *                appear in the array.</p>
3143
     *
3144
     * @psalm-return static<TKey,T>
3145
     */
3146 4
    public function getValues()
3147
    {
3148 4
        $this->generatorToArray(false);
3149
3150 4
        return static::create(
3151 4
            \array_values($this->array),
3152 4
            $this->iteratorClass,
3153 4
            false
3154
        );
3155
    }
3156
3157
    /**
3158
     * Gets all values via Generator.
3159
     *
3160
     * @return \Generator
3161
     *                    <p>The values of all elements in this array, in the order they
3162
     *                    appear in the array as Generator.</p>
3163
     *
3164
     * @psalm-return \Generator<TKey,T>
3165
     */
3166 4
    public function getValuesYield(): \Generator
3167
    {
3168 4
        yield from $this->getGenerator();
3169 4
    }
3170
3171
    /**
3172
     * Group values from a array according to the results of a closure.
3173
     *
3174
     * @param callable|string $grouper  <p>A callable function name.</p>
3175
     * @param bool            $saveKeys
3176
     *
3177
     * @return static
3178
     *                <p>(Immutable)</p>
3179
     *
3180
     * @psalm-return static<TKey,T>
3181
     * @psalm-mutation-free
3182
     */
3183 4
    public function group($grouper, bool $saveKeys = false): self
3184
    {
3185
        // init
3186 4
        $result = [];
3187
3188
        // Iterate over values, group by property/results from closure.
3189 4
        foreach ($this->getGenerator() as $key => $value) {
3190 4
            if (\is_callable($grouper) === true) {
3191 3
                $groupKey = $grouper($value, $key);
3192
            } else {
3193 1
                $groupKey = $this->get($grouper);
3194
            }
3195
3196 4
            $newValue = $this->get($groupKey, null, $result);
3197
3198 4
            if ($groupKey instanceof self) {
3199
                $groupKey = $groupKey->toArray();
3200
            }
3201
3202 4
            if ($newValue instanceof self) {
3203 4
                $newValue = $newValue->toArray();
3204
            }
3205
3206
            // Add to results.
3207 4
            if ($groupKey !== null) {
3208 3
                if ($saveKeys) {
3209 2
                    $result[$groupKey] = $newValue;
3210 2
                    $result[$groupKey][$key] = $value;
3211
                } else {
3212 1
                    $result[$groupKey] = $newValue;
3213 4
                    $result[$groupKey][] = $value;
3214
                }
3215
            }
3216
        }
3217
3218 4
        return static::create(
3219 4
            $result,
3220 4
            $this->iteratorClass,
3221 4
            false
3222
        );
3223
    }
3224
3225
    /**
3226
     * Check if an array has a given key.
3227
     *
3228
     * @param mixed $key
3229
     *
3230
     * @return bool
3231
     */
3232 30
    public function has($key): bool
3233
    {
3234 30
        static $UN_FOUND = null;
3235
3236 30
        if ($UN_FOUND === null) {
3237
            // Generate unique string to use as marker.
3238 1
            $UN_FOUND = \uniqid('arrayy', true);
3239
        }
3240
3241 30
        if (\is_array($key)) {
3242 1
            if ($key === []) {
3243
                return false;
3244
            }
3245
3246 1
            foreach ($key as $keyTmp) {
3247 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3248 1
                if ($found === false) {
3249 1
                    return false;
3250
                }
3251
            }
3252
3253 1
            return true;
3254
        }
3255
3256 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3257
    }
3258
3259
    /**
3260
     * Check if an array has a given value.
3261
     *
3262
     * INFO: If you need to search recursive please use ```contains($value, true)```.
3263
     *
3264
     * @param mixed $value
3265
     *
3266
     * @return bool
3267
     */
3268 1
    public function hasValue($value): bool
3269
    {
3270 1
        return $this->contains($value);
3271
    }
3272
3273
    /**
3274
     * Implodes the values of this array.
3275
     *
3276
     * EXAMPLE: <code>
3277
     * a([0 => -9, 1, 2])->implode('|'); // '-9|1|2'
3278
     * </code>
3279
     *
3280
     * @param string $glue
3281
     * @param string $prefix
3282
     *
3283
     * @return string
3284
     * @psalm-mutation-free
3285
     */
3286 28
    public function implode(string $glue = '', string $prefix = ''): string
3287
    {
3288 28
        return $prefix . $this->implode_recursive($glue, $this->toArray(), false);
3289
    }
3290
3291
    /**
3292
     * Implodes the keys of this array.
3293
     *
3294
     * @param string $glue
3295
     *
3296
     * @return string
3297
     * @psalm-mutation-free
3298
     */
3299 8
    public function implodeKeys(string $glue = ''): string
3300
    {
3301 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3302
    }
3303
3304
    /**
3305
     * Given a list and an iterate-function that returns
3306
     * a key for each element in the list (or a property name),
3307
     * returns an object with an index of each item.
3308
     *
3309
     * @param mixed $key
3310
     *
3311
     * @return static
3312
     *                <p>(Immutable)</p>
3313
     *
3314
     * @psalm-return static<TKey,T>
3315
     * @psalm-mutation-free
3316
     */
3317 4 View Code Duplication
    public function indexBy($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3318
    {
3319
        // init
3320 4
        $results = [];
3321
3322 4
        foreach ($this->getGenerator() as $a) {
3323 4
            if (\array_key_exists($key, $a) === true) {
3324 4
                $results[$a[$key]] = $a;
3325
            }
3326
        }
3327
3328 4
        return static::create(
3329 4
            $results,
3330 4
            $this->iteratorClass,
3331 4
            false
3332
        );
3333
    }
3334
3335
    /**
3336
     * alias: for "Arrayy->searchIndex()"
3337
     *
3338
     * @param mixed $value <p>The value to search for.</p>
3339
     *
3340
     * @return false|mixed
3341
     *
3342
     * @see Arrayy::searchIndex()
3343
     */
3344 4
    public function indexOf($value)
3345
    {
3346 4
        return $this->searchIndex($value);
3347
    }
3348
3349
    /**
3350
     * Get everything but the last..$to items.
3351
     *
3352
     * EXAMPLE: <code>
3353
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->initial(2); // Arrayy[0 => 'foo']
3354
     * </code>
3355
     *
3356
     * @param int $to
3357
     *
3358
     * @return static
3359
     *                <p>(Immutable)</p>
3360
     *
3361
     * @psalm-return static<TKey,T>
3362
     * @psalm-mutation-free
3363
     */
3364 12
    public function initial(int $to = 1): self
3365
    {
3366 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3367
    }
3368
3369
    /**
3370
     * Return an array with all elements found in input array.
3371
     *
3372
     * EXAMPLE: <code>
3373
     * a(['foo', 'bar'])->intersection(['bar', 'baz']); // Arrayy['bar']
3374
     * </code>
3375
     *
3376
     * @param array $search
3377
     * @param bool  $keepKeys
3378
     *
3379
     * @return static
3380
     *                <p>(Immutable)</p>
3381
     *
3382
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3383
     * @psalm-return static<TKey,T>
3384
     * @psalm-mutation-free
3385
     */
3386 4
    public function intersection(array $search, bool $keepKeys = false): self
3387
    {
3388 4
        if ($keepKeys) {
3389
            /**
3390
             * @psalm-suppress MissingClosureReturnType
3391
             * @psalm-suppress MissingClosureParamType
3392
             */
3393 1
            return static::create(
3394 1
                \array_uintersect(
3395 1
                    $this->toArray(),
3396 1
                    $search,
3397
                    static function ($a, $b) {
3398 1
                        return $a === $b ? 0 : -1;
3399 1
                    }
3400
                ),
3401 1
                $this->iteratorClass,
3402 1
                false
3403
            );
3404
        }
3405
3406 3
        return static::create(
3407 3
            \array_values(\array_intersect($this->toArray(), $search)),
3408 3
            $this->iteratorClass,
3409 3
            false
3410
        );
3411
    }
3412
3413
    /**
3414
     * Return an array with all elements found in input array.
3415
     *
3416
     * @param array ...$array
3417
     *
3418
     * @return static
3419
     *                <p>(Immutable)</p>
3420
     *
3421
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3422
     * @psalm-return static<TKey,T>
3423
     * @psalm-mutation-free
3424
     */
3425 1
    public function intersectionMulti(...$array): self
3426
    {
3427 1
        return static::create(
3428 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3429 1
            $this->iteratorClass,
3430 1
            false
3431
        );
3432
    }
3433
3434
    /**
3435
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3436
     *
3437
     * EXAMPLE: <code>
3438
     * a(['foo', 'bar'])->intersects(['föö', 'bär']); // false
3439
     * </code>
3440
     *
3441
     * @param array $search
3442
     *
3443
     * @return bool
3444
     *
3445
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3446
     */
3447 1
    public function intersects(array $search): bool
3448
    {
3449 1
        return $this->intersection($search)->count() > 0;
3450
    }
3451
3452
    /**
3453
     * Invoke a function on all of an array's values.
3454
     *
3455
     * @param callable $callable
3456
     * @param mixed    $arguments
3457
     *
3458
     * @return static
3459
     *                <p>(Immutable)</p>
3460
     *
3461
     * @psalm-param  callable(T=,mixed):mixed $callable
3462
     * @psalm-return static<TKey,T>
3463
     * @psalm-mutation-free
3464
     */
3465 1
    public function invoke($callable, $arguments = []): self
3466
    {
3467
        // If one argument given for each iteration, create an array for it.
3468 1
        if (!\is_array($arguments)) {
3469 1
            $arguments = \array_fill(
3470 1
                0,
3471 1
                $this->count(),
3472 1
                $arguments
3473
            );
3474
        }
3475
3476
        // If the callable has arguments, pass them.
3477 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...
3478 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3479
        } else {
3480 1
            $array = $this->map($callable);
3481
        }
3482
3483 1
        return static::create(
3484 1
            $array,
3485 1
            $this->iteratorClass,
3486 1
            false
3487
        );
3488
    }
3489
3490
    /**
3491
     * Check whether array is associative or not.
3492
     *
3493
     * EXAMPLE: <code>
3494
     * a(['foo' => 'bar', 2, 3])->isAssoc(); // true
3495
     * </code>
3496
     *
3497
     * @param bool $recursive
3498
     *
3499
     * @return bool
3500
     *              <p>Returns true if associative, false otherwise.</p>
3501
     */
3502 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...
3503
    {
3504 15
        if ($this->isEmpty()) {
3505 3
            return false;
3506
        }
3507
3508
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3509 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3510 13
            if ((string) $key !== $key) {
3511 13
                return false;
3512
            }
3513
        }
3514
3515 3
        return true;
3516
    }
3517
3518
    /**
3519
     * Check if a given key or keys are empty.
3520
     *
3521
     * @param int|int[]|string|string[]|null $keys
3522
     *
3523
     * @return bool
3524
     *              <p>Returns true if empty, false otherwise.</p>
3525
     * @psalm-mutation-free
3526
     */
3527 45
    public function isEmpty($keys = null): bool
3528
    {
3529 45
        if ($this->generator) {
3530
            return $this->toArray() === [];
3531
        }
3532
3533 45
        if ($keys === null) {
3534 43
            return $this->array === [];
3535
        }
3536
3537 2
        foreach ((array) $keys as $key) {
3538 2
            if (!empty($this->get($key))) {
3539 2
                return false;
3540
            }
3541
        }
3542
3543 2
        return true;
3544
    }
3545
3546
    /**
3547
     * Check if the current array is equal to the given "$array" or not.
3548
     *
3549
     * EXAMPLE: <code>
3550
     * a(['💩'])->isEqual(['💩']); // true
3551
     * </code>
3552
     *
3553
     * @param array $array
3554
     *
3555
     * @return bool
3556
     *
3557
     * @psalm-param array<mixed,mixed> $array
3558
     */
3559 1
    public function isEqual(array $array): bool
3560
    {
3561 1
        return $this->toArray() === $array;
3562
    }
3563
3564
    /**
3565
     * Check if the current array is a multi-array.
3566
     *
3567
     * EXAMPLE: <code>
3568
     * a(['foo' => [1, 2 , 3]])->isMultiArray(); // true
3569
     * </code>
3570
     *
3571
     * @return bool
3572
     */
3573 22
    public function isMultiArray(): bool
3574
    {
3575 22
        foreach ($this->getGenerator() as $key => $value) {
3576 20
            if (\is_array($value)) {
3577 20
                return true;
3578
            }
3579
        }
3580
3581 18
        return false;
3582
    }
3583
3584
    /**
3585
     * Check whether array is numeric or not.
3586
     *
3587
     * @return bool
3588
     *              <p>Returns true if numeric, false otherwise.</p>
3589
     */
3590 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...
3591
    {
3592 5
        if ($this->isEmpty()) {
3593 2
            return false;
3594
        }
3595
3596
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3597 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3598 4
            if ((int) $key !== $key) {
3599 4
                return false;
3600
            }
3601
        }
3602
3603 2
        return true;
3604
    }
3605
3606
    /**
3607
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3608
     *
3609
     * EXAMPLE: <code>
3610
     * a([0 => 'foo', 1 => 'lall', 2 => 'foobar'])->isSequential(); // true
3611
     * </code>
3612
     *
3613
     * INFO: If the array is empty we count it as non-sequential.
3614
     *
3615
     * @param bool $recursive
3616
     *
3617
     * @return bool
3618
     * @psalm-mutation-free
3619
     */
3620 10
    public function isSequential(bool $recursive = false): bool
3621
    {
3622 10
        $i = 0;
3623 10
        foreach ($this->getGenerator() as $key => $value) {
3624
            if (
3625 9
                $recursive
3626
                &&
3627 9
                (\is_array($value) || $value instanceof self)
3628
                &&
3629 9
                self::create($value)->isSequential() === false
3630
            ) {
3631 1
                return false;
3632
            }
3633
3634 9
            if ($key !== $i) {
3635 3
                return false;
3636
            }
3637
3638 8
            ++$i;
3639
        }
3640
3641
        /** @noinspection IfReturnReturnSimplificationInspection */
3642 9
        if ($i === 0) {
3643 3
            return false;
3644
        }
3645
3646 8
        return true;
3647
    }
3648
3649
    /**
3650
     * @return array
3651
     *
3652
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3653
     */
3654 2
    public function jsonSerialize(): array
3655
    {
3656 2
        return $this->toArray();
3657
    }
3658
3659
    /**
3660
     * Gets the key/index of the element at the current internal iterator position.
3661
     *
3662
     * @return int|string|null
3663
     */
3664
    public function key()
3665
    {
3666
        return \key($this->array);
3667
    }
3668
3669
    /**
3670
     * Checks if the given key exists in the provided array.
3671
     *
3672
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3673
     *       then you need to use "Arrayy->offsetExists()".
3674
     *
3675
     * @param int|string $key the key to look for
3676
     *
3677
     * @return bool
3678
     * @psalm-mutation-free
3679
     */
3680 164
    public function keyExists($key): bool
3681
    {
3682 164
        return \array_key_exists($key, $this->array);
3683
    }
3684
3685
    /**
3686
     * Get all keys from the current array.
3687
     *
3688
     * EXAMPLE: <code>
3689
     * a([1 => 'foo', 2 => 'foo2', 3 => 'bar'])->keys(); // Arrayy[1, 2, 3]
3690
     * </code>
3691
     *
3692
     * @param bool       $recursive     [optional] <p>
3693
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3694
     *                                  </p>
3695
     * @param mixed|null $search_values [optional] <p>
3696
     *                                  If specified, then only keys containing these values are returned.
3697
     *                                  </p>
3698
     * @param bool       $strict        [optional] <p>
3699
     *                                  Determines if strict comparison (===) should be used during the search.
3700
     *                                  </p>
3701
     *
3702
     * @return static
3703
     *                <p>(Immutable) An array of all the keys in input.</p>
3704
     *
3705
     * @psalm-return static<array-key,TKey>
3706
     * @psalm-mutation-free
3707
     */
3708 29
    public function keys(
3709
        bool $recursive = false,
3710
        $search_values = null,
3711
        bool $strict = true
3712
    ): self {
3713
3714
        // recursive
3715
3716 29
        if ($recursive === true) {
3717 4
            $array = $this->array_keys_recursive(
3718 4
                null,
3719 4
                $search_values,
3720 4
                $strict
3721
            );
3722
3723 4
            return static::create(
3724 4
                $array,
3725 4
                $this->iteratorClass,
3726 4
                false
3727
            );
3728
        }
3729
3730
        // non recursive
3731
3732 28
        if ($search_values === null) {
3733
            $arrayFunction = function (): \Generator {
3734 28
                foreach ($this->getGenerator() as $key => $value) {
3735 26
                    yield $key;
3736
                }
3737 28
            };
3738
        } else {
3739
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3740 1
                $is_array_tmp = \is_array($search_values);
3741
3742
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3743 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3744 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...
3745
                        (
3746 1
                            $is_array_tmp === false
3747
                            &&
3748 1
                            $strict === true
3749
                            &&
3750 1
                            $search_values === $value
3751
                        )
3752
                        ||
3753
                        (
3754 1
                            $is_array_tmp === false
3755
                            &&
3756 1
                            $strict === false
3757
                            &&
3758 1
                            $search_values == $value
3759
                        )
3760
                        ||
3761
                        (
3762 1
                            $is_array_tmp === true
3763
                            &&
3764 1
                            \in_array($value, $search_values, $strict)
3765
                        )
3766
                    ) {
3767 1
                        yield $key;
3768
                    }
3769
                }
3770 1
            };
3771
        }
3772
3773 28
        return static::create(
3774 28
            $arrayFunction,
3775 28
            $this->iteratorClass,
3776 28
            false
3777
        );
3778
    }
3779
3780
    /**
3781
     * Sort an array by key in reverse order.
3782
     *
3783
     * @param int $sort_flags [optional] <p>
3784
     *                        You may modify the behavior of the sort using the optional
3785
     *                        parameter sort_flags, for details
3786
     *                        see sort.
3787
     *                        </p>
3788
     *
3789
     * @return $this
3790
     *               <p>(Mutable) Return this Arrayy object.</p>
3791
     *
3792
     * @psalm-return static<TKey,T>
3793
     */
3794 4
    public function krsort(int $sort_flags = 0): self
3795
    {
3796 4
        $this->generatorToArray();
3797
3798 4
        \krsort($this->array, $sort_flags);
3799
3800 4
        return $this;
3801
    }
3802
3803
    /**
3804
     * Sort an array by key in reverse order.
3805
     *
3806
     * @param int $sort_flags [optional] <p>
3807
     *                        You may modify the behavior of the sort using the optional
3808
     *                        parameter sort_flags, for details
3809
     *                        see sort.
3810
     *                        </p>
3811
     *
3812
     * @return $this
3813
     *               <p>(Immutable) Return this Arrayy object.</p>
3814
     *
3815
     * @psalm-return static<TKey,T>
3816
     * @psalm-mutation-free
3817
     */
3818 4
    public function krsortImmutable(int $sort_flags = 0): self
3819
    {
3820 4
        $that = clone $this;
3821
3822
        /**
3823
         * @psalm-suppress ImpureMethodCall - object is already cloned
3824
         */
3825 4
        $that->krsort($sort_flags);
3826
3827 4
        return $that;
3828
    }
3829
3830
    /**
3831
     * Get the last value from the current array.
3832
     *
3833
     * EXAMPLE: <code>
3834
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->last(); // 'lall'
3835
     * </code>
3836
     *
3837
     * @return mixed|null
3838
     *                    <p>Return null if there wasn't a element.</p>
3839
     * @psalm-mutation-free
3840
     */
3841 17
    public function last()
3842
    {
3843 17
        $key_last = $this->lastKey();
3844 17
        if ($key_last === null) {
3845 2
            return null;
3846
        }
3847
3848 15
        return $this->get($key_last);
3849
    }
3850
3851
    /**
3852
     * Get the last key from the current array.
3853
     *
3854
     * @return mixed|null
3855
     *                    <p>Return null if there wasn't a element.</p>
3856
     * @psalm-mutation-free
3857
     */
3858 21
    public function lastKey()
3859
    {
3860 21
        $this->generatorToArray();
3861
3862 21
        return \array_key_last($this->array);
3863
    }
3864
3865
    /**
3866
     * Get the last value(s) from the current array.
3867
     *
3868
     * EXAMPLE: <code>
3869
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
3870
     * </code>
3871
     *
3872
     * @param int|null $number
3873
     *
3874
     * @return static
3875
     *                <p>(Immutable)</p>
3876
     *
3877
     * @psalm-return static<TKey,T>
3878
     * @psalm-mutation-free
3879
     */
3880 13
    public function lastsImmutable(int $number = null): self
3881
    {
3882 13
        if ($this->isEmpty()) {
3883 1
            return static::create(
3884 1
                [],
3885 1
                $this->iteratorClass,
3886 1
                false
3887
            );
3888
        }
3889
3890 12
        if ($number === null) {
3891 8
            $poppedValue = $this->last();
3892
3893 8
            if ($poppedValue === null) {
3894 1
                $poppedValue = [$poppedValue];
3895
            } else {
3896 7
                $poppedValue = (array) $poppedValue;
3897
            }
3898
3899 8
            $arrayy = static::create(
3900 8
                $poppedValue,
3901 8
                $this->iteratorClass,
3902 8
                false
3903
            );
3904
        } else {
3905 4
            $arrayy = $this->rest(-$number);
3906
        }
3907
3908 12
        return $arrayy;
3909
    }
3910
3911
    /**
3912
     * Get the last value(s) from the current array.
3913
     *
3914
     * EXAMPLE: <code>
3915
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
3916
     * </code>
3917
     *
3918
     * @param int|null $number
3919
     *
3920
     * @return $this
3921
     *               <p>(Mutable)</p>
3922
     *
3923
     * @psalm-return static<TKey,T>
3924
     */
3925 13
    public function lastsMutable(int $number = null): self
3926
    {
3927 13
        if ($this->isEmpty()) {
3928 1
            return $this;
3929
        }
3930
3931 12
        $this->array = $this->lastsImmutable($number)->toArray();
3932 12
        $this->generator = null;
3933
3934 12
        return $this;
3935
    }
3936
3937
    /**
3938
     * Count the values from the current array.
3939
     *
3940
     * alias: for "Arrayy->count()"
3941
     *
3942
     * @param int $mode
3943
     *
3944
     * @return int
3945
     *
3946
     * @see Arrayy::count()
3947
     */
3948 20
    public function length(int $mode = \COUNT_NORMAL): int
3949
    {
3950 20
        return $this->count($mode);
3951
    }
3952
3953
    /**
3954
     * Apply the given function to the every element of the array,
3955
     * collecting the results.
3956
     *
3957
     * @param callable $callable
3958
     * @param bool     $useKeyAsSecondParameter
3959
     * @param mixed    ...$arguments
3960
     *
3961
     * @return static
3962
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3963
     *
3964
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3965
     * @psalm-return static<TKey,T>
3966
     * @psalm-mutation-free
3967
     */
3968 5
    public function map(
3969
        callable $callable,
3970
        bool $useKeyAsSecondParameter = false,
3971
        ...$arguments
3972
    ) {
3973
        /**
3974
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3975
         */
3976 5
        $useArguments = \func_num_args() > 2;
3977
3978 5
        return static::create(
3979
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3980 5
                foreach ($this->getGenerator() as $key => $value) {
3981 4
                    if ($useArguments) {
3982 3
                        if ($useKeyAsSecondParameter) {
3983
                            yield $key => $callable($value, $key, ...$arguments);
3984
                        } else {
3985 3
                            yield $key => $callable($value, ...$arguments);
3986
                        }
3987
                    } else {
3988
                        /** @noinspection NestedPositiveIfStatementsInspection */
3989 4
                        if ($useKeyAsSecondParameter) {
3990
                            yield $key => $callable($value, $key);
3991
                        } else {
3992 4
                            yield $key => $callable($value);
3993
                        }
3994
                    }
3995
                }
3996 5
            },
3997 5
            $this->iteratorClass,
3998 5
            false
3999
        );
4000
    }
4001
4002
    /**
4003
     * Check if all items in current array match a truth test.
4004
     *
4005
     * EXAMPLE: <code>
4006
     * $closure = function ($value, $key) {
4007
     *     return ($value % 2 === 0);
4008
     * };
4009
     * a([2, 4, 8])->matches($closure); // true
4010
     * </code>
4011
     *
4012
     * @param \Closure $closure
4013
     *
4014
     * @return bool
4015
     */
4016 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...
4017
    {
4018 15
        if ($this->count() === 0) {
4019 2
            return false;
4020
        }
4021
4022 13
        foreach ($this->getGenerator() as $key => $value) {
4023 13
            $value = $closure($value, $key);
4024
4025 13
            if ($value === false) {
4026 13
                return false;
4027
            }
4028
        }
4029
4030 7
        return true;
4031
    }
4032
4033
    /**
4034
     * Check if any item in the current array matches a truth test.
4035
     *
4036
     * EXAMPLE: <code>
4037
     * $closure = function ($value, $key) {
4038
     *     return ($value % 2 === 0);
4039
     * };
4040
     * a([1, 4, 7])->matches($closure); // true
4041
     * </code>
4042
     *
4043
     * @param \Closure $closure
4044
     *
4045
     * @return bool
4046
     */
4047 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...
4048
    {
4049 14
        if ($this->count() === 0) {
4050 2
            return false;
4051
        }
4052
4053 12
        foreach ($this->getGenerator() as $key => $value) {
4054 12
            $value = $closure($value, $key);
4055
4056 12
            if ($value === true) {
4057 12
                return true;
4058
            }
4059
        }
4060
4061 4
        return false;
4062
    }
4063
4064
    /**
4065
     * Get the max value from an array.
4066
     *
4067
     * EXAMPLE: <code>
4068
     * a([-9, -8, -7, 1.32])->max(); // 1.32
4069
     * </code>
4070
     *
4071
     * @return false|mixed
4072
     *                     <p>Will return false if there are no values.</p>
4073
     */
4074 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...
4075
    {
4076 10
        if ($this->count() === 0) {
4077 1
            return false;
4078
        }
4079
4080 9
        $max = false;
4081
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4082 9
        foreach ($this->getGeneratorByReference() as &$value) {
4083
            if (
4084 9
                $max === false
4085
                ||
4086 9
                $value > $max
4087
            ) {
4088 9
                $max = $value;
4089
            }
4090
        }
4091
4092 9
        return $max;
4093
    }
4094
4095
    /**
4096
     * Merge the new $array into the current array.
4097
     *
4098
     * - keep key,value from the current array, also if the index is in the new $array
4099
     *
4100
     * EXAMPLE: <code>
4101
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4102
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4103
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[1 => 'one', 'foo' => 'bar2', 3 => 'three']
4104
     * // ---
4105
     * $array1 = [0 => 'one', 1 => 'foo'];
4106
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4107
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2']
4108
     * </code>
4109
     *
4110
     * @param array $array
4111
     * @param bool  $recursive
4112
     *
4113
     * @return static
4114
     *                <p>(Immutable)</p>
4115
     *
4116
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4117
     * @psalm-return static<int|TKey,T>
4118
     * @psalm-mutation-free
4119
     */
4120 33 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4121
    {
4122 33
        if ($recursive === true) {
4123 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
4124 9
            $result = \array_replace_recursive($this->toArray(), $array);
4125
        } else {
4126 24
            $result = \array_replace($this->toArray(), $array);
4127
        }
4128
4129 33
        return static::create(
4130 33
            $result,
4131 33
            $this->iteratorClass,
4132 33
            false
4133
        );
4134
    }
4135
4136
    /**
4137
     * Merge the new $array into the current array.
4138
     *
4139
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
4140
     * - create new indexes
4141
     *
4142
     * EXAMPLE: <code>
4143
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4144
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4145
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 'foo' => 'bar2', 1 => 'three']
4146
     * // ---
4147
     * $array1 = [0 => 'one', 1 => 'foo'];
4148
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4149
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 1 => 'foo', 2 => 'foo', 3 => 'bar2']
4150
     * </code>
4151
     *
4152
     * @param array $array
4153
     * @param bool  $recursive
4154
     *
4155
     * @return static
4156
     *                <p>(Immutable)</p>
4157
     *
4158
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4159
     * @psalm-return static<TKey,T>
4160
     * @psalm-mutation-free
4161
     */
4162 20 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4163
    {
4164 20
        if ($recursive === true) {
4165 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
4166 5
            $result = \array_merge_recursive($this->toArray(), $array);
4167
        } else {
4168 15
            $result = \array_merge($this->toArray(), $array);
4169
        }
4170
4171 20
        return static::create(
4172 20
            $result,
4173 20
            $this->iteratorClass,
4174 20
            false
4175
        );
4176
    }
4177
4178
    /**
4179
     * Merge the the current array into the $array.
4180
     *
4181
     * - use key,value from the new $array, also if the index is in the current array
4182
     *
4183
     * EXAMPLE: <code>
4184
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4185
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4186
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy['foo' => 'bar1', 3 => 'three', 1 => 'one']
4187
     * // ---
4188
     * $array1 = [0 => 'one', 1 => 'foo'];
4189
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4190
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy[0 => 'one', 1 => 'foo']
4191
     * </code>
4192
     *
4193
     * @param array $array
4194
     * @param bool  $recursive
4195
     *
4196
     * @return static
4197
     *                <p>(Immutable)</p>
4198
     *
4199
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4200
     * @psalm-return static<TKey,T>
4201
     * @psalm-mutation-free
4202
     */
4203 17 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4204
    {
4205 17
        if ($recursive === true) {
4206 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
4207 4
            $result = \array_replace_recursive($array, $this->toArray());
4208
        } else {
4209 13
            $result = \array_replace($array, $this->toArray());
4210
        }
4211
4212 17
        return static::create(
4213 17
            $result,
4214 17
            $this->iteratorClass,
4215 17
            false
4216
        );
4217
    }
4218
4219
    /**
4220
     * Merge the current array into the new $array.
4221
     *
4222
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
4223
     * - create new indexes
4224
     *
4225
     * EXAMPLE: <code>
4226
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4227
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4228
     * a($array1)->mergePrependNewIndex($array2); // Arrayy['foo' => 'bar1', 0 => 'three', 1 => 'one']
4229
     * // ---
4230
     * $array1 = [0 => 'one', 1 => 'foo'];
4231
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4232
     * a($array1)->mergePrependNewIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2', 2 => 'one', 3 => 'foo']
4233
     * </code>
4234
     *
4235
     * @param array $array
4236
     * @param bool  $recursive
4237
     *
4238
     * @return static
4239
     *                <p>(Immutable)</p>
4240
     *
4241
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4242
     * @psalm-return static<TKey,T>
4243
     * @psalm-mutation-free
4244
     */
4245 21 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4246
    {
4247 21
        if ($recursive === true) {
4248 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
4249 7
            $result = \array_merge_recursive($array, $this->toArray());
4250
        } else {
4251 14
            $result = \array_merge($array, $this->toArray());
4252
        }
4253
4254 21
        return static::create(
4255 21
            $result,
4256 21
            $this->iteratorClass,
4257 21
            false
4258
        );
4259
    }
4260
4261
    /**
4262
     * @return ArrayyMeta|static
4263
     */
4264 18
    public static function meta()
4265
    {
4266 18
        return (new ArrayyMeta())->getMetaObject(static::class);
4267
    }
4268
4269
    /**
4270
     * Get the min value from an array.
4271
     *
4272
     * EXAMPLE: <code>
4273
     * a([-9, -8, -7, 1.32])->min(); // -9
4274
     * </code>
4275
     *
4276
     * @return false|mixed
4277
     *                     <p>Will return false if there are no values.</p>
4278
     */
4279 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...
4280
    {
4281 10
        if ($this->count() === 0) {
4282 1
            return false;
4283
        }
4284
4285 9
        $min = false;
4286
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4287 9
        foreach ($this->getGeneratorByReference() as &$value) {
4288
            if (
4289 9
                $min === false
4290
                ||
4291 9
                $value < $min
4292
            ) {
4293 9
                $min = $value;
4294
            }
4295
        }
4296
4297 9
        return $min;
4298
    }
4299
4300
    /**
4301
     * Get the most used value from the array.
4302
     *
4303
     * @return mixed|null
4304
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4305
     * @psalm-mutation-free
4306
     */
4307 3
    public function mostUsedValue()
4308
    {
4309 3
        return $this->countValues()->arsortImmutable()->firstKey();
4310
    }
4311
4312
    /**
4313
     * Get the most used value from the array.
4314
     *
4315
     * @param int|null $number <p>How many values you will take?</p>
4316
     *
4317
     * @return static
4318
     *                <p>(Immutable)</p>
4319
     *
4320
     * @psalm-return static<TKey,T>
4321
     * @psalm-mutation-free
4322
     */
4323 3
    public function mostUsedValues(int $number = null): self
4324
    {
4325 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4326
    }
4327
4328
    /**
4329
     * Move an array element to a new index.
4330
     *
4331
     * EXAMPLE: <code>
4332
     * $arr2 = new A(['A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e']);
4333
     * $newArr2 = $arr2->moveElement('D', 1); // Arrayy['A' => 'a', 'D' => 'd', 'B' => 'b', 'C' => 'c', 'E' => 'e']
4334
     * </code>
4335
     *
4336
     * @param int|string $from
4337
     * @param int        $to
4338
     *
4339
     * @return static
4340
     *                <p>(Immutable)</p>
4341
     *
4342
     * @psalm-return static<TKey,T>
4343
     * @psalm-mutation-free
4344
     */
4345 1
    public function moveElement($from, $to): self
4346
    {
4347 1
        $array = $this->toArray();
4348
4349 1
        if ((int) $from === $from) {
4350 1
            $tmp = \array_splice($array, $from, 1);
4351 1
            \array_splice($array, (int) $to, 0, $tmp);
4352 1
            $output = $array;
4353 1
        } elseif ((string) $from === $from) {
4354 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4355 1
            $itemToMove = $array[$from];
4356 1
            if ($indexToMove !== false) {
4357 1
                \array_splice($array, $indexToMove, 1);
4358
            }
4359 1
            $i = 0;
4360 1
            $output = [];
4361 1
            foreach ($array as $key => $item) {
4362 1
                if ($i === $to) {
4363 1
                    $output[$from] = $itemToMove;
4364
                }
4365 1
                $output[$key] = $item;
4366 1
                ++$i;
4367
            }
4368
        } else {
4369
            $output = [];
4370
        }
4371
4372 1
        return static::create(
4373 1
            $output,
4374 1
            $this->iteratorClass,
4375 1
            false
4376
        );
4377
    }
4378
4379
    /**
4380
     * Move an array element to the first place.
4381
     *
4382
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4383
     *       loss the keys of an indexed array.
4384
     *
4385
     * @param int|string $key
4386
     *
4387
     * @return static
4388
     *                <p>(Immutable)</p>
4389
     *
4390
     * @psalm-return static<TKey,T>
4391
     * @psalm-mutation-free
4392
     */
4393 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...
4394
    {
4395 1
        $array = $this->toArray();
4396
4397 1
        if ($this->offsetExists($key)) {
4398 1
            $tmpValue = $this->get($key);
4399 1
            unset($array[$key]);
4400 1
            $array = [$key => $tmpValue] + $array;
4401
        }
4402
4403 1
        return static::create(
4404 1
            $array,
4405 1
            $this->iteratorClass,
4406 1
            false
4407
        );
4408
    }
4409
4410
    /**
4411
     * Move an array element to the last place.
4412
     *
4413
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4414
     *       loss the keys of an indexed array.
4415
     *
4416
     * @param int|string $key
4417
     *
4418
     * @return static
4419
     *                <p>(Immutable)</p>
4420
     *
4421
     * @psalm-return static<TKey,T>
4422
     * @psalm-mutation-free
4423
     */
4424 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...
4425
    {
4426 1
        $array = $this->toArray();
4427
4428 1
        if ($this->offsetExists($key)) {
4429 1
            $tmpValue = $this->get($key);
4430 1
            unset($array[$key]);
4431 1
            $array += [$key => $tmpValue];
4432
        }
4433
4434 1
        return static::create(
4435 1
            $array,
4436 1
            $this->iteratorClass,
4437 1
            false
4438
        );
4439
    }
4440
4441
    /**
4442
     * Moves the internal iterator position to the next element and returns this element.
4443
     *
4444
     * @return false|mixed
4445
     *                     <p>(Mutable) Will return false if there are no values.</p>
4446
     */
4447
    public function next()
4448
    {
4449
        return \next($this->array);
4450
    }
4451
4452
    /**
4453
     * Get the next nth keys and values from the array.
4454
     *
4455
     * @param int $step
4456
     * @param int $offset
4457
     *
4458
     * @return static
4459
     *                <p>(Immutable)</p>
4460
     *
4461
     * @psalm-return static<TKey,T>
4462
     * @psalm-mutation-free
4463
     */
4464 1
    public function nth(int $step, int $offset = 0): self
4465
    {
4466
        $arrayFunction = function () use ($step, $offset): \Generator {
4467 1
            $position = 0;
4468 1
            foreach ($this->getGenerator() as $key => $value) {
4469 1
                if ($position++ % $step !== $offset) {
4470 1
                    continue;
4471
                }
4472
4473 1
                yield $key => $value;
4474
            }
4475 1
        };
4476
4477 1
        return static::create(
4478 1
            $arrayFunction,
4479 1
            $this->iteratorClass,
4480 1
            false
4481
        );
4482
    }
4483
4484
    /**
4485
     * Get a subset of the items from the given array.
4486
     *
4487
     * @param int[]|string[] $keys
4488
     *
4489
     * @return static
4490
     *                <p>(Immutable)</p>
4491
     *
4492
     * @psalm-param array-key[] $keys
4493
     * @psalm-return static<TKey,T>
4494
     * @psalm-mutation-free
4495
     */
4496 1 View Code Duplication
    public function only(array $keys): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4497
    {
4498 1
        $keys = \array_flip($keys);
4499
4500
        $generator = function () use ($keys): \Generator {
4501 1
            foreach ($this->getGenerator() as $key => $value) {
4502 1
                if (isset($keys[$key])) {
4503 1
                    yield $key => $value;
4504
                }
4505
            }
4506 1
        };
4507
4508 1
        return static::create(
4509 1
            $generator,
4510 1
            $this->iteratorClass,
4511 1
            false
4512
        );
4513
    }
4514
4515
    /**
4516
     * Pad array to the specified size with a given value.
4517
     *
4518
     * @param int   $size  <p>Size of the result array.</p>
4519
     * @param mixed $value <p>Empty value by default.</p>
4520
     *
4521
     * @return static
4522
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4523
     *
4524
     * @psalm-return static<TKey,T>
4525
     * @psalm-mutation-free
4526
     */
4527 5
    public function pad(int $size, $value): self
4528
    {
4529 5
        return static::create(
4530 5
            \array_pad($this->toArray(), $size, $value),
4531 5
            $this->iteratorClass,
4532 5
            false
4533
        );
4534
    }
4535
4536
    /**
4537
     * Partitions this array in two array according to a predicate.
4538
     * Keys are preserved in the resulting array.
4539
     *
4540
     * @param \Closure $closure
4541
     *                          <p>The predicate on which to partition.</p>
4542
     *
4543
     * @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...
4544
     *                    <p>An array with two elements. The first element contains the array
4545
     *                    of elements where the predicate returned TRUE, the second element
4546
     *                    contains the array of elements where the predicate returned FALSE.</p>
4547
     *
4548
     * @psalm-return array<int, static<TKey,T>>
4549
     */
4550 1
    public function partition(\Closure $closure): array
4551
    {
4552
        // init
4553 1
        $matches = [];
4554 1
        $noMatches = [];
4555
4556 1
        foreach ($this->getGenerator() as $key => $value) {
4557 1
            if ($closure($value, $key)) {
4558 1
                $matches[$key] = $value;
4559
            } else {
4560 1
                $noMatches[$key] = $value;
4561
            }
4562
        }
4563
4564 1
        return [self::create($matches), self::create($noMatches)];
4565
    }
4566
4567
    /**
4568
     * Pop a specified value off the end of the current array.
4569
     *
4570
     * @return mixed|null
4571
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4572
     */
4573 5
    public function pop()
4574
    {
4575 5
        $this->generatorToArray();
4576
4577 5
        return \array_pop($this->array);
4578
    }
4579
4580
    /**
4581
     * Prepend a (key) + value to the current array.
4582
     *
4583
     * EXAMPLE: <code>
4584
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4585
     * </code>
4586
     *
4587
     * @param mixed $value
4588
     * @param mixed $key
4589
     *
4590
     * @return $this
4591
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4592
     *
4593
     * @psalm-return static<TKey,T>
4594
     */
4595 11
    public function prepend($value, $key = null)
4596
    {
4597 11
        $this->generatorToArray();
4598
4599 11
        if ($this->properties !== []) {
4600 3
            $this->checkType($key, $value);
4601
        }
4602
4603 9
        if ($key === null) {
4604 8
            \array_unshift($this->array, $value);
4605
        } else {
4606 2
            $this->array = [$key => $value] + $this->array;
4607
        }
4608
4609 9
        return $this;
4610
    }
4611
4612
    /**
4613
     * Prepend a (key) + value to the current array.
4614
     *
4615
     * EXAMPLE: <code>
4616
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4617
     * </code>
4618
     *
4619
     * @param mixed $value
4620
     * @param mixed $key
4621
     *
4622
     * @return $this
4623
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4624
     *
4625
     * @psalm-return static<TKey,T>
4626
     * @psalm-mutation-free
4627
     */
4628 1 View Code Duplication
    public function prependImmutable($value, $key = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4629
    {
4630
        $generator = function () use ($key, $value): \Generator {
4631 1
            if ($this->properties !== []) {
4632
                $this->checkType($key, $value);
4633
            }
4634
4635 1
            if ($key !== null) {
4636
                yield $key => $value;
4637
            } else {
4638 1
                yield $value;
4639
            }
4640
4641
            /** @noinspection YieldFromCanBeUsedInspection - FP */
4642 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
4643 1
                yield $keyOld => $itemOld;
4644
            }
4645 1
        };
4646
4647 1
        return static::create(
4648 1
            $generator,
4649 1
            $this->iteratorClass,
4650 1
            false
4651
        );
4652
    }
4653
4654
    /**
4655
     * Add a suffix to each key.
4656
     *
4657
     * @param mixed $suffix
4658
     *
4659
     * @return static
4660
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4661
     *
4662
     * @psalm-return static<TKey,T>
4663
     * @psalm-mutation-free
4664
     */
4665 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...
4666
    {
4667
        // init
4668 10
        $result = [];
4669
4670 10
        foreach ($this->getGenerator() as $key => $item) {
4671 9
            if ($item instanceof self) {
4672
                $result[$key] = $item->prependToEachKey($suffix);
4673 9
            } elseif (\is_array($item)) {
4674
                $result[$key] = self::create(
4675
                    $item,
4676
                    $this->iteratorClass,
4677
                    false
4678
                )->prependToEachKey($suffix)
4679
                    ->toArray();
4680
            } else {
4681 9
                $result[$key . $suffix] = $item;
4682
            }
4683
        }
4684
4685 10
        return self::create(
4686 10
            $result,
4687 10
            $this->iteratorClass,
4688 10
            false
4689
        );
4690
    }
4691
4692
    /**
4693
     * Add a suffix to each value.
4694
     *
4695
     * @param mixed $suffix
4696
     *
4697
     * @return static
4698
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4699
     *
4700
     * @psalm-return static<TKey,T>
4701
     * @psalm-mutation-free
4702
     */
4703 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...
4704
    {
4705
        // init
4706 10
        $result = [];
4707
4708 10
        foreach ($this->getGenerator() as $key => $item) {
4709 9
            if ($item instanceof self) {
4710
                $result[$key] = $item->prependToEachValue($suffix);
4711 9
            } elseif (\is_array($item)) {
4712
                $result[$key] = self::create(
4713
                    $item,
4714
                    $this->iteratorClass,
4715
                    false
4716
                )->prependToEachValue($suffix)
4717
                    ->toArray();
4718 9
            } elseif (\is_object($item) === true) {
4719 1
                $result[$key] = $item;
4720
            } else {
4721 9
                $result[$key] = $item . $suffix;
4722
            }
4723
        }
4724
4725 10
        return self::create(
4726 10
            $result,
4727 10
            $this->iteratorClass,
4728 10
            false
4729
        );
4730
    }
4731
4732
    /**
4733
     * Return the value of a given key and
4734
     * delete the key.
4735
     *
4736
     * @param int|int[]|string|string[]|null $keyOrKeys
4737
     * @param mixed                          $fallback
4738
     *
4739
     * @return mixed
4740
     */
4741 5
    public function pull($keyOrKeys = null, $fallback = null)
4742
    {
4743 5
        if ($keyOrKeys === null) {
4744 1
            $array = $this->toArray();
4745 1
            $this->clear();
4746
4747 1
            return $array;
4748
        }
4749
4750 4
        if (\is_array($keyOrKeys)) {
4751 1
            $valueOrValues = [];
4752 1
            foreach ($keyOrKeys as $key) {
4753 1
                $valueOrValues[] = $this->get($key, $fallback);
4754 1
                $this->offsetUnset($key);
4755
            }
4756
        } else {
4757 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4758 4
            $this->offsetUnset($keyOrKeys);
4759
        }
4760
4761 4
        return $valueOrValues;
4762
    }
4763
4764
    /**
4765
     * Push one or more values onto the end of array at once.
4766
     *
4767
     * @param array ...$args
4768
     *
4769
     * @return $this
4770
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4771
     *
4772
     * @noinspection ReturnTypeCanBeDeclaredInspection
4773
     *
4774
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4775
     * @psalm-return static<TKey,T>
4776
     */
4777 7
    public function push(...$args)
4778
    {
4779 7
        $this->generatorToArray();
4780
4781
        if (
4782 7
            $this->checkPropertyTypes
4783
            &&
4784 7
            $this->properties !== []
4785
        ) {
4786 1
            foreach ($args as $key => $value) {
4787 1
                $this->checkType($key, $value);
4788
            }
4789
        }
4790
4791 7
        \array_push($this->array, ...$args);
4792
4793 7
        return $this;
4794
    }
4795
4796
    /**
4797
     * Get a random value from the current array.
4798
     *
4799
     * EXAMPLE: <code>
4800
     * a([1, 2, 3, 4])->randomImmutable(2); // e.g.: Arrayy[1, 4]
4801
     * </code>
4802
     *
4803
     * @param int|null $number <p>How many values you will take?</p>
4804
     *
4805
     * @return static
4806
     *                <p>(Immutable)</p>
4807
     *
4808
     * @psalm-return static<int|array-key,T>
4809
     */
4810 19
    public function randomImmutable(int $number = null): self
4811
    {
4812 19
        $this->generatorToArray();
4813
4814 19
        if ($this->count() === 0) {
4815 1
            return static::create(
4816 1
                [],
4817 1
                $this->iteratorClass,
4818 1
                false
4819
            );
4820
        }
4821
4822 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...
4823
            /** @noinspection NonSecureArrayRandUsageInspection */
4824 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4825
4826 13
            return static::create(
4827 13
                $arrayRandValue,
4828 13
                $this->iteratorClass,
4829 13
                false
4830
            );
4831
        }
4832
4833 6
        $arrayTmp = $this->array;
4834
        /** @noinspection NonSecureShuffleUsageInspection */
4835 6
        \shuffle($arrayTmp);
4836
4837 6
        return static::create(
4838 6
            $arrayTmp,
4839 6
            $this->iteratorClass,
4840 6
            false
4841 6
        )->firstsImmutable($number);
4842
    }
4843
4844
    /**
4845
     * Pick a random key/index from the keys of this array.
4846
     *
4847
     * EXAMPLE: <code>
4848
     * $arrayy = A::create([1 => 'one', 2 => 'two']);
4849
     * $arrayy->randomKey(); // e.g. 2
4850
     * </code>
4851
     *
4852
     * @throws \RangeException If array is empty
4853
     *
4854
     * @return mixed
4855
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4856
     */
4857 4
    public function randomKey()
4858
    {
4859 4
        $result = $this->randomKeys(1);
4860
4861 4
        if (!isset($result[0])) {
4862
            $result[0] = null;
4863
        }
4864
4865 4
        return $result[0];
4866
    }
4867
4868
    /**
4869
     * Pick a given number of random keys/indexes out of this array.
4870
     *
4871
     * EXAMPLE: <code>
4872
     * a([1 => 'one', 2 => 'two'])->randomKeys(); // e.g. Arrayy[1, 2]
4873
     * </code>
4874
     *
4875
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4876
     *
4877
     * @throws \RangeException If array is empty
4878
     *
4879
     * @return static
4880
     *                <p>(Immutable)</p>
4881
     *
4882
     * @psalm-return static<TKey,T>
4883
     */
4884 13
    public function randomKeys(int $number): self
4885
    {
4886 13
        $this->generatorToArray();
4887
4888 13
        $count = $this->count();
4889
4890
        if (
4891 13
            $number === 0
4892
            ||
4893 13
            $number > $count
4894
        ) {
4895 2
            throw new \RangeException(
4896 2
                \sprintf(
4897 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4898 2
                    $number,
4899 2
                    $count
4900
                )
4901
            );
4902
        }
4903
4904 11
        $result = (array) \array_rand($this->array, $number);
4905
4906 11
        return static::create(
4907 11
            $result,
4908 11
            $this->iteratorClass,
4909 11
            false
4910
        );
4911
    }
4912
4913
    /**
4914
     * Get a random value from the current array.
4915
     *
4916
     * EXAMPLE: <code>
4917
     * a([1, 2, 3, 4])->randomMutable(2); // e.g.: Arrayy[1, 4]
4918
     * </code>
4919
     *
4920
     * @param int|null $number <p>How many values you will take?</p>
4921
     *
4922
     * @return $this
4923
     *               <p>(Mutable) Return this Arrayy object.</p>
4924
     *
4925
     * @psalm-return static<TKey,T>
4926
     */
4927 17
    public function randomMutable(int $number = null): self
4928
    {
4929 17
        $this->generatorToArray();
4930
4931 17
        if ($this->count() === 0) {
4932
            return static::create(
4933
                [],
4934
                $this->iteratorClass,
4935
                false
4936
            );
4937
        }
4938
4939 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...
4940
            /** @noinspection NonSecureArrayRandUsageInspection */
4941 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4942 7
            $this->array = $arrayRandValue;
4943
4944 7
            return $this;
4945
        }
4946
4947
        /** @noinspection NonSecureShuffleUsageInspection */
4948 11
        \shuffle($this->array);
4949
4950 11
        return $this->firstsMutable($number);
4951
    }
4952
4953
    /**
4954
     * Pick a random value from the values of this array.
4955
     *
4956
     * EXAMPLE: <code>
4957
     * a([1 => 'one', 2 => 'two'])->randomValue(); // e.g. 'one'
4958
     * </code>
4959
     *
4960
     * @return mixed
4961
     *               <p>Get a random value or null if there wasn't a value.</p>
4962
     */
4963 4
    public function randomValue()
4964
    {
4965 4
        $result = $this->randomImmutable();
4966
4967 4
        if (!isset($result[0])) {
4968
            $result[0] = null;
4969
        }
4970
4971 4
        return $result[0];
4972
    }
4973
4974
    /**
4975
     * Pick a given number of random values out of this array.
4976
     *
4977
     * EXAMPLE: <code>
4978
     * a([1 => 'one', 2 => 'two'])->randomValues(); // e.g. Arrayy['one', 'two']
4979
     * </code>
4980
     *
4981
     * @param int $number
4982
     *
4983
     * @return static
4984
     *                <p>(Mutable)</p>
4985
     *
4986
     * @psalm-return static<TKey,T>
4987
     */
4988 7
    public function randomValues(int $number): self
4989
    {
4990 7
        return $this->randomMutable($number);
4991
    }
4992
4993
    /**
4994
     * Get a random value from an array, with the ability to skew the results.
4995
     *
4996
     * EXAMPLE: <code>
4997
     * a([0 => 3, 1 => 4])->randomWeighted([1 => 4]); // e.g.: Arrayy[4] (has a 66% chance of returning 4)
4998
     * </code>
4999
     *
5000
     * @param array    $array
5001
     * @param int|null $number <p>How many values you will take?</p>
5002
     *
5003
     * @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...
5004
     *                           <p>(Immutable)</p>
5005
     *
5006
     * @psalm-param  array<mixed,mixed> $array
5007
     * @psalm-return static<int|array-key,T>
5008
     */
5009 9
    public function randomWeighted(array $array, int $number = null): self
5010
    {
5011
        // init
5012 9
        $options = [];
5013
5014 9
        foreach ($array as $option => $weight) {
5015 9
            if ($this->searchIndex($option) !== false) {
5016 9
                for ($i = 0; $i < $weight; ++$i) {
5017 1
                    $options[] = $option;
5018
                }
5019
            }
5020
        }
5021
5022 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
5023
    }
5024
5025
    /**
5026
     * Reduce the current array via callable e.g. anonymous-function.
5027
     *
5028
     * EXAMPLE: <code>
5029
     * function myReducer($resultArray, $value) {
5030
     *     if ($value == 'foo') {
5031
     *         $resultArray[] = $value;
5032
     *     }
5033
     *     return $resultArray;
5034
     * };
5035
     * a(['foo', 'bar'])->reduce('myReducer'); // Arrayy['foo']
5036
     * </cdde>
5037
     *
5038
     * @param callable $callable
5039
     * @param mixed    $init
5040
     *
5041
     * @return static
5042
     *                <p>(Immutable)</p>
5043
     *
5044
     * @psalm-return static<TKey,T>
5045
     * @psalm-mutation-free
5046
     */
5047 18
    public function reduce($callable, $init = []): self
5048
    {
5049 18
        if ($this->generator) {
5050 1
            $result = $init;
5051
5052 1
            foreach ($this->getGenerator() as $value) {
5053 1
                $result = $callable($result, $value);
5054
            }
5055
5056 1
            return static::create(
5057 1
                $result,
5058 1
                $this->iteratorClass,
5059 1
                false
5060
            );
5061
        }
5062
5063 18
        $result = \array_reduce($this->array, $callable, $init);
5064
5065 18
        if ($result === null) {
5066
            $this->array = [];
5067
        } else {
5068 18
            $this->array = (array) $result;
5069
        }
5070
5071 18
        return static::create(
5072 18
            $this->array,
5073 18
            $this->iteratorClass,
5074 18
            false
5075
        );
5076
    }
5077
5078
    /**
5079
     * @param bool $unique
5080
     *
5081
     * @return static
5082
     *                <p>(Immutable)</p>
5083
     *
5084
     * @psalm-return static<TKey,T>
5085
     * @psalm-mutation-free
5086
     */
5087 14
    public function reduce_dimension(bool $unique = true): self
5088
    {
5089
        // init
5090 14
        $result = [];
5091
5092 14
        foreach ($this->getGenerator() as $val) {
5093 12
            if (\is_array($val)) {
5094 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
5095
            } else {
5096 12
                $result[] = [$val];
5097
            }
5098
        }
5099
5100 14
        $result = $result === [] ? [] : \array_merge(...$result);
5101
5102 14
        $resultArrayy = new static($result);
5103
5104
        /**
5105
         * @psalm-suppress ImpureMethodCall - object is already re-created
5106
         * @psalm-suppress InvalidReturnStatement - why?
5107
         */
5108 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
5109
    }
5110
5111
    /**
5112
     * Create a numerically re-indexed Arrayy object.
5113
     *
5114
     * EXAMPLE: <code>
5115
     * a([2 => 1, 3 => 2])->reindex(); // Arrayy[0 => 1, 1 => 2]
5116
     * </code>
5117
     *
5118
     * @return $this
5119
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
5120
     *
5121
     * @psalm-return static<TKey,T>
5122
     */
5123 9
    public function reindex(): self
5124
    {
5125 9
        $this->generatorToArray(false);
5126
5127 9
        $this->array = \array_values($this->array);
5128
5129 9
        return $this;
5130
    }
5131
5132
    /**
5133
     * Return all items that fail the truth test.
5134
     *
5135
     * EXAMPLE: <code>
5136
     * $closure = function ($value) {
5137
     *     return $value % 2 !== 0;
5138
     * }
5139
     * a([1, 2, 3, 4])->reject($closure); // Arrayy[1 => 2, 3 => 4]
5140
     * </code>
5141
     *
5142
     * @param \Closure $closure
5143
     *
5144
     * @return static
5145
     *                <p>(Immutable)</p>
5146
     *
5147
     * @psalm-return static<TKey,T>
5148
     * @psalm-mutation-free
5149
     */
5150 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...
5151
    {
5152
        // init
5153 1
        $filtered = [];
5154
5155 1
        foreach ($this->getGenerator() as $key => $value) {
5156 1
            if (!$closure($value, $key)) {
5157 1
                $filtered[$key] = $value;
5158
            }
5159
        }
5160
5161 1
        return static::create(
5162 1
            $filtered,
5163 1
            $this->iteratorClass,
5164 1
            false
5165
        );
5166
    }
5167
5168
    /**
5169
     * Remove a value from the current array (optional using dot-notation).
5170
     *
5171
     * EXAMPLE: <code>
5172
     * a([1 => 'bar', 'foo' => 'foo'])->remove(1); // Arrayy['foo' => 'foo']
5173
     * </code>
5174
     *
5175
     * @param mixed $key
5176
     *
5177
     * @return static
5178
     *                <p>(Mutable)</p>
5179
     *
5180
     * @psalm-param  TKey $key
5181
     * @psalm-return static<TKey,T>
5182
     */
5183 22
    public function remove($key)
5184
    {
5185
        // recursive call
5186 22
        if (\is_array($key)) {
5187 1
            foreach ($key as $k) {
5188 1
                $this->internalRemove($k);
5189
            }
5190
5191 1
            return static::create(
5192 1
                $this->toArray(),
5193 1
                $this->iteratorClass,
5194 1
                false
5195
            );
5196
        }
5197
5198 21
        $this->internalRemove($key);
5199
5200 21
        return static::create(
5201 21
            $this->toArray(),
5202 21
            $this->iteratorClass,
5203 21
            false
5204
        );
5205
    }
5206
5207
    /**
5208
     * alias: for "Arrayy->removeValue()"
5209
     *
5210
     * @param mixed $element
5211
     *
5212
     * @return static
5213
     *                <p>(Immutable)</p>
5214
     *
5215
     * @psalm-param  T $element
5216
     * @psalm-return static<TKey,T>
5217
     * @psalm-mutation-free
5218
     */
5219 8
    public function removeElement($element)
5220
    {
5221 8
        return $this->removeValue($element);
5222
    }
5223
5224
    /**
5225
     * Remove the first value from the current array.
5226
     *
5227
     * EXAMPLE: <code>
5228
     * a([1 => 'bar', 'foo' => 'foo'])->removeFirst(); // Arrayy['foo' => 'foo']
5229
     * </code>
5230
     *
5231
     * @return static
5232
     *                <p>(Immutable)</p>
5233
     *
5234
     * @psalm-return static<TKey,T>
5235
     * @psalm-mutation-free
5236
     */
5237 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...
5238
    {
5239 7
        $tmpArray = $this->toArray();
5240
5241 7
        \array_shift($tmpArray);
5242
5243 7
        return static::create(
5244 7
            $tmpArray,
5245 7
            $this->iteratorClass,
5246 7
            false
5247
        );
5248
    }
5249
5250
    /**
5251
     * Remove the last value from the current array.
5252
     *
5253
     * EXAMPLE: <code>
5254
     * a([1 => 'bar', 'foo' => 'foo'])->removeLast(); // Arrayy[1 => 'bar']
5255
     * </code>
5256
     *
5257
     * @return static
5258
     *                <p>(Immutable)</p>
5259
     *
5260
     * @psalm-return static<TKey,T>
5261
     * @psalm-mutation-free
5262
     */
5263 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...
5264
    {
5265 7
        $tmpArray = $this->toArray();
5266
5267 7
        \array_pop($tmpArray);
5268
5269 7
        return static::create(
5270 7
            $tmpArray,
5271 7
            $this->iteratorClass,
5272 7
            false
5273
        );
5274
    }
5275
5276
    /**
5277
     * Removes a particular value from an array (numeric or associative).
5278
     *
5279
     * EXAMPLE: <code>
5280
     * a([1 => 'bar', 'foo' => 'foo'])->removeValue('foo'); // Arrayy[1 => 'bar']
5281
     * </code>
5282
     *
5283
     * @param mixed $value
5284
     *
5285
     * @return static
5286
     *                <p>(Immutable)</p>
5287
     *
5288
     * @psalm-param  T $value
5289
     * @psalm-return static<TKey,T>
5290
     * @psalm-mutation-free
5291
     */
5292 8
    public function removeValue($value): self
5293
    {
5294 8
        $this->generatorToArray();
5295
5296
        // init
5297 8
        $isSequentialArray = $this->isSequential();
5298
5299 8
        foreach ($this->array as $key => $item) {
5300 7
            if ($item === $value) {
5301 7
                unset($this->array[$key]);
5302
            }
5303
        }
5304
5305 8
        if ($isSequentialArray) {
5306 6
            $this->array = \array_values($this->array);
5307
        }
5308
5309 8
        return static::create(
5310 8
            $this->array,
5311 8
            $this->iteratorClass,
5312 8
            false
5313
        );
5314
    }
5315
5316
    /**
5317
     * Generate array of repeated arrays.
5318
     *
5319
     * @param int $times <p>How many times has to be repeated.</p>
5320
     *
5321
     * @return static
5322
     *                <p>(Immutable)</p>
5323
     *
5324
     * @psalm-return static<TKey,T>
5325
     * @psalm-mutation-free
5326
     */
5327 1
    public function repeat($times): self
5328
    {
5329 1
        if ($times === 0) {
5330 1
            return static::create([], $this->iteratorClass);
5331
        }
5332
5333 1
        return static::create(
5334 1
            \array_fill(0, (int) $times, $this->toArray()),
5335 1
            $this->iteratorClass,
5336 1
            false
5337
        );
5338
    }
5339
5340
    /**
5341
     * Replace a key with a new key/value pair.
5342
     *
5343
     * EXAMPLE: <code>
5344
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
5345
     * $arrayy->replace(2, 'notfoo', 'notbar'); // Arrayy[1 => 'foo', 'notfoo' => 'notbar', 3 => 'bar']
5346
     * </code>
5347
     *
5348
     * @param mixed $oldKey
5349
     * @param mixed $newKey
5350
     * @param mixed $newValue
5351
     *
5352
     * @return static
5353
     *                <p>(Immutable)</p>
5354
     *
5355
     * @psalm-return static<TKey,T>
5356
     * @psalm-mutation-free
5357
     */
5358 5
    public function replace($oldKey, $newKey, $newValue): self
5359
    {
5360 5
        $that = clone $this;
5361
5362
        /**
5363
         * @psalm-suppress ImpureMethodCall - object is already cloned
5364
         */
5365 5
        return $that->remove($oldKey)
5366 5
            ->set($newKey, $newValue);
5367
    }
5368
5369
    /**
5370
     * Create an array using the current array as values and the other array as keys.
5371
     *
5372
     * EXAMPLE: <code>
5373
     * $firstArray = [
5374
     *     1 => 'one',
5375
     *     2 => 'two',
5376
     *     3 => 'three',
5377
     * ];
5378
     * $secondArray = [
5379
     *     'one' => 1,
5380
     *     1     => 'one',
5381
     *     2     => 2,
5382
     * ];
5383
     * $arrayy = a($firstArray);
5384
     * $arrayy->replaceAllKeys($secondArray); // Arrayy[1 => "one", 'one' => "two", 2 => "three"]
5385
     * </code>
5386
     *
5387
     * @param array $keys <p>An array of keys.</p>
5388
     *
5389
     * @return static
5390
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
5391
     *
5392
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5393
     * @psalm-return static<TKey,T>
5394
     * @psalm-mutation-free
5395
     */
5396 2
    public function replaceAllKeys(array $keys): self
5397
    {
5398 2
        return static::create(
5399 2
            \array_combine($keys, $this->toArray()),
5400 2
            $this->iteratorClass,
5401 2
            false
5402
        );
5403
    }
5404
5405
    /**
5406
     * Create an array using the current array as keys and the other array as values.
5407
     *
5408
     * EXAMPLE: <code>
5409
     * $firstArray = [
5410
     *     1 => 'one',
5411
     *     2 => 'two',
5412
     *     3 => 'three',
5413
     * ];
5414
     * $secondArray = [
5415
     *     'one' => 1,
5416
     *     1     => 'one',
5417
     *     2     => 2,
5418
     * ];
5419
     * $arrayy = a($firstArray);
5420
     * $arrayy->replaceAllValues($secondArray); // Arrayy['one' => 1, 'two' => 'one', 'three' => 2]
5421
     * </code>
5422
     *
5423
     * @param array $array <p>An array of values.</p>
5424
     *
5425
     * @return static
5426
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
5427
     *
5428
     * @psalm-param  array<mixed,T> $array
5429
     * @psalm-return static<TKey,T>
5430
     * @psalm-mutation-free
5431
     */
5432 2
    public function replaceAllValues(array $array): self
5433
    {
5434 2
        return static::create(
5435 2
            \array_combine($this->array, $array),
5436 2
            $this->iteratorClass,
5437 2
            false
5438
        );
5439
    }
5440
5441
    /**
5442
     * Replace the keys in an array with another set.
5443
     *
5444
     * EXAMPLE: <code>
5445
     * a([1 => 'bar', 'foo' => 'foo'])->replaceKeys([1 => 2, 'foo' => 'replaced']); // Arrayy[2 => 'bar', 'replaced' => 'foo']
5446
     * </code>
5447
     *
5448
     * @param array $keys <p>An array of keys matching the array's size</p>
5449
     *
5450
     * @return static
5451
     *                <p>(Immutable)</p>
5452
     *
5453
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5454
     * @psalm-return static<TKey,T>
5455
     * @psalm-mutation-free
5456
     */
5457 1
    public function replaceKeys(array $keys): self
5458
    {
5459 1
        $values = \array_values($this->toArray());
5460 1
        $result = \array_combine($keys, $values);
5461
5462 1
        return static::create(
5463 1
            $result,
5464 1
            $this->iteratorClass,
5465 1
            false
5466
        );
5467
    }
5468
5469
    /**
5470
     * Replace the first matched value in an array.
5471
     *
5472
     * EXAMPLE: <code>
5473
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5474
     * a($testArray)->replaceOneValue('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'foobar']
5475
     * </code>
5476
     *
5477
     * @param mixed $search      <p>The value to replace.</p>
5478
     * @param mixed $replacement <p>The value to replace.</p>
5479
     *
5480
     * @return static
5481
     *                <p>(Immutable)</p>
5482
     *
5483
     * @psalm-return static<TKey,T>
5484
     * @psalm-mutation-free
5485
     */
5486 3 View Code Duplication
    public function replaceOneValue($search, $replacement = ''): 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...
5487
    {
5488 3
        $array = $this->toArray();
5489 3
        $key = \array_search($search, $array, true);
5490
5491 3
        if ($key !== false) {
5492 3
            $array[$key] = $replacement;
5493
        }
5494
5495 3
        return static::create(
5496 3
            $array,
5497 3
            $this->iteratorClass,
5498 3
            false
5499
        );
5500
    }
5501
5502
    /**
5503
     * Replace values in the current array.
5504
     *
5505
     * EXAMPLE: <code>
5506
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5507
     * a($testArray)->replaceValues('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'replacedbar']
5508
     * </code>
5509
     *
5510
     * @param mixed $search      <p>The value to replace.</p>
5511
     * @param mixed $replacement <p>What to replace it with.</p>
5512
     *
5513
     * @return static
5514
     *                <p>(Immutable)</p>
5515
     *
5516
     * @psalm-return static<TKey,T>
5517
     * @psalm-mutation-free
5518
     */
5519 1
    public function replaceValues($search, $replacement = ''): self
5520
    {
5521
        /**
5522
         * @psalm-suppress MissingClosureReturnType
5523
         * @psalm-suppress MissingClosureParamType
5524
         */
5525 1
        return $this->each(
5526
            static function ($value) use ($search, $replacement) {
5527 1
                return \str_replace($search, $replacement, $value);
5528 1
            }
5529
        );
5530
    }
5531
5532
    /**
5533
     * Get the last elements from index $from until the end of this array.
5534
     *
5535
     * EXAMPLE: <code>
5536
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->rest(2); // Arrayy[0 => 'lall']
5537
     * </code>
5538
     *
5539
     * @param int $from
5540
     *
5541
     * @return static
5542
     *                <p>(Immutable)</p>
5543
     *
5544
     * @psalm-return static<TKey,T>
5545
     * @psalm-mutation-free
5546
     */
5547 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...
5548
    {
5549 15
        $tmpArray = $this->toArray();
5550
5551 15
        return static::create(
5552 15
            \array_splice($tmpArray, $from),
5553 15
            $this->iteratorClass,
5554 15
            false
5555
        );
5556
    }
5557
5558
    /**
5559
     * Return the array in the reverse order.
5560
     *
5561
     * EXAMPLE: <code>
5562
     * a([1, 2, 3])->reverse(); // self[3, 2, 1]
5563
     * </code>
5564
     *
5565
     * @return $this
5566
     *               <p>(Mutable) Return this Arrayy object.</p>
5567
     *
5568
     * @psalm-return static<TKey,T>
5569
     */
5570 9
    public function reverse(): self
5571
    {
5572 9
        $this->generatorToArray();
5573
5574 9
        $this->array = \array_reverse($this->array);
5575
5576 9
        return $this;
5577
    }
5578
5579
    /**
5580
     * Sort an array in reverse order.
5581
     *
5582
     * @param int $sort_flags [optional] <p>
5583
     *                        You may modify the behavior of the sort using the optional
5584
     *                        parameter sort_flags, for details
5585
     *                        see sort.
5586
     *                        </p>
5587
     *
5588
     * @return $this
5589
     *               <p>(Mutable) Return this Arrayy object.</p>
5590
     *
5591
     * @psalm-return static<TKey,T>
5592
     */
5593 4
    public function rsort(int $sort_flags = 0): self
5594
    {
5595 4
        $this->generatorToArray();
5596
5597 4
        \rsort($this->array, $sort_flags);
5598
5599 4
        return $this;
5600
    }
5601
5602
    /**
5603
     * Sort an array in reverse order.
5604
     *
5605
     * @param int $sort_flags [optional] <p>
5606
     *                        You may modify the behavior of the sort using the optional
5607
     *                        parameter sort_flags, for details
5608
     *                        see sort.
5609
     *                        </p>
5610
     *
5611
     * @return $this
5612
     *               <p>(Immutable) Return this Arrayy object.</p>
5613
     *
5614
     * @psalm-return static<TKey,T>
5615
     * @psalm-mutation-free
5616
     */
5617 4
    public function rsortImmutable(int $sort_flags = 0): self
5618
    {
5619 4
        $that = clone $this;
5620
5621
        /**
5622
         * @psalm-suppress ImpureMethodCall - object is already cloned
5623
         */
5624 4
        $that->rsort($sort_flags);
5625
5626 4
        return $that;
5627
    }
5628
5629
    /**
5630
     * Search for the first index of the current array via $value.
5631
     *
5632
     * EXAMPLE: <code>
5633
     * a(['fòô' => 'bàř', 'lall' => 'bàř'])->searchIndex('bàř'); // Arrayy[0 => 'fòô']
5634
     * </code>
5635
     *
5636
     * @param mixed $value
5637
     *
5638
     * @return false|float|int|string
5639
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5640
     * @psalm-mutation-free
5641
     */
5642 21
    public function searchIndex($value)
5643
    {
5644 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5645 20
            if ($value === $valueFromArray) {
5646 20
                return $keyFromArray;
5647
            }
5648
        }
5649
5650 11
        return false;
5651
    }
5652
5653
    /**
5654
     * Search for the value of the current array via $index.
5655
     *
5656
     * EXAMPLE: <code>
5657
     * a(['fòô' => 'bàř'])->searchValue('fòô'); // Arrayy[0 => 'bàř']
5658
     * </code>
5659
     *
5660
     * @param mixed $index
5661
     *
5662
     * @return static
5663
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5664
     *
5665
     * @psalm-return static<TKey,T>
5666
     * @psalm-mutation-free
5667
     */
5668 9
    public function searchValue($index): self
5669
    {
5670 9
        $this->generatorToArray();
5671
5672
        // init
5673 9
        $return = [];
5674
5675 9
        if ($this->array === []) {
5676
            return static::create(
5677
                [],
5678
                $this->iteratorClass,
5679
                false
5680
            );
5681
        }
5682
5683
        // php cast "bool"-index into "int"-index
5684 9
        if ((bool) $index === $index) {
5685 1
            $index = (int) $index;
5686
        }
5687
5688 9
        if ($this->offsetExists($index)) {
5689 7
            $return = [$this->array[$index]];
5690
        }
5691
5692 9
        return static::create(
5693 9
            $return,
5694 9
            $this->iteratorClass,
5695 9
            false
5696
        );
5697
    }
5698
5699
    /**
5700
     * Set a value for the current array (optional using dot-notation).
5701
     *
5702
     * EXAMPLE: <code>
5703
     * $arrayy = a(['Lars' => ['lastname' => 'Moelleken']]);
5704
     * $arrayy->set('Lars.lastname', 'Müller'); // Arrayy['Lars', ['lastname' => 'Müller']]]
5705
     * </code>
5706
     *
5707
     * @param string $key   <p>The key to set.</p>
5708
     * @param mixed  $value <p>Its value.</p>
5709
     *
5710
     * @return $this
5711
     *               <p>(Mutable) Return this Arrayy object.</p>
5712
     *
5713
     * @psalm-param  TKey $key
5714
     * @psalm-param  T $value
5715
     * @psalm-return static<TKey,T>
5716
     */
5717 28
    public function set($key, $value): self
5718
    {
5719 28
        $this->internalSet($key, $value);
5720
5721 27
        return $this;
5722
    }
5723
5724
    /**
5725
     * Get a value from a array and set it if it was not.
5726
     *
5727
     * WARNING: this method only set the value, if the $key is not already set
5728
     *
5729
     * EXAMPLE: <code>
5730
     * $arrayy = a([1 => 1, 2 => 2, 3 => 3]);
5731
     * $arrayy->setAndGet(1, 4); // 1
5732
     * $arrayy->setAndGet(0, 4); // 4
5733
     * </code>
5734
     *
5735
     * @param mixed $key      <p>The key</p>
5736
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5737
     *
5738
     * @return mixed
5739
     *               <p>(Mutable)</p>
5740
     */
5741 11
    public function setAndGet($key, $fallback = null)
5742
    {
5743 11
        $this->generatorToArray();
5744
5745
        // If the key doesn't exist, set it.
5746 11
        if (!$this->has($key)) {
5747 4
            $this->array = $this->set($key, $fallback)->toArray();
5748
        }
5749
5750 11
        return $this->get($key);
5751
    }
5752
5753
    /**
5754
     * Shifts a specified value off the beginning of array.
5755
     *
5756
     * @return mixed
5757
     *               <p>(Mutable) A shifted element from the current array.</p>
5758
     */
5759 5
    public function shift()
5760
    {
5761 5
        $this->generatorToArray();
5762
5763 5
        return \array_shift($this->array);
5764
    }
5765
5766
    /**
5767
     * Shuffle the current array.
5768
     *
5769
     * EXAMPLE: <code>
5770
     * a([1 => 'bar', 'foo' => 'foo'])->shuffle(); // e.g.: Arrayy[['foo' => 'foo', 1 => 'bar']]
5771
     * </code>
5772
     *
5773
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5774
     * @param array $array  [optional]
5775
     *
5776
     * @return static
5777
     *                <p>(Immutable)</p>
5778
     *
5779
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5780
     * @psalm-return static<TKey,T>
5781
     *
5782
     * @noinspection BadExceptionsProcessingInspection
5783
     * @noinspection RandomApiMigrationInspection
5784
     * @noinspection NonSecureShuffleUsageInspection
5785
     */
5786 2
    public function shuffle(bool $secure = false, array $array = null): self
5787
    {
5788 2
        if ($array === null) {
5789 2
            $array = $this->toArray(false);
5790
        }
5791
5792 2
        if ($secure !== true) {
5793 2
            \shuffle($array);
5794
        } else {
5795 1
            $size = \count($array, \COUNT_NORMAL);
5796 1
            $keys = \array_keys($array);
5797 1
            for ($i = $size - 1; $i > 0; --$i) {
5798
                try {
5799 1
                    $r = \random_int(0, $i);
5800
                } catch (\Exception $e) {
5801
                    $r = \mt_rand(0, $i);
5802
                }
5803 1
                if ($r !== $i) {
5804
                    $temp = $array[$keys[$r]];
5805
                    $array[$keys[$r]] = $array[$keys[$i]];
5806
                    $array[$keys[$i]] = $temp;
5807
                }
5808
            }
5809
        }
5810
5811 2
        foreach ($array as $key => $value) {
5812
            // check if recursive is needed
5813 2
            if (\is_array($value)) {
5814 2
                $array[$key] = $this->shuffle($secure, $value);
5815
            }
5816
        }
5817
5818 2
        return static::create(
5819 2
            $array,
5820 2
            $this->iteratorClass,
5821 2
            false
5822
        );
5823
    }
5824
5825
    /**
5826
     * Count the values from the current array.
5827
     *
5828
     * alias: for "Arrayy->count()"
5829
     *
5830
     * @param int $mode
5831
     *
5832
     * @return int
5833
     */
5834 20
    public function size(int $mode = \COUNT_NORMAL): int
5835
    {
5836 20
        return $this->count($mode);
5837
    }
5838
5839
    /**
5840
     * Checks whether array has exactly $size items.
5841
     *
5842
     * @param int $size
5843
     *
5844
     * @return bool
5845
     */
5846 1
    public function sizeIs(int $size): bool
5847
    {
5848
        // init
5849 1
        $itemsTempCount = 0;
5850
5851
        /** @noinspection PhpUnusedLocalVariableInspection */
5852
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
5853 1
        foreach ($this->getGeneratorByReference() as &$value) {
5854 1
            ++$itemsTempCount;
5855 1
            if ($itemsTempCount > $size) {
5856 1
                return false;
5857
            }
5858
        }
5859
5860 1
        return $itemsTempCount === $size;
5861
    }
5862
5863
    /**
5864
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5865
     * smaller than $fromSize.
5866
     *
5867
     * @param int $fromSize
5868
     * @param int $toSize
5869
     *
5870
     * @return bool
5871
     */
5872 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5873
    {
5874 1
        if ($fromSize > $toSize) {
5875 1
            $tmp = $toSize;
5876 1
            $toSize = $fromSize;
5877 1
            $fromSize = $tmp;
5878
        }
5879
5880
        // init
5881 1
        $itemsTempCount = 0;
5882
5883 1
        foreach ($this->getGenerator() as $key => $value) {
5884 1
            ++$itemsTempCount;
5885 1
            if ($itemsTempCount > $toSize) {
5886 1
                return false;
5887
            }
5888
        }
5889
5890 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5891
    }
5892
5893
    /**
5894
     * Checks whether array has more than $size items.
5895
     *
5896
     * @param int $size
5897
     *
5898
     * @return bool
5899
     */
5900 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...
5901
    {
5902
        // init
5903 1
        $itemsTempCount = 0;
5904
5905 1
        foreach ($this->getGenerator() as $key => $value) {
5906 1
            ++$itemsTempCount;
5907 1
            if ($itemsTempCount > $size) {
5908 1
                return true;
5909
            }
5910
        }
5911
5912 1
        return $itemsTempCount > $size;
5913
    }
5914
5915
    /**
5916
     * Checks whether array has less than $size items.
5917
     *
5918
     * @param int $size
5919
     *
5920
     * @return bool
5921
     */
5922 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...
5923
    {
5924
        // init
5925 1
        $itemsTempCount = 0;
5926
5927 1
        foreach ($this->getGenerator() as $key => $value) {
5928 1
            ++$itemsTempCount;
5929 1
            if ($itemsTempCount > $size) {
5930 1
                return false;
5931
            }
5932
        }
5933
5934 1
        return $itemsTempCount < $size;
5935
    }
5936
5937
    /**
5938
     * Counts all elements in an array, or something in an object.
5939
     *
5940
     * <p>
5941
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5942
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5943
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5944
     * implemented and used in PHP.
5945
     * </p>
5946
     *
5947
     * @return int
5948
     *             <p>
5949
     *             The number of elements in var, which is
5950
     *             typically an array, since anything else will have one
5951
     *             element.
5952
     *             </p>
5953
     *             <p>
5954
     *             If var is not an array or an object with
5955
     *             implemented Countable interface,
5956
     *             1 will be returned.
5957
     *             There is one exception, if var is &null;,
5958
     *             0 will be returned.
5959
     *             </p>
5960
     *             <p>
5961
     *             Caution: count may return 0 for a variable that isn't set,
5962
     *             but it may also return 0 for a variable that has been initialized with an
5963
     *             empty array. Use isset to test if a variable is set.
5964
     *             </p>
5965
     */
5966 10
    public function sizeRecursive(): int
5967
    {
5968 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5969
    }
5970
5971
    /**
5972
     * Extract a slice of the array.
5973
     *
5974
     * @param int      $offset       <p>Slice begin index.</p>
5975
     * @param int|null $length       <p>Length of the slice.</p>
5976
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5977
     *
5978
     * @return static
5979
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5980
     *
5981
     * @psalm-return static<TKey,T>
5982
     * @psalm-mutation-free
5983
     */
5984 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5985
    {
5986 5
        return static::create(
5987 5
            \array_slice(
5988 5
                $this->toArray(),
5989 5
                $offset,
5990 5
                $length,
5991 5
                $preserveKeys
5992
            ),
5993 5
            $this->iteratorClass,
5994 5
            false
5995
        );
5996
    }
5997
5998
    /**
5999
     * Sort the current array and optional you can keep the keys.
6000
     *
6001
     * EXAMPLE: <code>
6002
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sort(SORT_ASC, SORT_NATURAL, false); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6003
     * </code>
6004
     *
6005
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6006
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6007
     *                              <strong>SORT_NATURAL</strong></p>
6008
     * @param bool       $keepKeys
6009
     *
6010
     * @return static
6011
     *                <p>(Mutable) Return this Arrayy object.</p>
6012
     *
6013
     * @psalm-return static<TKey,T>
6014
     */
6015 20
    public function sort(
6016
        $direction = \SORT_ASC,
6017
        int $strategy = \SORT_REGULAR,
6018
        bool $keepKeys = false
6019
    ): self {
6020 20
        $this->generatorToArray();
6021
6022 20
        return $this->sorting(
6023 20
            $this->array,
6024 20
            $direction,
6025 20
            $strategy,
6026 20
            $keepKeys
6027
        );
6028
    }
6029
6030
    /**
6031
     * Sort the current array and optional you can keep the keys.
6032
     *
6033
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6034
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6035
     *                              <strong>SORT_NATURAL</strong></p>
6036
     * @param bool       $keepKeys
6037
     *
6038
     * @return static
6039
     *                <p>(Immutable) Return this Arrayy object.</p>
6040
     *
6041
     * @psalm-return static<TKey,T>
6042
     */
6043 12
    public function sortImmutable(
6044
        $direction = \SORT_ASC,
6045
        int $strategy = \SORT_REGULAR,
6046
        bool $keepKeys = false
6047
    ): self {
6048 12
        $that = clone $this;
6049
6050 12
        $that->generatorToArray();
6051
6052 12
        return $that->sorting(
6053 12
            $that->array,
6054 12
            $direction,
6055 12
            $strategy,
6056 12
            $keepKeys
6057
        );
6058
    }
6059
6060
    /**
6061
     * Sort the current array by key.
6062
     *
6063
     * EXAMPLE: <code>
6064
     * a([1 => 2, 0 => 1])->sortKeys(\SORT_ASC); // Arrayy[0 => 1, 1 => 2]
6065
     * </code>
6066
     *
6067
     * @see http://php.net/manual/en/function.ksort.php
6068
     * @see http://php.net/manual/en/function.krsort.php
6069
     *
6070
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6071
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6072
     *                              <strong>SORT_NATURAL</strong></p>
6073
     *
6074
     * @return $this
6075
     *               <p>(Mutable) Return this Arrayy object.</p>
6076
     *
6077
     * @psalm-return static<TKey,T>
6078
     */
6079 18
    public function sortKeys(
6080
        $direction = \SORT_ASC,
6081
        int $strategy = \SORT_REGULAR
6082
    ): self {
6083 18
        $this->generatorToArray();
6084
6085 18
        $this->sorterKeys($this->array, $direction, $strategy);
6086
6087 18
        return $this;
6088
    }
6089
6090
    /**
6091
     * Sort the current array by key.
6092
     *
6093
     * @see          http://php.net/manual/en/function.ksort.php
6094
     * @see          http://php.net/manual/en/function.krsort.php
6095
     *
6096
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6097
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6098
     *                              <strong>SORT_NATURAL</strong></p>
6099
     *
6100
     * @return $this
6101
     *               <p>(Immutable) Return this Arrayy object.</p>
6102
     *
6103
     * @psalm-return static<TKey,T>
6104
     * @psalm-mutation-free
6105
     */
6106 8
    public function sortKeysImmutable(
6107
        $direction = \SORT_ASC,
6108
        int $strategy = \SORT_REGULAR
6109
    ): self {
6110 8
        $that = clone $this;
6111
6112
        /**
6113
         * @psalm-suppress ImpureMethodCall - object is already cloned
6114
         */
6115 8
        $that->sortKeys($direction, $strategy);
6116
6117 8
        return $that;
6118
    }
6119
6120
    /**
6121
     * Sort the current array by value.
6122
     *
6123
     * EXAMPLE: <code>
6124
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueKeepIndex(SORT_ASC, SORT_REGULAR); // Arrayy[0 => 'a', 3 => 'd', 2 => 'f']
6125
     * </code>
6126
     *
6127
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6128
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6129
     *                              <strong>SORT_NATURAL</strong></p>
6130
     *
6131
     * @return static
6132
     *                <p>(Mutable)</p>
6133
     *
6134
     * @psalm-return static<TKey,T>
6135
     */
6136 1
    public function sortValueKeepIndex(
6137
        $direction = \SORT_ASC,
6138
        int $strategy = \SORT_REGULAR
6139
    ): self {
6140 1
        return $this->sort($direction, $strategy, true);
6141
    }
6142
6143
    /**
6144
     * Sort the current array by value.
6145
     *
6146
     * EXAMPLE: <code>
6147
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueNewIndex(SORT_ASC, SORT_NATURAL); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6148
     * </code>
6149
     *
6150
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6151
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6152
     *                              <strong>SORT_NATURAL</strong></p>
6153
     *
6154
     * @return static
6155
     *                <p>(Mutable)</p>
6156
     *
6157
     * @psalm-return static<TKey,T>
6158
     */
6159 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6160
    {
6161 1
        return $this->sort($direction, $strategy, false);
6162
    }
6163
6164
    /**
6165
     * Sort a array by value or by a closure.
6166
     *
6167
     * - If the sorter is null, the array is sorted naturally.
6168
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
6169
     *
6170
     * EXAMPLE: <code>
6171
     * $testArray = range(1, 5);
6172
     * $under = a($testArray)->sorter(
6173
     *     function ($value) {
6174
     *         return $value % 2 === 0;
6175
     *     }
6176
     * );
6177
     * var_dump($under); // Arrayy[1, 3, 5, 2, 4]
6178
     * </code>
6179
     *
6180
     * @param callable|string|null $sorter
6181
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
6182
     *                                        <strong>SORT_DESC</strong></p>
6183
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6184
     *                                        <strong>SORT_NATURAL</strong></p>
6185
     *
6186
     * @return static
6187
     *                <p>(Immutable)</p>
6188
     *
6189
     * @psalm-return static<TKey,T>
6190
     * @psalm-mutation-free
6191
     */
6192 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6193
    {
6194 1
        $array = $this->toArray();
6195 1
        $direction = $this->getDirection($direction);
6196
6197
        // Transform all values into their results.
6198 1
        if ($sorter) {
6199 1
            $arrayy = static::create(
6200 1
                $array,
6201 1
                $this->iteratorClass,
6202 1
                false
6203
            );
6204
6205
            /**
6206
             * @psalm-suppress MissingClosureReturnType
6207
             * @psalm-suppress MissingClosureParamType
6208
             */
6209 1
            $results = $arrayy->each(
6210
                static function ($value) use ($sorter) {
6211 1
                    if (\is_callable($sorter) === true) {
6212 1
                        return $sorter($value);
6213
                    }
6214
6215 1
                    return $sorter === $value;
6216 1
                }
6217
            );
6218
6219 1
            $results = $results->toArray();
6220
        } else {
6221 1
            $results = $array;
6222
        }
6223
6224
        // Sort by the results and replace by original values
6225 1
        \array_multisort($results, $direction, $strategy, $array);
6226
6227 1
        return static::create(
6228 1
            $array,
6229 1
            $this->iteratorClass,
6230 1
            false
6231
        );
6232
    }
6233
6234
    /**
6235
     * @param int      $offset
6236
     * @param int|null $length
6237
     * @param array    $replacement
6238
     *
6239
     * @return static
6240
     *                <p>(Immutable)</p>
6241
     *
6242
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
6243
     * @psalm-return static<TKey,T>
6244
     * @psalm-mutation-free
6245
     */
6246 1
    public function splice(int $offset, int $length = null, $replacement = []): self
6247
    {
6248 1
        $tmpArray = $this->toArray();
6249
6250 1
        \array_splice(
6251 1
            $tmpArray,
6252 1
            $offset,
6253 1
            $length ?? $this->count(),
6254 1
            $replacement
6255
        );
6256
6257 1
        return static::create(
6258 1
            $tmpArray,
6259 1
            $this->iteratorClass,
6260 1
            false
6261
        );
6262
    }
6263
6264
    /**
6265
     * Split an array in the given amount of pieces.
6266
     *
6267
     * EXAMPLE: <code>
6268
     * a(['a' => 1, 'b' => 2])->split(2, true); // Arrayy[['a' => 1], ['b' => 2]]
6269
     * </code>
6270
     *
6271
     * @param int  $numberOfPieces
6272
     * @param bool $keepKeys
6273
     *
6274
     * @return static
6275
     *                <p>(Immutable)</p>
6276
     *
6277
     * @psalm-return static<TKey,T>
6278
     * @psalm-mutation-free
6279
     */
6280 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
6281
    {
6282 1
        if ($keepKeys) {
6283 View Code Duplication
            $generator = function () use ($numberOfPieces) {
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...
6284 1
                $carry = [];
6285 1
                $i = 1;
6286 1
                foreach ($this->getGenerator() as $key => $value) {
6287 1
                    $carry[$key] = $value;
6288
6289 1
                    if ($i % $numberOfPieces !== 0) {
6290 1
                        ++$i;
6291
6292 1
                        continue;
6293
                    }
6294
6295 1
                    yield $carry;
6296
6297 1
                    $carry = [];
6298 1
                    $i = 1;
6299
                }
6300
6301 1
                if ($carry !== []) {
6302 1
                    yield $carry;
6303
                }
6304 1
            };
6305
        } else {
6306 View Code Duplication
            $generator = function () use ($numberOfPieces) {
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...
6307 1
                $carry = [];
6308 1
                $i = 1;
6309 1
                foreach ($this->getGenerator() as $key => $value) {
6310 1
                    $carry[] = $value;
6311
6312 1
                    if ($i % $numberOfPieces !== 0) {
6313 1
                        ++$i;
6314
6315 1
                        continue;
6316
                    }
6317
6318 1
                    yield $carry;
6319
6320 1
                    $carry = [];
6321 1
                    $i = 1;
6322
                }
6323
6324 1
                if ($carry !== []) {
6325 1
                    yield $carry;
6326
                }
6327 1
            };
6328
        }
6329
6330 1
        return static::create(
6331 1
            $generator,
6332 1
            $this->iteratorClass,
6333 1
            false
6334
        );
6335
    }
6336
6337
    /**
6338
     * Strip all empty items from the current array.
6339
     *
6340
     * EXAMPLE: <code>
6341
     * a(['a' => 1, 'b' => ''])->stripEmpty(); // Arrayy[['a' => 1]]
6342
     * </code>
6343
     *
6344
     * @return static
6345
     *                <p>(Immutable)</p>
6346
     *
6347
     * @psalm-return static<TKey,T>
6348
     * @psalm-mutation-free
6349
     */
6350 1
    public function stripEmpty(): self
6351
    {
6352 1
        return $this->filter(
6353
            static function ($item) {
6354 1
                if ($item === null) {
6355 1
                    return false;
6356
                }
6357
6358 1
                return (bool) \trim((string) $item);
6359 1
            }
6360
        );
6361
    }
6362
6363
    /**
6364
     * Swap two values between positions by key.
6365
     *
6366
     * EXAMPLE: <code>
6367
     * a(['a' => 1, 'b' => ''])->swap('a', 'b'); // Arrayy[['a' => '', 'b' => 1]]
6368
     * </code>
6369
     *
6370
     * @param int|string $swapA <p>a key in the array</p>
6371
     * @param int|string $swapB <p>a key in the array</p>
6372
     *
6373
     * @return static
6374
     *                <p>(Immutable)</p>
6375
     *
6376
     * @psalm-return static<TKey,T>
6377
     * @psalm-mutation-free
6378
     */
6379 1
    public function swap($swapA, $swapB): self
6380
    {
6381 1
        $array = $this->toArray();
6382
6383 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
6384
6385 1
        return static::create(
6386 1
            $array,
6387 1
            $this->iteratorClass,
6388 1
            false
6389
        );
6390
    }
6391
6392
    /**
6393
     * Get the current array from the "Arrayy"-object.
6394
     * alias for "getArray()"
6395
     *
6396
     * @param bool $convertAllArrayyElements <p>
6397
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6398
     *                                       </p>
6399
     * @param bool $preserveKeys             <p>
6400
     *                                       e.g.: A generator maybe return the same key more then once,
6401
     *                                       so maybe you will ignore the keys.
6402
     *                                       </p>
6403
     *
6404
     * @return array
6405
     *
6406
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6407
     * @psalm-mutation-free
6408
     */
6409 931
    public function toArray(
6410
        bool $convertAllArrayyElements = false,
6411
        bool $preserveKeys = true
6412
    ): array {
6413
        // init
6414 931
        $array = [];
6415
6416 931
        if ($convertAllArrayyElements) {
6417 2
            foreach ($this->getGenerator() as $key => $value) {
6418 2
                if ($value instanceof self) {
6419 1
                    $value = $value->toArray(
6420 1
                        $convertAllArrayyElements,
6421 1
                        $preserveKeys
6422
                    );
6423
                }
6424
6425 2
                if ($preserveKeys) {
6426 1
                    $array[$key] = $value;
6427
                } else {
6428 2
                    $array[] = $value;
6429
                }
6430
            }
6431
        } else {
6432 931
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
6433
        }
6434
6435 931
        return $array;
6436
    }
6437
6438
    /**
6439
     * Get the current array from the "Arrayy"-object as list.
6440
     *
6441
     * @param bool $convertAllArrayyElements <p>
6442
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6443
     *                                       </p>
6444
     *
6445
     * @return array
6446
     *
6447
     * @psalm-return list<array<TKey,T>>
6448
     * @psalm-mutation-free
6449
     */
6450 1
    public function toList(bool $convertAllArrayyElements = false): array
6451
    {
6452 1
        return $this->toArray(
6453 1
            $convertAllArrayyElements,
6454 1
            false
6455
        );
6456
    }
6457
6458
    /**
6459
     * Convert the current array to JSON.
6460
     *
6461
     * EXAMPLE: <code>
6462
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6463
     * </code>
6464
     *
6465
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6466
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6467
     *
6468
     * @return string
6469
     */
6470 12
    public function toJson(int $options = 0, int $depth = 512): string
6471
    {
6472 12
        $return = \json_encode($this->toArray(), $options, $depth);
6473 12
        if ($return === false) {
6474
            return '';
6475
        }
6476
6477 12
        return $return;
6478
    }
6479
6480
    /**
6481
     * @param string[]|null $items  [optional]
6482
     * @param string[]      $helper [optional]
6483
     *
6484
     * @return static|static[]
6485
     *
6486
     * @psalm-return static<int, static<TKey,T>>
6487
     */
6488 1
    public function toPermutation(array $items = null, array $helper = []): self
6489
    {
6490
        // init
6491 1
        $return = [];
6492
6493 1
        if ($items === null) {
6494 1
            $items = $this->toArray();
6495
        }
6496
6497 1
        if (empty($items)) {
6498 1
            $return[] = $helper;
6499
        } else {
6500 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6501 1
                $new_items = $items;
6502 1
                $new_helper = $helper;
6503 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6504
                /** @noinspection PhpSillyAssignmentInspection */
6505
                /** @var string[] $new_items */
6506 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...
6507 1
                \array_unshift($new_helper, $tmp_helper);
6508
                /** @noinspection SlowArrayOperationsInLoopInspection */
6509 1
                $return = \array_merge(
6510 1
                    $return,
6511 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6512
                );
6513
            }
6514
        }
6515
6516 1
        return static::create(
6517 1
            $return,
6518 1
            $this->iteratorClass,
6519 1
            false
6520
        );
6521
    }
6522
6523
    /**
6524
     * Implodes array to a string with specified separator.
6525
     *
6526
     * @param string $separator [optional] <p>The element's separator.</p>
6527
     *
6528
     * @return string
6529
     *                <p>The string representation of array, separated by ",".</p>
6530
     */
6531 19
    public function toString(string $separator = ','): string
6532
    {
6533 19
        return $this->implode($separator);
6534
    }
6535
6536
    /**
6537
     * Return a duplicate free copy of the current array.
6538
     *
6539
     * EXAMPLE: <code>
6540
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6541
     * </code>
6542
     *
6543
     * @return $this
6544
     *               <p>(Mutable)</p>
6545
     *
6546
     * @psalm-return static<TKey,T>
6547
     */
6548 13
    public function uniqueNewIndex(): self
6549
    {
6550
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6551
6552
        /**
6553
         * @psalm-suppress MissingClosureReturnType
6554
         * @psalm-suppress MissingClosureParamType
6555
         */
6556 13
        $this->array = $this->reduce(
6557
            static function ($resultArray, $value) {
6558 12
                if (!\in_array($value, $resultArray, true)) {
6559 12
                    $resultArray[] = $value;
6560
                }
6561
6562 12
                return $resultArray;
6563 13
            },
6564 13
            []
6565 13
        )->toArray();
6566 13
        $this->generator = null;
6567
6568 13
        return $this;
6569
    }
6570
6571
    /**
6572
     * Return a duplicate free copy of the current array. (with the old keys)
6573
     *
6574
     * EXAMPLE: <code>
6575
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6576
     * </code>
6577
     *
6578
     * @return $this
6579
     *               <p>(Mutable)</p>
6580
     *
6581
     * @psalm-return static<TKey,T>
6582
     */
6583 11
    public function uniqueKeepIndex(): self
6584
    {
6585
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6586
6587
        // init
6588 11
        $array = $this->toArray();
6589
6590
        /**
6591
         * @psalm-suppress MissingClosureReturnType
6592
         * @psalm-suppress MissingClosureParamType
6593
         */
6594 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...
6595 11
            \array_keys($array),
6596
            static function ($resultArray, $key) use ($array) {
6597 10
                if (!\in_array($array[$key], $resultArray, true)) {
6598 10
                    $resultArray[$key] = $array[$key];
6599
                }
6600
6601 10
                return $resultArray;
6602 11
            },
6603 11
            []
6604
        );
6605 11
        $this->generator = null;
6606
6607 11
        return $this;
6608
    }
6609
6610
    /**
6611
     * alias: for "Arrayy->uniqueNewIndex()"
6612
     *
6613
     * @return static
6614
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6615
     *
6616
     * @see          Arrayy::unique()
6617
     *
6618
     * @psalm-return static<TKey,T>
6619
     */
6620 13
    public function unique(): self
6621
    {
6622 13
        return $this->uniqueNewIndex();
6623
    }
6624
6625
    /**
6626
     * Prepends one or more values to the beginning of array at once.
6627
     *
6628
     * @param array ...$args
6629
     *
6630
     * @return $this
6631
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6632
     *
6633
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
6634
     * @psalm-return static<TKey,T>
6635
     */
6636 4
    public function unshift(...$args): self
6637
    {
6638 4
        $this->generatorToArray();
6639
6640 4
        \array_unshift($this->array, ...$args);
6641
6642 4
        return $this;
6643
    }
6644
6645
    /**
6646
     * Tests whether the given closure return something valid for all elements of this array.
6647
     *
6648
     * @param \Closure $closure the predicate
6649
     *
6650
     * @return bool
6651
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6652
     */
6653 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...
6654
    {
6655 1
        foreach ($this->getGenerator() as $key => $value) {
6656 1
            if (!$closure($value, $key)) {
6657 1
                return false;
6658
            }
6659
        }
6660
6661 1
        return true;
6662
    }
6663
6664
    /**
6665
     * Get all values from a array.
6666
     *
6667
     * EXAMPLE: <code>
6668
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
6669
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
6670
     * </code>
6671
     *
6672
     * @return static
6673
     *                <p>(Immutable)</p>
6674
     *
6675
     * @psalm-return static<TKey,T>
6676
     * @psalm-mutation-free
6677
     */
6678 2
    public function values(): self
6679
    {
6680 2
        return static::create(
6681
            function () {
6682
                /** @noinspection YieldFromCanBeUsedInspection */
6683 2
                foreach ($this->getGenerator() as $value) {
6684 2
                    yield $value;
6685
                }
6686 2
            },
6687 2
            $this->iteratorClass,
6688 2
            false
6689
        );
6690
    }
6691
6692
    /**
6693
     * Apply the given function to every element in the array, discarding the results.
6694
     *
6695
     * EXAMPLE: <code>
6696
     * $callable = function (&$value, $key) {
6697
     *     $value = $key;
6698
     * };
6699
     * $arrayy = a([1, 2, 3]);
6700
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
6701
     * </code>
6702
     *
6703
     * @param callable $callable
6704
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6705
     * @param mixed    $userData  [optional] <p>
6706
     *                            If the optional $userData parameter is supplied,
6707
     *                            it will be passed as the third parameter to the $callable.
6708
     *                            </p>
6709
     *
6710
     * @return $this
6711
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6712
     *
6713
     * @psalm-return static<TKey,T>
6714
     */
6715 12
    public function walk(
6716
        $callable,
6717
        bool $recursive = false,
6718
        $userData = self::ARRAYY_HELPER_WALK
6719
    ): self {
6720 12
        $this->generatorToArray();
6721
6722 12
        if ($this->array !== []) {
6723 10
            if ($recursive === true) {
6724 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6725
                    \array_walk_recursive($this->array, $callable, $userData);
6726
                } else {
6727 5
                    \array_walk_recursive($this->array, $callable);
6728
                }
6729
            } else {
6730
                /** @noinspection NestedPositiveIfStatementsInspection */
6731 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6732
                    \array_walk($this->array, $callable, $userData);
6733
                } else {
6734 5
                    \array_walk($this->array, $callable);
6735
                }
6736
            }
6737
        }
6738
6739 12
        return $this;
6740
    }
6741
6742
    /**
6743
     * Returns a collection of matching items.
6744
     *
6745
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
6746
     * @param mixed  $value                 the value to match
6747
     *
6748
     * @throws \InvalidArgumentException if property or method is not defined
6749
     *
6750
     * @return static
6751
     *
6752
     * @psalm-param  T $value
6753
     * @psalm-return static<TKey,T>
6754
     */
6755 1
    public function where(string $keyOrPropertyOrMethod, $value): self
6756
    {
6757 1
        return $this->filter(
6758
            function ($item) use ($keyOrPropertyOrMethod, $value) {
6759
                $accessorValue = $this->extractValue(
6760
                    $item,
6761
                    $keyOrPropertyOrMethod
6762
                );
6763
6764
                return $accessorValue === $value;
6765 1
            }
6766
        );
6767
    }
6768
6769
    /**
6770
     * Convert an array into a object.
6771
     *
6772
     * @param array $array
6773
     *
6774
     * @return \stdClass
6775
     *
6776
     * @psalm-param array<mixed,mixed> $array
6777
     */
6778 4
    final protected static function arrayToObject(array $array = []): \stdClass
6779
    {
6780
        // init
6781 4
        $object = new \stdClass();
6782
6783 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6784 1
            return $object;
6785
        }
6786
6787 3
        foreach ($array as $name => $value) {
6788 3
            if (\is_array($value)) {
6789 1
                $object->{$name} = static::arrayToObject($value);
6790
            } else {
6791 3
                $object->{$name} = $value;
6792
            }
6793
        }
6794
6795 3
        return $object;
6796
    }
6797
6798
    /**
6799
     * @param array|\Generator|null $input         <p>
6800
     *                                             An array containing keys to return.
6801
     *                                             </p>
6802
     * @param mixed|null            $search_values [optional] <p>
6803
     *                                             If specified, then only keys containing these values are returned.
6804
     *                                             </p>
6805
     * @param bool                  $strict        [optional] <p>
6806
     *                                             Determines if strict comparison (===) should be used during the
6807
     *                                             search.
6808
     *                                             </p>
6809
     *
6810
     * @return array
6811
     *               <p>an array of all the keys in input</p>
6812
     *
6813
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6814
     * @psalm-return array<TKey|mixed>
6815
     * @psalm-mutation-free
6816
     */
6817 11
    protected function array_keys_recursive(
6818
        $input = null,
6819
        $search_values = null,
6820
        bool $strict = true
6821
    ): array {
6822
        // init
6823 11
        $keys = [];
6824 11
        $keysTmp = [];
6825
6826 11
        if ($input === null) {
6827 4
            $input = $this->getGenerator();
6828
        }
6829
6830 11
        if ($search_values === null) {
6831 11
            foreach ($input as $key => $value) {
6832 11
                $keys[] = $key;
6833
6834
                // check if recursive is needed
6835 11
                if (\is_array($value)) {
6836 11
                    $keysTmp[] = $this->array_keys_recursive($value);
6837
                }
6838
            }
6839
        } else {
6840 1
            $is_array_tmp = \is_array($search_values);
6841
6842 1
            foreach ($input as $key => $value) {
6843 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...
6844
                    (
6845 1
                        $is_array_tmp === false
6846
                        &&
6847 1
                        $strict === true
6848
                        &&
6849 1
                        $search_values === $value
6850
                    )
6851
                    ||
6852
                    (
6853 1
                        $is_array_tmp === false
6854
                        &&
6855 1
                        $strict === false
6856
                        &&
6857 1
                        $search_values == $value
6858
                    )
6859
                    ||
6860
                    (
6861 1
                        $is_array_tmp === true
6862
                        &&
6863 1
                        \in_array($value, $search_values, $strict)
6864
                    )
6865
                ) {
6866 1
                    $keys[] = $key;
6867
                }
6868
6869
                // check if recursive is needed
6870 1
                if (\is_array($value)) {
6871 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6872
                }
6873
            }
6874
        }
6875
6876 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6877
    }
6878
6879
    /**
6880
     * @param mixed      $path
6881
     * @param callable   $callable
6882
     * @param array|null $currentOffset
6883
     *
6884
     * @return void
6885
     *
6886
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6887
     * @psalm-mutation-free
6888
     */
6889 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6890
    {
6891 10
        $this->generatorToArray();
6892
6893 10
        if ($currentOffset === null) {
6894 10
            $currentOffset = &$this->array;
6895
        }
6896
6897 10
        $explodedPath = \explode($this->pathSeparator, $path);
6898 10
        if ($explodedPath === false) {
6899
            return;
6900
        }
6901
6902 10
        $nextPath = \array_shift($explodedPath);
6903
6904 10
        if (!isset($currentOffset[$nextPath])) {
6905 1
            return;
6906
        }
6907
6908 9
        if (!empty($explodedPath)) {
6909 1
            $this->callAtPath(
6910 1
                \implode($this->pathSeparator, $explodedPath),
6911 1
                $callable,
6912 1
                $currentOffset[$nextPath]
6913
            );
6914
        } else {
6915 9
            $callable($currentOffset[$nextPath]);
6916
        }
6917 9
    }
6918
6919
    /**
6920
     * Extracts the value of the given property or method from the object.
6921
     *
6922
     * @param static $object                <p>The object to extract the value from.</p>
6923
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
6924
     *                                      value should be extracted.</p>
6925
     *
6926
     * @throws \InvalidArgumentException if the method or property is not defined
6927
     *
6928
     * @return mixed
6929
     *               <p>The value extracted from the specified property or method.</p>
6930
     *
6931
     * @psalm-param self<TKey,T> $object
6932
     */
6933 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
6934
    {
6935 1
        if (isset($object[$keyOrPropertyOrMethod])) {
6936 1
            $return = $object->get($keyOrPropertyOrMethod);
6937
6938 1
            if ($return instanceof self) {
6939
                return $return->toArray();
6940
            }
6941
6942 1
            return $return;
6943
        }
6944
6945
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
6946
            return $object->{$keyOrPropertyOrMethod};
6947
        }
6948
6949
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
6950
            return $object->{$keyOrPropertyOrMethod}();
6951
        }
6952
6953
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6954
    }
6955
6956
    /**
6957
     * create a fallback for array
6958
     *
6959
     * 1. use the current array, if it's a array
6960
     * 2. fallback to empty array, if there is nothing
6961
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6962
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6963
     * 5. call "__toArray()" on object, if the method exists
6964
     * 6. cast a string or object with "__toString()" into an array
6965
     * 7. throw a "InvalidArgumentException"-Exception
6966
     *
6967
     * @param mixed $data
6968
     *
6969
     * @throws \InvalidArgumentException
6970
     *
6971
     * @return array
6972
     *
6973
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6974
     */
6975 1206
    protected function fallbackForArray(&$data): array
6976
    {
6977 1206
        $data = $this->internalGetArray($data);
6978
6979 1206
        if ($data === null) {
6980 2
            throw new \InvalidArgumentException('Passed value should be a array');
6981
        }
6982
6983 1204
        return $data;
6984
    }
6985
6986
    /**
6987
     * @param bool $preserveKeys <p>
6988
     *                           e.g.: A generator maybe return the same key more then once,
6989
     *                           so maybe you will ignore the keys.
6990
     *                           </p>
6991
     *
6992
     * @return bool
6993
     *
6994
     * @noinspection ReturnTypeCanBeDeclaredInspection
6995
     * @psalm-mutation-free :/
6996
     */
6997 1118
    protected function generatorToArray(bool $preserveKeys = true)
6998
    {
6999 1118
        if ($this->generator) {
7000 2
            $this->array = $this->toArray(false, $preserveKeys);
7001 2
            $this->generator = null;
7002
7003 2
            return true;
7004
        }
7005
7006 1118
        return false;
7007
    }
7008
7009
    /**
7010
     * Get correct PHP constant for direction.
7011
     *
7012
     * @param int|string $direction
7013
     *
7014
     * @return int
7015
     * @psalm-mutation-free
7016
     */
7017 43
    protected function getDirection($direction): int
7018
    {
7019 43
        if ((string) $direction === $direction) {
7020 10
            $direction = \strtolower($direction);
7021
7022 10
            if ($direction === 'desc') {
7023 2
                $direction = \SORT_DESC;
7024
            } else {
7025 9
                $direction = \SORT_ASC;
7026
            }
7027
        }
7028
7029
        if (
7030 43
            $direction !== \SORT_DESC
7031
            &&
7032 43
            $direction !== \SORT_ASC
7033
        ) {
7034
            $direction = \SORT_ASC;
7035
        }
7036
7037 43
        return $direction;
7038
    }
7039
7040
    /**
7041
     * @return TypeCheckInterface[]
7042
     *
7043
     * @noinspection ReturnTypeCanBeDeclaredInspection
7044
     */
7045 24
    protected function getPropertiesFromPhpDoc()
7046
    {
7047 24
        static $PROPERTY_CACHE = [];
7048 24
        $cacheKey = 'Class::' . static::class;
7049
7050 24
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7051 22
            return $PROPERTY_CACHE[$cacheKey];
7052
        }
7053
7054
        // init
7055 4
        $properties = [];
7056
7057 4
        $reflector = new \ReflectionClass($this);
7058 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7059 4
        $docComment = $reflector->getDocComment();
7060 4 View Code Duplication
        if ($docComment) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
7061 3
            $docblock = $factory->create($docComment);
7062
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7063 3
            foreach ($docblock->getTagsByName('property') as $tag) {
7064 3
                $typeName = $tag->getVariableName();
7065
                /** @var string|null $typeName */
7066 3
                if ($typeName !== null) {
7067 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7068 3
                    if ($typeCheckPhpDoc !== null) {
7069 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7070
                    }
7071
                }
7072
            }
7073
        }
7074
7075
        /** @noinspection PhpAssignmentInConditionInspection */
7076 4
        while ($reflector = $reflector->getParentClass()) {
7077 4
            $docComment = $reflector->getDocComment();
7078 4 View Code Duplication
            if ($docComment) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
7079 4
                $docblock = $factory->create($docComment);
7080
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7081 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7082 1
                    $typeName = $tag->getVariableName();
7083
                    /** @var string|null $typeName */
7084 1
                    if ($typeName !== null) {
7085 1
                        if (isset($properties[$typeName])) {
7086 1
                            continue;
7087
                        }
7088
7089 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7090 1
                        if ($typeCheckPhpDoc !== null) {
7091 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7092
                        }
7093
                    }
7094
                }
7095
            }
7096
        }
7097
7098 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7099
    }
7100
7101
    /**
7102
     * @param mixed $glue
7103
     * @param mixed $pieces
7104
     * @param bool  $useKeys
7105
     *
7106
     * @return string
7107
     * @psalm-mutation-free
7108
     */
7109 36
    protected function implode_recursive(
7110
        $glue = '',
7111
        $pieces = [],
7112
        bool $useKeys = false
7113
    ): string {
7114 36
        if ($pieces instanceof self) {
7115 1
            $pieces = $pieces->toArray();
7116
        }
7117
7118 36
        if (\is_array($pieces)) {
7119 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7120 36
            $pieces_count_not_zero = $pieces_count > 0;
7121
7122 36
            return \implode(
7123 36
                $glue,
7124 36
                \array_map(
7125 36
                    [$this, 'implode_recursive'],
7126 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7127 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7128
                )
7129
            );
7130
        }
7131
7132
        if (
7133 36
            \is_scalar($pieces) === true
7134
            ||
7135 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7136
        ) {
7137 32
            return (string) $pieces;
7138
        }
7139
7140 8
        return '';
7141
    }
7142
7143
    /**
7144
     * @param mixed                 $needle   <p>
7145
     *                                        The searched value.
7146
     *                                        </p>
7147
     *                                        <p>
7148
     *                                        If needle is a string, the comparison is done
7149
     *                                        in a case-sensitive manner.
7150
     *                                        </p>
7151
     * @param array|\Generator|null $haystack <p>
7152
     *                                        The array.
7153
     *                                        </p>
7154
     * @param bool                  $strict   [optional] <p>
7155
     *                                        If the third parameter strict is set to true
7156
     *                                        then the in_array function will also check the
7157
     *                                        types of the
7158
     *                                        needle in the haystack.
7159
     *                                        </p>
7160
     *
7161
     * @return bool
7162
     *              <p>true if needle is found in the array, false otherwise</p>
7163
     *
7164
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
7165
     * @psalm-mutation-free
7166
     */
7167 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7168
    {
7169 18
        if ($haystack === null) {
7170
            $haystack = $this->getGenerator();
7171
        }
7172
7173 18
        foreach ($haystack as $item) {
7174 14
            if (\is_array($item)) {
7175 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7176
            } else {
7177
                /** @noinspection NestedPositiveIfStatementsInspection */
7178 14
                if ($strict === true) {
7179 14
                    $returnTmp = $item === $needle;
7180
                } else {
7181
                    $returnTmp = $item == $needle;
7182
                }
7183
            }
7184
7185 14
            if ($returnTmp === true) {
7186 14
                return true;
7187
            }
7188
        }
7189
7190 8
        return false;
7191
    }
7192
7193
    /**
7194
     * @param mixed $data
7195
     *
7196
     * @return array|null
7197
     *
7198
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
7199
     */
7200 1206
    protected function internalGetArray(&$data)
7201
    {
7202 1206
        if (\is_array($data)) {
7203 1200
            return $data;
7204
        }
7205
7206 74
        if (!$data) {
7207 7
            return [];
7208
        }
7209
7210 73
        if (\is_object($data) === true) {
7211 66
            if ($data instanceof \ArrayObject) {
7212 5
                return $data->getArrayCopy();
7213
            }
7214
7215 62
            if ($data instanceof \Generator) {
7216
                return static::createFromGeneratorImmutable($data)->toArray();
7217
            }
7218
7219 62
            if ($data instanceof \Traversable) {
7220
                return static::createFromObject($data)->toArray();
7221
            }
7222
7223 62
            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...
7224
                return (array) $data->jsonSerialize();
7225
            }
7226
7227 62
            if (\method_exists($data, '__toArray')) {
7228
                return (array) $data->__toArray();
7229
            }
7230
7231 62
            if (\method_exists($data, '__toString')) {
7232
                return [(string) $data];
7233
            }
7234
        }
7235
7236 69
        if (\is_callable($data)) {
7237
            /**
7238
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7239
             */
7240 60
            $this->generator = new ArrayyRewindableGenerator($data);
7241
7242 60
            return [];
7243
        }
7244
7245 11
        if (\is_scalar($data)) {
7246 9
            return [$data];
7247
        }
7248
7249 2
        return null;
7250
    }
7251
7252
    /**
7253
     * Internal mechanics of remove method.
7254
     *
7255
     * @param mixed $key
7256
     *
7257
     * @return bool
7258
     */
7259 22
    protected function internalRemove($key): bool
7260
    {
7261 22
        $this->generatorToArray();
7262
7263
        if (
7264 22
            $this->pathSeparator
7265
            &&
7266 22
            (string) $key === $key
7267
            &&
7268 22
            \strpos($key, $this->pathSeparator) !== false
7269
        ) {
7270
            $path = \explode($this->pathSeparator, (string) $key);
7271
7272
            if ($path !== false) {
7273
                // crawl though the keys
7274
                while (\count($path, \COUNT_NORMAL) > 1) {
7275
                    $key = \array_shift($path);
7276
7277
                    if (!$this->has($key)) {
7278
                        return false;
7279
                    }
7280
7281
                    $this->array = &$this->array[$key];
7282
                }
7283
7284
                $key = \array_shift($path);
7285
            }
7286
        }
7287
7288 22
        unset($this->array[$key]);
7289
7290 22
        return true;
7291
    }
7292
7293
    /**
7294
     * Internal mechanic of set method.
7295
     *
7296
     * @param int|string|null $key
7297
     * @param mixed           $value
7298
     * @param bool            $checkProperties
7299
     *
7300
     * @return bool
7301
     */
7302 1056
    protected function internalSet(
7303
        $key,
7304
        &$value,
7305
        bool $checkProperties = true
7306
    ): bool {
7307
        if (
7308 1056
            $checkProperties === true
7309
            &&
7310 1056
            $this->properties !== []
7311
        ) {
7312 112
            $this->checkType($key, $value);
7313
        }
7314
7315 1054
        if ($key === null) {
7316
            return false;
7317
        }
7318
7319 1054
        $this->generatorToArray();
7320
7321
        /** @psalm-var array<int|string,mixed> $array */
7322 1054
        $array = &$this->array;
7323
7324
        /**
7325
         * https://github.com/vimeo/psalm/issues/2536
7326
         *
7327
         * @psalm-suppress PossiblyInvalidArgument
7328
         * @psalm-suppress InvalidScalarArgument
7329
         */
7330
        if (
7331 1054
            $this->pathSeparator
7332
            &&
7333 1054
            (string) $key === $key
7334
            &&
7335 1054
            \strpos($key, $this->pathSeparator) !== false
7336
        ) {
7337 9
            $path = \explode($this->pathSeparator, (string) $key);
7338
7339 9
            if ($path !== false) {
7340
                // crawl through the keys
7341 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7342 9
                    $key = \array_shift($path);
7343
7344 9
                    $array = &$array[$key];
7345
                }
7346
7347 9
                $key = \array_shift($path);
7348
            }
7349
        }
7350
7351 1054
        if ($array === null) {
7352 4
            $array = [];
7353 1051
        } elseif (!\is_array($array)) {
7354 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7355
        }
7356
7357 1054
        $array[$key] = $value;
7358
7359 1054
        return true;
7360
    }
7361
7362
    /**
7363
     * Convert a object into an array.
7364
     *
7365
     * @param mixed|object $object
7366
     *
7367
     * @return array|mixed
7368
     *
7369
     * @psalm-mutation-free
7370
     */
7371 5
    protected static function objectToArray($object)
7372
    {
7373 5
        if (!\is_object($object)) {
7374 4
            return $object;
7375
        }
7376
7377 5
        $object = \get_object_vars($object);
7378
7379
        /**
7380
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7381
         */
7382 5
        return \array_map(['static', 'objectToArray'], $object);
7383
    }
7384
7385
    /**
7386
     * @param array $data
7387
     * @param bool  $checkPropertiesInConstructor
7388
     *
7389
     * @return void
7390
     *
7391
     * @psalm-param array<mixed,T> $data
7392
     */
7393 1204
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7394
    {
7395 1204
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7396
                                        &&
7397 1204
                                        $checkPropertiesInConstructor === true;
7398
7399 1204
        if ($this->properties !== []) {
7400 99
            foreach ($data as $key => &$valueInner) {
7401 99
                $this->internalSet(
7402 99
                    $key,
7403 99
                    $valueInner,
7404 99
                    $checkPropertiesInConstructor
7405
                );
7406
            }
7407
        } else {
7408
            if (
7409 1124
                $this->checkPropertyTypes === true
7410
                ||
7411 1124
                $checkPropertiesInConstructor === true
7412
            ) {
7413 23
                $this->properties = $this->getPropertiesFromPhpDoc();
7414
            }
7415
7416
            /** @var TypeCheckInterface[] $properties */
7417 1124
            $properties = $this->properties;
7418
7419
            if (
7420 1124
                $this->checkPropertiesMismatchInConstructor === true
7421
                &&
7422 1124
                \count($data) !== 0
7423
                &&
7424 1124
                \count(\array_diff_key($properties, $data)) > 0
7425
            ) {
7426 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...
7427
            }
7428
7429 1123
            foreach ($data as $key => &$valueInner) {
7430 952
                $this->internalSet(
7431 952
                    $key,
7432 952
                    $valueInner,
7433 952
                    $checkPropertiesInConstructor
7434
                );
7435
            }
7436
        }
7437 1196
    }
7438
7439
    /**
7440
     * sorting keys
7441
     *
7442
     * @param array      $elements
7443
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7444
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7445
     *                              <strong>SORT_NATURAL</strong></p>
7446
     *
7447
     * @return $this
7448
     *               <p>(Mutable) Return this Arrayy object.</p>
7449
     *
7450
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7451
     * @psalm-return static<TKey,T>
7452
     */
7453 18
    protected function sorterKeys(
7454
        array &$elements,
7455
        $direction = \SORT_ASC,
7456
        int $strategy = \SORT_REGULAR
7457
    ): self {
7458 18
        $direction = $this->getDirection($direction);
7459
7460
        switch ($direction) {
7461 18
            case 'desc':
7462 18
            case \SORT_DESC:
7463 6
                \krsort($elements, $strategy);
7464
7465 6
                break;
7466 13
            case 'asc':
7467 13
            case \SORT_ASC:
7468
            default:
7469 13
                \ksort($elements, $strategy);
7470
        }
7471
7472 18
        return $this;
7473
    }
7474
7475
    /**
7476
     * @param array      $elements  <p>Warning: used as reference</p>
7477
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7478
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7479
     *                              <strong>SORT_NATURAL</strong></p>
7480
     * @param bool       $keepKeys
7481
     *
7482
     * @return $this
7483
     *               <p>(Mutable) Return this Arrayy object.</p>
7484
     *
7485
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7486
     * @psalm-return static<TKey,T>
7487
     */
7488 24
    protected function sorting(
7489
        array &$elements,
7490
        $direction = \SORT_ASC,
7491
        int $strategy = \SORT_REGULAR,
7492
        bool $keepKeys = false
7493
    ): self {
7494 24
        $direction = $this->getDirection($direction);
7495
7496 24
        if (!$strategy) {
7497 24
            $strategy = \SORT_REGULAR;
7498
        }
7499
7500
        switch ($direction) {
7501 24
            case 'desc':
7502 24
            case \SORT_DESC:
7503 13
                if ($keepKeys) {
7504 9
                    \arsort($elements, $strategy);
7505
                } else {
7506 4
                    \rsort($elements, $strategy);
7507
                }
7508
7509 13
                break;
7510 11
            case 'asc':
7511 11
            case \SORT_ASC:
7512
            default:
7513 11
                if ($keepKeys) {
7514 4
                    \asort($elements, $strategy);
7515
                } else {
7516 7
                    \sort($elements, $strategy);
7517
                }
7518
        }
7519
7520 24
        return $this;
7521
    }
7522
7523
    /**
7524
     * @param array $array
7525
     *
7526
     * @return array
7527
     *
7528
     * @psalm-mutation-free
7529
     */
7530 25
    private function getArrayRecursiveHelperArrayy(array $array)
7531
    {
7532 25
        if ($array === []) {
7533
            return [];
7534
        }
7535
7536 25
        \array_walk_recursive(
7537 25
            $array,
7538
            /**
7539
             * @param array|self $item
7540
             *
7541
             * @return void
7542
             */
7543
            static function (&$item) {
7544 25
                if ($item instanceof self) {
7545 1
                    $item = $item->getArray();
7546
                }
7547 25
            }
7548
        );
7549
7550 25
        return $array;
7551
    }
7552
7553
    /**
7554
     * @param int|string|null $key
7555
     * @param mixed           $value
7556
     *
7557
     * @return void
7558
     */
7559 112
    private function checkType($key, $value)
7560
    {
7561
        if (
7562 112
            $key !== null
7563
            &&
7564 112
            isset($this->properties[$key]) === false
7565
            &&
7566 112
            $this->checkPropertiesMismatch === true
7567
        ) {
7568
            throw new \TypeError('The key "' . $key . '" does not exists as "@property" phpdoc. (' . \get_class($this) . ').');
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'The key "' . $key . '" ...get_class($this) . ').'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
7569
        }
7570
7571 112
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7572 97
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7573 24
        } elseif ($key !== null && isset($this->properties[$key])) {
7574 24
            $this->properties[$key]->checkType($value);
7575
        }
7576 110
    }
7577
}
7578