Completed
Push — master ( d163c5...b4c66b )
by Lars
05:00 queued 12s
created

Arrayy::mostUsedValues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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

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

Loading history...
2782
    {
2783 25
        if ($recursive === true) {
2784 4
            $result = \array_replace_recursive($this->getArray(), $array);
2785
        } else {
2786 21
            $result = \array_replace($this->getArray(), $array);
2787
        }
2788
2789 25
        return static::create(
2790 25
            $result,
2791 25
            $this->iteratorClass,
2792 25
            false
2793
        );
2794
    }
2795
2796
    /**
2797
     * Merge the new $array into the current array.
2798
     *
2799
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2800
     * - create new indexes
2801
     *
2802
     * @param array $array
2803
     * @param bool  $recursive
2804
     *
2805
     * @return static
2806
     *                <p>(Immutable)</p>
2807
     */
2808 16 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2809
    {
2810 16
        if ($recursive === true) {
2811 4
            $result = \array_merge_recursive($this->getArray(), $array);
2812
        } else {
2813 12
            $result = \array_merge($this->getArray(), $array);
2814
        }
2815
2816 16
        return static::create(
2817 16
            $result,
2818 16
            $this->iteratorClass,
2819 16
            false
2820
        );
2821
    }
2822
2823
    /**
2824
     * Merge the the current array into the $array.
2825
     *
2826
     * - use key,value from the new $array, also if the index is in the current array
2827
     *
2828
     * @param array $array
2829
     * @param bool  $recursive
2830
     *
2831
     * @return static
2832
     *                <p>(Immutable)</p>
2833
     */
2834 16 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2835
    {
2836 16
        if ($recursive === true) {
2837 4
            $result = \array_replace_recursive($array, $this->getArray());
2838
        } else {
2839 12
            $result = \array_replace($array, $this->getArray());
2840
        }
2841
2842 16
        return static::create(
2843 16
            $result,
2844 16
            $this->iteratorClass,
2845 16
            false
2846
        );
2847
    }
2848
2849
    /**
2850
     * Merge the current array into the new $array.
2851
     *
2852
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2853
     * - create new indexes
2854
     *
2855
     * @param array $array
2856
     * @param bool  $recursive
2857
     *
2858
     * @return static
2859
     *                <p>(Immutable)</p>
2860
     */
2861 17 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2862
    {
2863 17
        if ($recursive === true) {
2864 4
            $result = \array_merge_recursive($array, $this->getArray());
2865
        } else {
2866 13
            $result = \array_merge($array, $this->getArray());
2867
        }
2868
2869 17
        return static::create(
2870 17
            $result,
2871 17
            $this->iteratorClass,
2872 17
            false
2873
        );
2874
    }
2875
2876
    /**
2877
     * @return ArrayyMeta|static
2878
     */
2879 14
    public static function meta()
2880
    {
2881 14
        return (new ArrayyMeta())->getMetaObject(static::class);
2882
    }
2883
2884
    /**
2885
     * Get the min value from an array.
2886
     *
2887
     * @return mixed
2888
     */
2889 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...
2890
    {
2891 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2892 1
            return false;
2893
        }
2894
2895 9
        return \min($this->getArray());
2896
    }
2897
2898
    /**
2899
     * Move an array element to a new index.
2900
     *
2901
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2902
     *
2903
     * @param int|string $from
2904
     * @param int        $to
2905
     *
2906
     * @return static
2907
     *                <p>(Immutable)</p>
2908
     */
2909 1
    public function moveElement($from, $to): self
2910
    {
2911 1
        $array = $this->getArray();
2912
2913 1
        if (\is_int($from)) {
2914 1
            $tmp = \array_splice($array, $from, 1);
2915 1
            \array_splice($array, (int) $to, 0, $tmp);
2916 1
            $output = $array;
2917 1
        } elseif (\is_string($from)) {
2918 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2919 1
            $itemToMove = $array[$from];
2920 1
            if ($indexToMove !== false) {
2921 1
                \array_splice($array, $indexToMove, 1);
2922
            }
2923 1
            $i = 0;
2924 1
            $output = [];
2925 1
            foreach ($array as $key => $item) {
2926 1
                if ($i === $to) {
2927 1
                    $output[$from] = $itemToMove;
2928
                }
2929 1
                $output[$key] = $item;
2930 1
                ++$i;
2931
            }
2932
        } else {
2933
            $output = [];
2934
        }
2935
2936 1
        return static::create(
2937 1
            $output,
2938 1
            $this->iteratorClass,
2939 1
            false
2940
        );
2941
    }
2942
2943
    /**
2944
     * Get a subset of the items from the given array.
2945
     *
2946
     * @param mixed[] $keys
2947
     *
2948
     * @return static
2949
     *                <p>(Immutable)</p>
2950
     */
2951
    public function only(array $keys): self
2952
    {
2953
        $array = $this->getArray();
2954
2955
        return static::create(
2956
            \array_intersect_key($array, \array_flip($keys)),
2957
            $this->iteratorClass,
2958
            false
2959
        );
2960
    }
2961
2962
    /**
2963
     * Pad array to the specified size with a given value.
2964
     *
2965
     * @param int   $size  <p>Size of the result array.</p>
2966
     * @param mixed $value <p>Empty value by default.</p>
2967
     *
2968
     * @return static
2969
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
2970
     */
2971 4
    public function pad(int $size, $value): self
2972
    {
2973 4
        return static::create(
2974 4
            \array_pad($this->getArray(), $size, $value),
2975 4
            $this->iteratorClass,
2976 4
            false
2977
        );
2978
    }
2979
2980
    /**
2981
     * Pop a specified value off the end of the current array.
2982
     *
2983
     * @return mixed
2984
     *               <p>(Mutable) The popped element from the current array.</p>
2985
     */
2986 4
    public function pop()
2987
    {
2988 4
        $this->generatorToArray();
2989
2990 4
        return \array_pop($this->array);
2991
    }
2992
2993
    /**
2994
     * Prepend a (key) + value to the current array.
2995
     *
2996
     * @param mixed $value
2997
     * @param mixed $key
2998
     *
2999
     * @return static
3000
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3001
     */
3002 9
    public function prepend($value, $key = null): self
3003
    {
3004 9
        $this->generatorToArray();
3005
3006 9
        if ($key === null) {
3007 8
            \array_unshift($this->array, $value);
3008
        } else {
3009
            /** @noinspection AdditionOperationOnArraysInspection */
3010 2
            $this->array = [$key => $value] + $this->array;
3011
        }
3012
3013 9
        return $this;
3014
    }
3015
3016
    /**
3017
     * Add a suffix to each key.
3018
     *
3019
     * @param mixed $suffix
3020
     *
3021
     * @return static
3022
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3023
     */
3024 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...
3025
    {
3026
        // init
3027 10
        $result = [];
3028
3029 10
        foreach ($this->getGenerator() as $key => $item) {
3030 9
            if ($item instanceof self) {
3031
                $result[$key] = $item->prependToEachKey($suffix);
3032 9
            } elseif (\is_array($item)) {
3033
                $result[$key] = self::create(
3034
                    $item,
3035
                    $this->iteratorClass,
3036
                    false
3037
                )->prependToEachKey($suffix)
3038
                    ->toArray();
3039
            } else {
3040 9
                $result[$key . $suffix] = $item;
3041
            }
3042
        }
3043
3044 10
        return self::create(
3045 10
            $result,
3046 10
            $this->iteratorClass,
3047 10
            false
3048
        );
3049
    }
3050
3051
    /**
3052
     * Add a suffix to each value.
3053
     *
3054
     * @param mixed $suffix
3055
     *
3056
     * @return static
3057
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3058
     */
3059 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...
3060
    {
3061
        // init
3062 10
        $result = [];
3063
3064 10
        foreach ($this->getGenerator() as $key => $item) {
3065 9
            if ($item instanceof self) {
3066
                $result[$key] = $item->prependToEachValue($suffix);
3067 9
            } elseif (\is_array($item)) {
3068
                $result[$key] = self::create(
3069
                    $item,
3070
                    $this->iteratorClass,
3071
                    false
3072
                )->prependToEachValue($suffix)
3073
                    ->toArray();
3074 9
            } elseif (\is_object($item)) {
3075 1
                $result[$key] = $item;
3076
            } else {
3077 9
                $result[$key] = $item . $suffix;
3078
            }
3079
        }
3080
3081 10
        return self::create(
3082 10
            $result,
3083 10
            $this->iteratorClass,
3084 10
            false
3085
        );
3086
    }
3087
3088
    /**
3089
     * Return the value of a given key and
3090
     * delete the key.
3091
     *
3092
     * @param int|int[]|string|string[]|null $keyOrKeys
3093
     * @param mixed                          $fallback
3094
     *
3095
     * @return mixed
3096
     */
3097 1
    public function pull($keyOrKeys = null, $fallback = null)
3098
    {
3099 1
        if ($keyOrKeys === null) {
3100
            $array = $this->getArray();
3101
            $this->clear();
3102
3103
            return $array;
3104
        }
3105
3106 1
        if (\is_array($keyOrKeys)) {
3107 1
            $valueOrValues = [];
3108 1
            foreach ($keyOrKeys as $key) {
3109 1
                $valueOrValues[] = $this->get($key, $fallback);
3110 1
                $this->offsetUnset($key);
3111
            }
3112
        } else {
3113 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3114 1
            $this->offsetUnset($keyOrKeys);
3115
        }
3116
3117 1
        return $valueOrValues;
3118
    }
3119
3120
    /**
3121
     * Push one or more values onto the end of array at once.
3122
     *
3123
     * @return static
3124
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3125
     */
3126 4 View Code Duplication
    public function push(/* variadic arguments allowed */): 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...
3127
    {
3128 4
        $this->generatorToArray();
3129
3130 4
        if (\func_num_args()) {
3131 4
            $args = \func_get_args();
3132 4
            \array_push(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_push() as the parameter $array expects a reference.
Loading history...
3133
        }
3134
3135 4
        return $this;
3136
    }
3137
3138
    /**
3139
     * Get a random value from the current array.
3140
     *
3141
     * @param int|null $number <p>How many values you will take?</p>
3142
     *
3143
     * @return static
3144
     *                <p>(Immutable)</p>
3145
     */
3146 18
    public function randomImmutable(int $number = null): self
3147
    {
3148 18
        $this->generatorToArray();
3149
3150 18 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
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...
3151 1
            return static::create(
3152 1
                [],
3153 1
                $this->iteratorClass,
3154 1
                false
3155
            );
3156
        }
3157
3158 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...
3159
            /** @noinspection NonSecureArrayRandUsageInspection */
3160 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3161
3162 13
            return static::create(
3163 13
                $arrayRandValue,
3164 13
                $this->iteratorClass,
3165 13
                false
3166
            );
3167
        }
3168
3169 5
        $arrayTmp = $this->array;
3170
        /** @noinspection NonSecureShuffleUsageInspection */
3171 5
        \shuffle($arrayTmp);
3172
3173 5
        return static::create(
3174 5
            $arrayTmp,
3175 5
            $this->iteratorClass,
3176 5
            false
3177 5
        )->firstsImmutable($number);
3178
    }
3179
3180
    /**
3181
     * Pick a random key/index from the keys of this array.
3182
     *
3183
     * @throws \RangeException If array is empty
3184
     *
3185
     * @return mixed
3186
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3187
     */
3188 4
    public function randomKey()
3189
    {
3190 4
        $result = $this->randomKeys(1);
3191
3192 4
        if (!isset($result[0])) {
3193
            $result[0] = null;
3194
        }
3195
3196 4
        return $result[0];
3197
    }
3198
3199
    /**
3200
     * Pick a given number of random keys/indexes out of this array.
3201
     *
3202
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3203
     *
3204
     * @throws \RangeException If array is empty
3205
     *
3206
     * @return static
3207
     *                <p>(Immutable)</p>
3208
     */
3209 13
    public function randomKeys(int $number): self
3210
    {
3211 13
        $this->generatorToArray();
3212
3213 13
        $count = \count($this->array, \COUNT_NORMAL);
3214
3215 13
        if ($number === 0 || $number > $count) {
3216 2
            throw new \RangeException(
3217 2
                \sprintf(
3218 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3219 2
                    $number,
3220 2
                    $count
3221
                )
3222
            );
3223
        }
3224
3225 11
        $result = (array) \array_rand($this->array, $number);
3226
3227 11
        return static::create(
3228 11
            $result,
3229 11
            $this->iteratorClass,
3230 11
            false
3231
        );
3232
    }
3233
3234
    /**
3235
     * Get a random value from the current array.
3236
     *
3237
     * @param int|null $number <p>How many values you will take?</p>
3238
     *
3239
     * @return static
3240
     *                <p>(Mutable)</p>
3241
     */
3242 17
    public function randomMutable(int $number = null): self
3243
    {
3244 17
        $this->generatorToArray();
3245
3246 17 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
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...
3247
            return static::create(
3248
                [],
3249
                $this->iteratorClass,
3250
                false
3251
            );
3252
        }
3253
3254 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...
3255
            /** @noinspection NonSecureArrayRandUsageInspection */
3256 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3257 7
            $this->array = $arrayRandValue;
3258
3259 7
            return $this;
3260
        }
3261
3262
        /** @noinspection NonSecureShuffleUsageInspection */
3263 11
        \shuffle($this->array);
3264
3265 11
        return $this->firstsMutable($number);
3266
    }
3267
3268
    /**
3269
     * Pick a random value from the values of this array.
3270
     *
3271
     * @return mixed
3272
     *               <p>Get a random value or null if there wasn't a value.</p>
3273
     */
3274 4
    public function randomValue()
3275
    {
3276 4
        $result = $this->randomImmutable();
3277
3278 4
        if (!isset($result[0])) {
3279
            $result[0] = null;
3280
        }
3281
3282 4
        return $result[0];
3283
    }
3284
3285
    /**
3286
     * Pick a given number of random values out of this array.
3287
     *
3288
     * @param int $number
3289
     *
3290
     * @return static
3291
     *                <p>(Mutable)</p>
3292
     */
3293 7
    public function randomValues(int $number): self
3294
    {
3295 7
        return $this->randomMutable($number);
3296
    }
3297
3298
    /**
3299
     * Get a random value from an array, with the ability to skew the results.
3300
     *
3301
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3302
     *
3303
     * @param array    $array
3304
     * @param int|null $number <p>How many values you will take?</p>
3305
     *
3306
     * @return static
3307
     *                <p>(Immutable)</p>
3308
     */
3309 9
    public function randomWeighted(array $array, int $number = null): self
3310
    {
3311
        // init
3312 9
        $options = [];
3313
3314 9
        foreach ($array as $option => $weight) {
3315 9
            if ($this->searchIndex($option) !== false) {
3316 9
                for ($i = 0; $i < $weight; ++$i) {
3317 1
                    $options[] = $option;
3318
                }
3319
            }
3320
        }
3321
3322 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3323
    }
3324
3325
    /**
3326
     * Reduce the current array via callable e.g. anonymous-function.
3327
     *
3328
     * @param callable $callable
3329
     * @param array    $init
3330
     *
3331
     * @return static
3332
     *                <p>(Immutable)</p>
3333
     */
3334 16
    public function reduce($callable, array $init = []): self
3335
    {
3336 16
        if ($this->generator) {
3337 1
            $result = $init;
3338
3339 1
            foreach ($this->getGenerator() as $value) {
3340 1
                $result = $callable($result, $value);
3341
            }
3342
3343 1
            return static::create(
3344 1
                $result,
3345 1
                $this->iteratorClass,
3346 1
                false
3347
            );
3348
        }
3349
3350 16
        $result = \array_reduce($this->array, $callable, $init);
3351
3352 16
        if ($result === null) {
3353
            $this->array = [];
3354
        } else {
3355 16
            $this->array = (array) $result;
3356
        }
3357
3358 16
        return static::create(
3359 16
            $this->array,
3360 16
            $this->iteratorClass,
3361 16
            false
3362
        );
3363
    }
3364
3365
    /**
3366
     * @param bool $unique
3367
     *
3368
     * @return static
3369
     *                <p>(Immutable)</p>
3370
     */
3371 14
    public function reduce_dimension(bool $unique = true): self
3372
    {
3373
        // init
3374 14
        $result = [[]];
3375
3376 14
        foreach ($this->getGenerator() as $val) {
3377 12
            if (\is_array($val)) {
3378 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
3379
            } else {
3380 12
                $result[] = [$val];
3381
            }
3382
        }
3383 14
        $result = \array_merge(...$result);
3384
3385 14
        $resultArrayy = new self($result);
3386
3387 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3388
    }
3389
3390
    /**
3391
     * Create a numerically re-indexed Arrayy object.
3392
     *
3393
     * @return static
3394
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3395
     */
3396 9
    public function reindex(): self
3397
    {
3398 9
        $this->generatorToArray();
3399
3400 9
        $this->array = \array_values($this->array);
3401
3402 9
        return $this;
3403
    }
3404
3405
    /**
3406
     * Return all items that fail the truth test.
3407
     *
3408
     * @param \Closure $closure
3409
     *
3410
     * @return static
3411
     *                <p>(Immutable)</p>
3412
     */
3413 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...
3414
    {
3415
        // init
3416 1
        $filtered = [];
3417
3418 1
        foreach ($this->getGenerator() as $key => $value) {
3419 1
            if (!$closure($value, $key)) {
3420 1
                $filtered[$key] = $value;
3421
            }
3422
        }
3423
3424 1
        return static::create(
3425 1
            $filtered,
3426 1
            $this->iteratorClass,
3427 1
            false
3428
        );
3429
    }
3430
3431
    /**
3432
     * Remove a value from the current array (optional using dot-notation).
3433
     *
3434
     * @param mixed $key
3435
     *
3436
     * @return static
3437
     *                <p>(Mutable)</p>
3438
     */
3439 18
    public function remove($key): self
3440
    {
3441
        // recursive call
3442 18
        if (\is_array($key)) {
3443
            foreach ($key as $k) {
3444
                $this->internalRemove($k);
3445
            }
3446
3447
            return static::create(
3448
                $this->getArray(),
3449
                $this->iteratorClass,
3450
                false
3451
            );
3452
        }
3453
3454 18
        $this->internalRemove($key);
3455
3456 18
        return static::create(
3457 18
            $this->getArray(),
3458 18
            $this->iteratorClass,
3459 18
            false
3460
        );
3461
    }
3462
3463
    /**
3464
     * Remove the first value from the current array.
3465
     *
3466
     * @return static
3467
     *                <p>(Immutable)</p>
3468
     */
3469 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...
3470
    {
3471 7
        $tmpArray = $this->getArray();
3472
3473 7
        \array_shift($tmpArray);
3474
3475 7
        return static::create(
3476 7
            $tmpArray,
3477 7
            $this->iteratorClass,
3478 7
            false
3479
        );
3480
    }
3481
3482
    /**
3483
     * Remove the last value from the current array.
3484
     *
3485
     * @return static
3486
     *                <p>(Immutable)</p>
3487
     */
3488 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...
3489
    {
3490 7
        $tmpArray = $this->getArray();
3491
3492 7
        \array_pop($tmpArray);
3493
3494 7
        return static::create(
3495 7
            $tmpArray,
3496 7
            $this->iteratorClass,
3497 7
            false
3498
        );
3499
    }
3500
3501
    /**
3502
     * Removes a particular value from an array (numeric or associative).
3503
     *
3504
     * @param mixed $value
3505
     *
3506
     * @return static
3507
     *                <p>(Immutable)</p>
3508
     */
3509 7
    public function removeValue($value): self
3510
    {
3511 7
        $this->generatorToArray();
3512
3513
        // init
3514 7
        $isNumericArray = true;
3515
3516 7
        foreach ($this->getGenerator() as $key => $item) {
3517 6
            if ($item === $value) {
3518 6
                if (!\is_int($key)) {
3519
                    $isNumericArray = false;
3520
                }
3521 6
                unset($this->array[$key]);
3522
            }
3523
        }
3524
3525 7
        if ($isNumericArray) {
3526 7
            $this->array = \array_values($this->array);
3527
        }
3528
3529 7
        return static::create(
3530 7
            $this->array,
3531 7
            $this->iteratorClass,
3532 7
            false
3533
        );
3534
    }
3535
3536
    /**
3537
     * Generate array of repeated arrays.
3538
     *
3539
     * @param int $times <p>How many times has to be repeated.</p>
3540
     *
3541
     * @return static
3542
     *                <p>(Immutable)</p>
3543
     */
3544 1
    public function repeat($times): self
3545
    {
3546 1
        if ($times === 0) {
3547 1
            return new static();
3548
        }
3549
3550 1
        return static::create(
3551 1
            \array_fill(0, (int) $times, $this->getArray()),
3552 1
            $this->iteratorClass,
3553 1
            false
3554
        );
3555
    }
3556
3557
    /**
3558
     * Replace a key with a new key/value pair.
3559
     *
3560
     * @param mixed $replace
3561
     * @param mixed $key
3562
     * @param mixed $value
3563
     *
3564
     * @return static
3565
     *                <p>(Immutable)</p>
3566
     */
3567 2
    public function replace($replace, $key, $value): self
3568
    {
3569 2
        $that = clone $this;
3570
3571 2
        return $that->remove($replace)
3572 2
            ->set($key, $value);
3573
    }
3574
3575
    /**
3576
     * Create an array using the current array as values and the other array as keys.
3577
     *
3578
     * @param array $keys <p>An array of keys.</p>
3579
     *
3580
     * @return static
3581
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3582
     */
3583 2
    public function replaceAllKeys(array $keys): self
3584
    {
3585 2
        return static::create(
3586 2
            \array_combine($keys, $this->getArray()),
3587 2
            $this->iteratorClass,
3588 2
            false
3589
        );
3590
    }
3591
3592
    /**
3593
     * Create an array using the current array as keys and the other array as values.
3594
     *
3595
     * @param array $array <p>An array o values.</p>
3596
     *
3597
     * @return static
3598
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3599
     */
3600 2
    public function replaceAllValues(array $array): self
3601
    {
3602 2
        return static::create(
3603 2
            \array_combine($this->array, $array),
3604 2
            $this->iteratorClass,
3605 2
            false
3606
        );
3607
    }
3608
3609
    /**
3610
     * Replace the keys in an array with another set.
3611
     *
3612
     * @param array $keys <p>An array of keys matching the array's size</p>
3613
     *
3614
     * @return static
3615
     *                <p>(Immutable)</p>
3616
     */
3617 1
    public function replaceKeys(array $keys): self
3618
    {
3619 1
        $values = \array_values($this->getArray());
3620 1
        $result = \array_combine($keys, $values);
3621
3622 1
        return static::create(
3623 1
            $result,
3624 1
            $this->iteratorClass,
3625 1
            false
3626
        );
3627
    }
3628
3629
    /**
3630
     * Replace the first matched value in an array.
3631
     *
3632
     * @param mixed $search      <p>The value to replace.</p>
3633
     * @param mixed $replacement <p>The value to replace.</p>
3634
     *
3635
     * @return static
3636
     *                <p>(Immutable)</p>
3637
     */
3638 3
    public function replaceOneValue($search, $replacement = ''): self
3639
    {
3640 3
        $array = $this->getArray();
3641 3
        $key = \array_search($search, $array, true);
3642
3643 3
        if ($key !== false) {
3644 3
            $array[$key] = $replacement;
3645
        }
3646
3647 3
        return static::create(
3648 3
            $array,
3649 3
            $this->iteratorClass,
3650 3
            false
3651
        );
3652
    }
3653
3654
    /**
3655
     * Replace values in the current array.
3656
     *
3657
     * @param mixed $search      <p>The value to replace.</p>
3658
     * @param mixed $replacement <p>What to replace it with.</p>
3659
     *
3660
     * @return static
3661
     *                <p>(Immutable)</p>
3662
     */
3663 1
    public function replaceValues($search, $replacement = ''): self
3664
    {
3665 1
        $array = $this->each(
3666
            static function ($value) use ($search, $replacement) {
3667 1
                return \str_replace($search, $replacement, $value);
3668 1
            }
3669
        );
3670
3671 1
        return $array;
3672
    }
3673
3674
    /**
3675
     * Get the last elements from index $from until the end of this array.
3676
     *
3677
     * @param int $from
3678
     *
3679
     * @return static
3680
     *                <p>(Immutable)</p>
3681
     */
3682 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...
3683
    {
3684 15
        $tmpArray = $this->getArray();
3685
3686 15
        return static::create(
3687 15
            \array_splice($tmpArray, $from),
3688 15
            $this->iteratorClass,
3689 15
            false
3690
        );
3691
    }
3692
3693
    /**
3694
     * Return the array in the reverse order.
3695
     *
3696
     * @return static
3697
     *                <p>(Mutable) Return this Arrayy object.</p>
3698
     */
3699 8
    public function reverse(): self
3700
    {
3701 8
        $this->generatorToArray();
3702
3703 8
        $this->array = \array_reverse($this->array);
3704
3705 8
        return $this;
3706
    }
3707
3708
    /**
3709
     * Sort an array in reverse order.
3710
     *
3711
     * @param int $sort_flags [optional] <p>
3712
     *                        You may modify the behavior of the sort using the optional
3713
     *                        parameter sort_flags, for details
3714
     *                        see sort.
3715
     *                        </p>
3716
     *
3717
     * @return static
3718
     *                <p>(Mutable) Return this Arrayy object.</p>
3719
     */
3720 4
    public function rsort(int $sort_flags = 0): self
3721
    {
3722 4
        $this->generatorToArray();
3723
3724 4
        \rsort($this->array, $sort_flags);
3725
3726 4
        return $this;
3727
    }
3728
3729
    /**
3730
     * Search for the first index of the current array via $value.
3731
     *
3732
     * @param mixed $value
3733
     *
3734
     * @return false|float|int|string
3735
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3736
     */
3737 20
    public function searchIndex($value)
3738
    {
3739 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3740 19
            if ($value === $valueFromArray) {
3741 19
                return $keyFromArray;
3742
            }
3743
        }
3744
3745 11
        return false;
3746
    }
3747
3748
    /**
3749
     * Search for the value of the current array via $index.
3750
     *
3751
     * @param mixed $index
3752
     *
3753
     * @return static
3754
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3755
     */
3756 9
    public function searchValue($index): self
3757
    {
3758 9
        $this->generatorToArray();
3759
3760
        // init
3761 9
        $return = [];
3762
3763 9
        if ($this->array === []) {
3764
            return static::create(
3765
                [],
3766
                $this->iteratorClass,
3767
                false
3768
            );
3769
        }
3770
3771
        // php cast "bool"-index into "int"-index
3772 9
        if ((bool) $index === $index) {
3773 1
            $index = (int) $index;
3774
        }
3775
3776 9
        if (\array_key_exists($index, $this->array) === true) {
3777 7
            $return = [$this->array[$index]];
3778
        }
3779
3780 9
        return static::create(
3781 9
            $return,
3782 9
            $this->iteratorClass,
3783 9
            false
3784
        );
3785
    }
3786
3787
    /**
3788
     * Set a value for the current array (optional using dot-notation).
3789
     *
3790
     * @param string $key   <p>The key to set.</p>
3791
     * @param mixed  $value <p>Its value.</p>
3792
     *
3793
     * @return static
3794
     *                <p>(Mutable)</p>
3795
     */
3796 18
    public function set($key, $value): self
3797
    {
3798 18
        $this->generatorToArray();
3799
3800 18
        $this->internalSet($key, $value);
3801
3802 18
        return $this;
3803
    }
3804
3805
    /**
3806
     * Get a value from a array and set it if it was not.
3807
     *
3808
     * WARNING: this method only set the value, if the $key is not already set
3809
     *
3810
     * @param mixed $key      <p>The key</p>
3811
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3812
     *
3813
     * @return mixed
3814
     *               <p>(Mutable)</p>
3815
     */
3816 11
    public function setAndGet($key, $fallback = null)
3817
    {
3818 11
        $this->generatorToArray();
3819
3820
        // If the key doesn't exist, set it.
3821 11
        if (!$this->has($key)) {
3822 4
            $this->array = $this->set($key, $fallback)->getArray();
3823
        }
3824
3825 11
        return $this->get($key);
3826
    }
3827
3828
    /**
3829
     * Shifts a specified value off the beginning of array.
3830
     *
3831
     * @return mixed
3832
     *               <p>(Mutable) A shifted element from the current array.</p>
3833
     */
3834 4
    public function shift()
3835
    {
3836 4
        $this->generatorToArray();
3837
3838 4
        return \array_shift($this->array);
3839
    }
3840
3841
    /**
3842
     * Shuffle the current array.
3843
     *
3844
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3845
     * @param array $array  [optional]
3846
     *
3847
     * @return static
3848
     *                <p>(Immutable)</p>
3849
     */
3850 1
    public function shuffle(bool $secure = false, array $array = null): self
3851
    {
3852 1
        if ($array === null) {
3853 1
            $array = $this->getArray();
3854
        }
3855
3856 1
        if ($secure !== true) {
3857
            /** @noinspection NonSecureShuffleUsageInspection */
3858 1
            \shuffle($array);
3859
        } else {
3860 1
            $size = \count($array, \COUNT_NORMAL);
3861 1
            $keys = \array_keys($array);
3862 1
            for ($i = $size - 1; $i > 0; --$i) {
3863
                try {
3864 1
                    $r = \random_int(0, $i);
3865
                } catch (\Exception $e) {
3866
                    /** @noinspection RandomApiMigrationInspection */
3867
                    $r = \mt_rand(0, $i);
3868
                }
3869 1
                if ($r !== $i) {
3870
                    $temp = $array[$keys[$r]];
3871
                    $array[$keys[$r]] = $array[$keys[$i]];
3872
                    $array[$keys[$i]] = $temp;
3873
                }
3874
            }
3875
3876
            // reset indices
3877 1
            $array = \array_values($array);
3878
        }
3879
3880 1
        foreach ($array as $key => $value) {
3881
            // check if recursive is needed
3882 1
            if (\is_array($value) === true) {
3883 1
                $array[$key] = $this->shuffle($secure, $value);
3884
            }
3885
        }
3886
3887 1
        return static::create(
3888 1
            $array,
3889 1
            $this->iteratorClass,
3890 1
            false
3891
        );
3892
    }
3893
3894
    /**
3895
     * Count the values from the current array.
3896
     *
3897
     * alias: for "Arrayy->count()"
3898
     *
3899
     * @param int $mode
3900
     *
3901
     * @return int
3902
     */
3903 20
    public function size(int $mode = \COUNT_NORMAL): int
3904
    {
3905 20
        return $this->count($mode);
3906
    }
3907
3908
    /**
3909
     * Checks whether array has exactly $size items.
3910
     *
3911
     * @param int $size
3912
     *
3913
     * @return bool
3914
     */
3915 1 View Code Duplication
    public function sizeIs(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3916
    {
3917
        // init
3918 1
        $itemsTempCount = 0;
3919
3920 1
        foreach ($this->getGenerator() as $key => $value) {
3921 1
            ++$itemsTempCount;
3922 1
            if ($itemsTempCount > $size) {
3923 1
                return false;
3924
            }
3925
        }
3926
3927 1
        return $itemsTempCount === $size;
3928
    }
3929
3930
    /**
3931
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
3932
     * smaller than $fromSize.
3933
     *
3934
     * @param int $fromSize
3935
     * @param int $toSize
3936
     *
3937
     * @return bool
3938
     */
3939 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
3940
    {
3941 1
        if ($fromSize > $toSize) {
3942 1
            $tmp = $toSize;
3943 1
            $toSize = $fromSize;
3944 1
            $fromSize = $tmp;
3945
        }
3946
3947
        // init
3948 1
        $itemsTempCount = 0;
3949
3950 1
        foreach ($this->getGenerator() as $key => $value) {
3951 1
            ++$itemsTempCount;
3952 1
            if ($itemsTempCount > $toSize) {
3953 1
                return false;
3954
            }
3955
        }
3956
3957 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
3958
    }
3959
3960
    /**
3961
     * Checks whether array has more than $size items.
3962
     *
3963
     * @param int $size
3964
     *
3965
     * @return bool
3966
     */
3967 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...
3968
    {
3969
        // init
3970 1
        $itemsTempCount = 0;
3971
3972 1
        foreach ($this->getGenerator() as $key => $value) {
3973 1
            ++$itemsTempCount;
3974 1
            if ($itemsTempCount > $size) {
3975 1
                return true;
3976
            }
3977
        }
3978
3979 1
        return $itemsTempCount > $size;
3980
    }
3981
3982
    /**
3983
     * Checks whether array has less than $size items.
3984
     *
3985
     * @param int $size
3986
     *
3987
     * @return bool
3988
     */
3989 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...
3990
    {
3991
        // init
3992 1
        $itemsTempCount = 0;
3993
3994 1
        foreach ($this->getGenerator() as $key => $value) {
3995 1
            ++$itemsTempCount;
3996 1
            if ($itemsTempCount > $size) {
3997 1
                return false;
3998
            }
3999
        }
4000
4001 1
        return $itemsTempCount < $size;
4002
    }
4003
4004
    /**
4005
     * Counts all elements in an array, or something in an object.
4006
     *
4007
     * <p>
4008
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4009
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4010
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4011
     * implemented and used in PHP.
4012
     * </p>
4013
     *
4014
     * @return int
4015
     *             <p>
4016
     *             The number of elements in var, which is
4017
     *             typically an array, since anything else will have one
4018
     *             element.
4019
     *             </p>
4020
     *             <p>
4021
     *             If var is not an array or an object with
4022
     *             implemented Countable interface,
4023
     *             1 will be returned.
4024
     *             There is one exception, if var is &null;,
4025
     *             0 will be returned.
4026
     *             </p>
4027
     *             <p>
4028
     *             Caution: count may return 0 for a variable that isn't set,
4029
     *             but it may also return 0 for a variable that has been initialized with an
4030
     *             empty array. Use isset to test if a variable is set.
4031
     *             </p>
4032
     */
4033 10
    public function sizeRecursive(): int
4034
    {
4035 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4036
    }
4037
4038
    /**
4039
     * Extract a slice of the array.
4040
     *
4041
     * @param int      $offset       <p>Slice begin index.</p>
4042
     * @param int|null $length       <p>Length of the slice.</p>
4043
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4044
     *
4045
     * @return static
4046
     *                <p>A slice of the original array with length $length.</p>
4047
     */
4048 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
4049
    {
4050 4
        return static::create(
4051 4
            \array_slice(
4052 4
                $this->getArray(),
4053 4
                $offset,
4054 4
                $length,
4055 4
                $preserveKeys
4056
            ),
4057 4
            $this->iteratorClass,
4058 4
            false
4059
        );
4060
    }
4061
4062
    /**
4063
     * Sort the current array and optional you can keep the keys.
4064
     *
4065
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4066
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4067
     *                              <strong>SORT_NATURAL</strong></p>
4068
     * @param bool       $keepKeys
4069
     *
4070
     * @return static
4071
     *                <p>(Mutable) Return this Arrayy object.</p>
4072
     */
4073 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4074
    {
4075 20
        $this->generatorToArray();
4076
4077 20
        return $this->sorting(
4078 20
            $this->array,
4079 20
            $direction,
4080 20
            $strategy,
4081 20
            $keepKeys
4082
        );
4083
    }
4084
4085
    /**
4086
     * Sort the current array by key.
4087
     *
4088
     * @see http://php.net/manual/en/function.ksort.php
4089
     * @see http://php.net/manual/en/function.krsort.php
4090
     *
4091
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4092
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4093
     *                              <strong>SORT_NATURAL</strong></p>
4094
     *
4095
     * @return static
4096
     *                <p>(Mutable) Return this Arrayy object.</p>
4097
     */
4098 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4099
    {
4100 18
        $this->generatorToArray();
4101
4102 18
        $this->sorterKeys($this->array, $direction, $strategy);
4103
4104 18
        return $this;
4105
    }
4106
4107
    /**
4108
     * Sort the current array by value.
4109
     *
4110
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4111
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4112
     *                              <strong>SORT_NATURAL</strong></p>
4113
     *
4114
     * @return static
4115
     *                <p>(Mutable)</p>
4116
     */
4117 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4118
    {
4119 1
        return $this->sort($direction, $strategy, true);
4120
    }
4121
4122
    /**
4123
     * Sort the current array by value.
4124
     *
4125
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4126
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4127
     *                              <strong>SORT_NATURAL</strong></p>
4128
     *
4129
     * @return static
4130
     *                <p>(Mutable)</p>
4131
     */
4132 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4133
    {
4134 1
        return $this->sort($direction, $strategy, false);
4135
    }
4136
4137
    /**
4138
     * Sort a array by value, by a closure or by a property.
4139
     *
4140
     * - If the sorter is null, the array is sorted naturally.
4141
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4142
     *
4143
     * @param callable|string|null $sorter
4144
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4145
     *                                        <strong>SORT_DESC</strong></p>
4146
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4147
     *                                        <strong>SORT_NATURAL</strong></p>
4148
     *
4149
     * @return static
4150
     *                <p>(Immutable)</p>
4151
     */
4152 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4153
    {
4154 1
        $array = $this->getArray();
4155 1
        $direction = $this->getDirection($direction);
4156
4157
        // Transform all values into their results.
4158 1
        if ($sorter) {
4159 1
            $arrayy = static::create(
4160 1
                $array,
4161 1
                $this->iteratorClass,
4162 1
                false
4163
            );
4164
4165 1
            $results = $arrayy->each(
4166
                function ($value) use ($sorter) {
4167 1
                    if (\is_callable($sorter)) {
4168 1
                        return $sorter($value);
4169
                    }
4170
4171 1
                    return $this->get($sorter, null, $this->getArray());
4172 1
                }
4173
            );
4174
4175 1
            $results = $results->getArray();
4176
        } else {
4177 1
            $results = $array;
4178
        }
4179
4180
        // Sort by the results and replace by original values
4181 1
        \array_multisort($results, $direction, $strategy, $array);
4182
4183 1
        return static::create(
4184 1
            $array,
4185 1
            $this->iteratorClass,
4186 1
            false
4187
        );
4188
    }
4189
4190
    /**
4191
     * Split an array in the given amount of pieces.
4192
     *
4193
     * @param int  $numberOfPieces
4194
     * @param bool $keepKeys
4195
     *
4196
     * @return static
4197
     *                <p>(Immutable)</p>
4198
     */
4199 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4200
    {
4201 1
        $this->generatorToArray();
4202
4203 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4204
4205 1
        if ($arrayCount === 0) {
4206 1
            $result = [];
4207
        } else {
4208 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4209 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4210
        }
4211
4212 1
        return static::create(
4213 1
            $result,
4214 1
            $this->iteratorClass,
4215 1
            false
4216
        );
4217
    }
4218
4219
    /**
4220
     * Stripe all empty items.
4221
     *
4222
     * @return static
4223
     *                <p>(Immutable)</p>
4224
     */
4225 1
    public function stripEmpty(): self
4226
    {
4227 1
        return $this->filter(
4228
            static function ($item) {
4229 1
                if ($item === null) {
4230 1
                    return false;
4231
                }
4232
4233 1
                return (bool) \trim((string) $item);
4234 1
            }
4235
        );
4236
    }
4237
4238
    /**
4239
     * Swap two values between positions by key.
4240
     *
4241
     * @param int|string $swapA <p>a key in the array</p>
4242
     * @param int|string $swapB <p>a key in the array</p>
4243
     *
4244
     * @return static
4245
     *                <p>(Immutable)</p>
4246
     */
4247 1
    public function swap($swapA, $swapB): self
4248
    {
4249 1
        $array = $this->getArray();
4250
4251 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4252
4253 1
        return static::create(
4254 1
            $array,
4255 1
            $this->iteratorClass,
4256 1
            false
4257
        );
4258
    }
4259
4260
    /**
4261
     * alias: for "Arrayy->getArray()"
4262
     *
4263
     * @see Arrayy::getArray()
4264
     */
4265 202
    public function toArray()
4266
    {
4267 202
        return $this->getArray();
4268
    }
4269
4270
    /**
4271
     * Convert the current array to JSON.
4272
     *
4273
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4274
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4275
     *
4276
     * @return string
4277
     */
4278 6
    public function toJson(int $options = 0, int $depth = 512): string
4279
    {
4280 6
        $return = \json_encode($this->getArray(), $options, $depth);
4281 6
        if ($return === false) {
4282
            return '';
4283
        }
4284
4285 6
        return $return;
4286
    }
4287
4288
    /**
4289
     * Implodes array to a string with specified separator.
4290
     *
4291
     * @param string $separator [optional] <p>The element's separator.</p>
4292
     *
4293
     * @return string
4294
     *                <p>The string representation of array, separated by ",".</p>
4295
     */
4296 20
    public function toString(string $separator = ','): string
4297
    {
4298 20
        return $this->implode($separator);
4299
    }
4300
4301
    /**
4302
     * Return a duplicate free copy of the current array.
4303
     *
4304
     * @return static
4305
     *                <p>(Mutable)</p>
4306
     */
4307 12
    public function unique(): self
4308
    {
4309
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4310
4311 12
        $this->array = $this->reduce(
4312
            static function ($resultArray, $value) {
4313 11
                if (!\in_array($value, $resultArray, true)) {
4314 11
                    $resultArray[] = $value;
4315
                }
4316
4317 11
                return $resultArray;
4318 12
            },
4319 12
            []
4320 12
        )->getArray();
4321 12
        $this->generator = null;
4322
4323 12
        return $this;
4324
    }
4325
4326
    /**
4327
     * Return a duplicate free copy of the current array. (with the old keys)
4328
     *
4329
     * @return static
4330
     *                <p>(Mutable)</p>
4331
     */
4332 11
    public function uniqueKeepIndex(): self
4333
    {
4334
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4335
4336
        // init
4337 11
        $array = $this->getArray();
4338
4339 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...
4340 11
            \array_keys($array),
4341
            static function ($resultArray, $key) use ($array) {
4342 10
                if (!\in_array($array[$key], $resultArray, true)) {
4343 10
                    $resultArray[$key] = $array[$key];
4344
                }
4345
4346 10
                return $resultArray;
4347 11
            },
4348 11
            []
4349
        );
4350 11
        $this->generator = null;
4351
4352 11
        if ($this->array === null) {
4353
            $this->array = [];
4354
        } else {
4355 11
            $this->array = (array) $this->array;
4356
        }
4357
4358 11
        return $this;
4359
    }
4360
4361
    /**
4362
     * alias: for "Arrayy->unique()"
4363
     *
4364
     * @return static
4365
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4366
     *
4367
     * @see Arrayy::unique()
4368
     */
4369 10
    public function uniqueNewIndex(): self
4370
    {
4371 10
        return $this->unique();
4372
    }
4373
4374
    /**
4375
     * Prepends one or more values to the beginning of array at once.
4376
     *
4377
     * @return static
4378
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4379
     */
4380 4 View Code Duplication
    public function unshift(/* variadic arguments allowed */): 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...
4381
    {
4382 4
        $this->generatorToArray();
4383
4384 4
        if (\func_num_args()) {
4385 4
            $args = \func_get_args();
4386 4
            \array_unshift(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_unshift() as the parameter $array expects a reference.
Loading history...
4387
        }
4388
4389 4
        return $this;
4390
    }
4391
4392
    /**
4393
     * Get all values from a array.
4394
     *
4395
     * @return static
4396
     *                <p>(Immutable)</p>
4397
     */
4398 2
    public function values(): self
4399
    {
4400 2
        return static::create(
4401
            function () {
4402
                /** @noinspection YieldFromCanBeUsedInspection */
4403 2
                foreach ($this->getGenerator() as $value) {
4404 2
                    yield $value;
4405
                }
4406 2
            },
4407 2
            $this->iteratorClass,
4408 2
            false
4409
        );
4410
    }
4411
4412
    /**
4413
     * Apply the given function to every element in the array, discarding the results.
4414
     *
4415
     * @param callable $callable
4416
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4417
     *
4418
     * @return static
4419
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4420
     */
4421 11
    public function walk($callable, bool $recursive = false): self
4422
    {
4423 11
        $this->generatorToArray();
4424
4425 11
        if ($recursive === true) {
4426 6
            \array_walk_recursive($this->array, $callable);
4427
        } else {
4428 5
            \array_walk($this->array, $callable);
4429
        }
4430
4431 11
        return $this;
4432
    }
4433
4434
    /**
4435
     * Convert an array into a object.
4436
     *
4437
     * @param array $array PHP array
4438
     *
4439
     * @return \stdClass
4440
     */
4441 4
    protected static function arrayToObject(array $array = []): \stdClass
4442
    {
4443
        // init
4444 4
        $object = new \stdClass();
4445
4446 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4447 1
            return $object;
4448
        }
4449
4450 3
        foreach ($array as $name => $value) {
4451 3
            if (\is_array($value)) {
4452 1
                $object->{$name} = self::arrayToObject($value);
4453
            } else {
4454 3
                $object->{$name} = $value;
4455
            }
4456
        }
4457
4458 3
        return $object;
4459
    }
4460
4461
    /**
4462
     * @param array|\Generator|null $input        <p>
4463
     *                                            An array containing keys to return.
4464
     *                                            </p>
4465
     * @param mixed                 $search_value [optional] <p>
4466
     *                                            If specified, then only keys containing these values are returned.
4467
     *                                            </p>
4468
     * @param bool                  $strict       [optional] <p>
4469
     *                                            Determines if strict comparison (===) should be used during the
4470
     *                                            search.
4471
     *                                            </p>
4472
     *
4473
     * @return array
4474
     *               <p>an array of all the keys in input</p>
4475
     */
4476 10
    protected function array_keys_recursive(
4477
        $input = null,
4478
        $search_value = null,
4479
        bool $strict = true
4480
    ): array {
4481
        // init
4482 10
        $keys = [];
4483 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
4484
4485 10
        if ($input === null) {
4486
            $input = $this->getGenerator();
4487
        }
4488
4489 10
        foreach ($input as $key => $value) {
4490
            if (
4491 10
                $search_value === null
4492
                ||
4493
                (
4494
                    \is_array($search_value) === true
4495
                    &&
4496 10
                    \in_array($key, $search_value, $strict)
4497
                )
4498
            ) {
4499 10
                $keys[] = $key;
4500
            }
4501
4502
            // check if recursive is needed
4503 10
            if (\is_array($value) === true) {
4504 10
                $keysTmp[] = $this->array_keys_recursive($value);
4505
            }
4506
        }
4507
4508 10
        return \array_merge($keys, ...$keysTmp);
4509
    }
4510
4511
    /**
4512
     * @param mixed      $path
4513
     * @param callable   $callable
4514
     * @param array|null $currentOffset
4515
     */
4516 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4517
    {
4518 4
        $this->generatorToArray();
4519
4520 4
        if ($currentOffset === null) {
4521 4
            $currentOffset = &$this->array;
4522
        }
4523
4524 4
        $explodedPath = \explode($this->pathSeparator, $path);
4525 4
        if ($explodedPath === false) {
4526
            return;
4527
        }
4528
4529 4
        $nextPath = \array_shift($explodedPath);
4530
4531 4
        if (!isset($currentOffset[$nextPath])) {
4532
            return;
4533
        }
4534
4535 4
        if (!empty($explodedPath)) {
4536 1
            $this->callAtPath(
4537 1
                \implode($this->pathSeparator, $explodedPath),
4538 1
                $callable,
4539 1
                $currentOffset[$nextPath]
4540
            );
4541
        } else {
4542 4
            $callable($currentOffset[$nextPath]);
4543
        }
4544 4
    }
4545
4546
    /**
4547
     * create a fallback for array
4548
     *
4549
     * 1. use the current array, if it's a array
4550
     * 2. fallback to empty array, if there is nothing
4551
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4552
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4553
     * 5. call "__toArray()" on object, if the method exists
4554
     * 6. cast a string or object with "__toString()" into an array
4555
     * 7. throw a "InvalidArgumentException"-Exception
4556
     *
4557
     * @param mixed $data
4558
     *
4559
     * @throws \InvalidArgumentException
4560
     *
4561
     * @return array
4562
     */
4563 967
    protected function fallbackForArray(&$data): array
4564
    {
4565 967
        $data = $this->internalGetArray($data);
4566
4567 967
        if ($data === null) {
4568 2
            throw new \InvalidArgumentException(
4569 2
                'Passed value should be a array'
4570
            );
4571
        }
4572
4573 965
        return $data;
4574
    }
4575
4576
    /**
4577
     * Get correct PHP constant for direction.
4578
     *
4579
     * @param int|string $direction
4580
     *
4581
     * @return int
4582
     */
4583 39
    protected function getDirection($direction): int
4584
    {
4585 39
        if (\is_string($direction)) {
4586 10
            $direction = \strtolower($direction);
4587
4588 10
            if ($direction === 'desc') {
4589 2
                $direction = \SORT_DESC;
4590
            } else {
4591 8
                $direction = \SORT_ASC;
4592
            }
4593
        }
4594
4595
        if (
4596 39
            $direction !== \SORT_DESC
4597
            &&
4598 39
            $direction !== \SORT_ASC
4599
        ) {
4600
            $direction = \SORT_ASC;
4601
        }
4602
4603 39
        return $direction;
4604
    }
4605
4606
    /**
4607
     * @param mixed $glue
4608
     * @param mixed $pieces
4609
     * @param bool  $useKeys
4610
     *
4611
     * @return string
4612
     */
4613 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4614
    {
4615 36
        if ($pieces instanceof self) {
4616 1
            $pieces = $pieces->getArray();
4617
        }
4618
4619 36
        if (\is_array($pieces)) {
4620 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4621 36
            $pieces_count_not_zero = $pieces_count > 0;
4622
4623 36
            return \implode(
4624 36
                $glue,
4625 36
                \array_map(
4626 36
                    [$this, 'implode_recursive'],
4627 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4628 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4629
                )
4630
            );
4631
        }
4632
4633
        if (
4634 36
            \is_scalar($pieces)
4635
            ||
4636 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
4637
        ) {
4638 31
            return (string) $pieces;
4639
        }
4640
4641 9
        return '';
4642
    }
4643
4644
    /**
4645
     * @param mixed                 $needle   <p>
4646
     *                                        The searched value.
4647
     *                                        </p>
4648
     *                                        <p>
4649
     *                                        If needle is a string, the comparison is done
4650
     *                                        in a case-sensitive manner.
4651
     *                                        </p>
4652
     * @param array|\Generator|null $haystack <p>
4653
     *                                        The array.
4654
     *                                        </p>
4655
     * @param bool                  $strict   [optional] <p>
4656
     *                                        If the third parameter strict is set to true
4657
     *                                        then the in_array function will also check the
4658
     *                                        types of the
4659
     *                                        needle in the haystack.
4660
     *                                        </p>
4661
     *
4662
     * @return bool
4663
     *              <p>true if needle is found in the array, false otherwise</p>
4664
     */
4665 19
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4666
    {
4667 19
        if ($haystack === null) {
4668
            $haystack = $this->getGenerator();
4669
        }
4670
4671 19
        foreach ($haystack as $item) {
4672 15
            if (\is_array($item) === true) {
4673 4
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4674
            } else {
4675
                /** @noinspection NestedPositiveIfStatementsInspection */
4676 15
                if ($strict === true) {
4677 15
                    $returnTmp = $item === $needle;
4678
                } else {
4679
                    $returnTmp = $item == $needle;
4680
                }
4681
            }
4682
4683 15
            if ($returnTmp === true) {
4684 15
                return true;
4685
            }
4686
        }
4687
4688 8
        return false;
4689
    }
4690
4691
    /**
4692
     * @param mixed $data
4693
     *
4694
     * @return array|null
4695
     */
4696 967
    protected function internalGetArray(&$data)
4697
    {
4698 967
        if (\is_array($data)) {
4699 964
            return $data;
4700
        }
4701
4702 49
        if (!$data) {
4703 6
            return [];
4704
        }
4705
4706 48
        $isObject = \is_object($data);
4707
4708 48
        if ($isObject && $data instanceof self) {
4709 2
            return $data->getArray();
4710
        }
4711
4712 47
        if ($isObject && $data instanceof \ArrayObject) {
4713
            return $data->getArrayCopy();
4714
        }
4715
4716 47
        if ($isObject && $data instanceof \Generator) {
4717
            return static::createFromGeneratorImmutable($data)->getArray();
4718
        }
4719
4720 47
        if ($isObject && $data instanceof \Traversable) {
4721
            return static::createFromObject($data)->getArray();
4722
        }
4723
4724 47
        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...
4725
            return (array) $data->jsonSerialize();
4726
        }
4727
4728 47
        if (\is_callable($data)) {
4729 40
            $this->generator = new ArrayyRewindableGenerator($data);
4730
4731 40
            return [];
4732
        }
4733
4734 9
        if ($isObject && \method_exists($data, '__toArray')) {
4735
            return (array) $data->__toArray();
4736
        }
4737
4738 9
        if (\is_scalar($data)) {
4739 7
            return [$data];
4740
        }
4741
4742 2
        if ($isObject && \method_exists($data, '__toString')) {
4743
            return [(string) $data];
4744
        }
4745
4746 2
        return null;
4747
    }
4748
4749
    /**
4750
     * Internal mechanics of remove method.
4751
     *
4752
     * @param mixed $key
4753
     *
4754
     * @return bool
4755
     */
4756 18
    protected function internalRemove($key): bool
4757
    {
4758 18
        $this->generatorToArray();
4759
4760
        if (
4761 18
            $this->pathSeparator
4762
            &&
4763 18
            \is_string($key)
4764
            &&
4765 18
            \strpos($key, $this->pathSeparator) !== false
4766
        ) {
4767
            $path = \explode($this->pathSeparator, (string) $key);
4768
4769
            if ($path !== false) {
4770
                // crawl though the keys
4771
                while (\count($path, \COUNT_NORMAL) > 1) {
4772
                    $key = \array_shift($path);
4773
4774
                    if (!$this->has($key)) {
4775
                        return false;
4776
                    }
4777
4778
                    $this->array = &$this->array[$key];
4779
                }
4780
4781
                $key = \array_shift($path);
4782
            }
4783
        }
4784
4785 18
        unset($this->array[$key]);
4786
4787 18
        return true;
4788
    }
4789
4790
    /**
4791
     * Internal mechanic of set method.
4792
     *
4793
     * @param int|string|null $key
4794
     * @param mixed           $value
4795
     * @param bool            $checkProperties
4796
     *
4797
     * @return bool
4798
     */
4799 844
    protected function internalSet($key, $value, $checkProperties = true): bool
4800
    {
4801
        if (
4802 844
            $checkProperties === true
4803
            &&
4804 844
            $this->properties !== []
4805
        ) {
4806 13
            if (isset($this->properties[$key]) === false) {
4807
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4808
            }
4809
4810 13
            $this->properties[$key]->checkType($value);
4811
        }
4812
4813 843
        if ($key === null) {
4814
            return false;
4815
        }
4816
4817 843
        $this->generatorToArray();
4818
4819 843
        $array = &$this->array;
4820
4821
        if (
4822 843
            $this->pathSeparator
4823
            &&
4824 843
            \is_string($key)
4825
            &&
4826 843
            \strpos($key, $this->pathSeparator) !== false
4827
        ) {
4828 3
            $path = \explode($this->pathSeparator, (string) $key);
4829
4830 3
            if ($path !== false) {
4831
                // crawl through the keys
4832 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4833 3
                    $key = \array_shift($path);
4834
4835 3
                    $array = &$array[$key];
4836
                }
4837
4838 3
                $key = \array_shift($path);
4839
            }
4840
        }
4841
4842 843
        $array[$key] = $value;
4843
4844 843
        return true;
4845
    }
4846
4847
    /**
4848
     * Convert a object into an array.
4849
     *
4850
     * @param object $object
4851
     *
4852
     * @return mixed
4853
     */
4854 5
    protected static function objectToArray($object)
4855
    {
4856 5
        if (!\is_object($object)) {
4857 4
            return $object;
4858
        }
4859
4860 5
        if (\is_object($object)) {
4861 5
            $object = \get_object_vars($object);
4862
        }
4863
4864 5
        return \array_map(['static', 'objectToArray'], $object);
4865
    }
4866
4867
    /**
4868
     * sorting keys
4869
     *
4870
     * @param array      $elements
4871
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4872
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4873
     *                              <strong>SORT_NATURAL</strong></p>
4874
     *
4875
     * @return static
4876
     *                <p>(Mutable) Return this Arrayy object.</p>
4877
     */
4878 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4879
    {
4880 18
        $direction = $this->getDirection($direction);
4881
4882
        switch ($direction) {
4883 18
            case 'desc':
4884 18
            case \SORT_DESC:
4885 6
                \krsort($elements, $strategy);
4886
4887 6
                break;
4888 13
            case 'asc':
4889 13
            case \SORT_ASC:
4890
            default:
4891 13
                \ksort($elements, $strategy);
4892
        }
4893
4894 18
        return $this;
4895
    }
4896
4897
    /**
4898
     * @param array      $elements  <p>Warning: used as reference</p>
4899
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4900
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4901
     *                              <strong>SORT_NATURAL</strong></p>
4902
     * @param bool       $keepKeys
4903
     *
4904
     * @return static
4905
     *                <p>(Mutable) Return this Arrayy object.</p>
4906
     */
4907 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4908
    {
4909 20
        $direction = $this->getDirection($direction);
4910
4911 20
        if (!$strategy) {
4912 20
            $strategy = \SORT_REGULAR;
4913
        }
4914
4915
        switch ($direction) {
4916 20
            case 'desc':
4917 20
            case \SORT_DESC:
4918 9
                if ($keepKeys) {
4919 5
                    \arsort($elements, $strategy);
4920
                } else {
4921 4
                    \rsort($elements, $strategy);
4922
                }
4923
4924 9
                break;
4925 11
            case 'asc':
4926 11
            case \SORT_ASC:
4927
            default:
4928 11
                if ($keepKeys) {
4929 4
                    \asort($elements, $strategy);
4930
                } else {
4931 7
                    \sort($elements, $strategy);
4932
                }
4933
        }
4934
4935 20
        return $this;
4936
    }
4937
4938
    /**
4939
     * @return bool
4940
     */
4941 887
    private function generatorToArray(): bool
4942
    {
4943 887
        if ($this->generator) {
4944 1
            $this->array = $this->getArray();
4945 1
            $this->generator = null;
4946
4947 1
            return true;
4948
        }
4949
4950 887
        return false;
4951
    }
4952
4953
    /**
4954
     * @return Property[]
4955
     */
4956 15
    private function getPropertiesFromPhpDoc(): array
4957
    {
4958 15
        static $PROPERTY_CACHE = [];
4959 15
        $cacheKey = 'Class::' . static::class;
4960
4961 15
        if (isset($PROPERTY_CACHE[$cacheKey])) {
4962 14
            return $PROPERTY_CACHE[$cacheKey];
4963
        }
4964
4965
        // init
4966 2
        $properties = [];
4967
4968 2
        $reflector = new \ReflectionClass($this);
4969 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
4970 2
        $docComment = $reflector->getDocComment();
4971 2
        if ($docComment) {
4972 2
            $docblock = $factory->create($docComment);
4973 2
            foreach ($docblock->getTagsByName('property') as $tag) {
4974
                /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
4975 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
4976
            }
4977
        }
4978
4979 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
4980
    }
4981
}
4982