Completed
Push — master ( 9da4f3...caa7ee )
by Lars
02:50 queued 11s
created

Arrayy::setIteratorClass()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 7.456

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 1
dl 0
loc 19
ccs 4
cts 10
cp 0.4
crap 7.456
rs 9.6333
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 991
    public function __construct(
76
        $data = [],
77
        string $iteratorClass = ArrayyIterator::class,
78
        bool $checkForMissingPropertiesInConstructor = true
79
    ) {
80 991
        $data = $this->fallbackForArray($data);
81
82
        // used only for serialize + unserialize, all other methods are overwritten
83 989
        parent::__construct([], 0, $iteratorClass);
84
85 989
        $checkForMissingPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
86
                                                  &&
87 989
                                                  $checkForMissingPropertiesInConstructor === true;
88
89
        if (
90 989
            $this->checkPropertyTypes === true
91
            ||
92 989
            $checkForMissingPropertiesInConstructor === true
93
        ) {
94 15
            $this->properties = $this->getPropertiesFromPhpDoc();
95
        }
96
97
        if (
98 989
            $this->checkPropertiesMismatchInConstructor === true
99
            &&
100 989
            \count($data) !== 0
101
            &&
102 989
            \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 988
        foreach ($data as $key => &$value) {
109 860
            $this->internalSet(
110 860
                $key,
111 860
                $value,
112 860
                $checkForMissingPropertiesInConstructor
113
            );
114
        }
115
116 984
        $this->setIteratorClass($iteratorClass);
117 984
    }
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 61
    public function offsetExists($offset): bool
423
    {
424 61
        $this->generatorToArray();
425
426 61
        if ($this->array === []) {
427 5
            return false;
428
        }
429
430
        // php cast "bool"-index into "int"-index
431 56
        if (\is_bool($offset)) {
432 1
            $offset = (int) $offset;
433
        }
434
435 56
        $tmpReturn = $this->keyExists($offset);
436
437
        if (
438 56
            $tmpReturn === true
439
            ||
440
            (
441 21
                $tmpReturn === false
442
                &&
443 56
                \strpos((string) $offset, $this->pathSeparator) === false
444
            )
445
        ) {
446 54
            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 984
    public function setIteratorClass($class)
570
    {
571 984
        if (\class_exists($class)) {
572 984
            $this->iteratorClass = $class;
573
574 984
            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 22
    public function contains($value, bool $recursive = false, bool $strict = true): bool
907
    {
908 22
        if ($recursive === true) {
909 18
            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 580
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1097
    {
1098 580
        return new static(
1099 580
            $array,
1100 580
            $iteratorClass,
1101 580
            $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
        return new static($generatorFunction);
1133
    }
1134
1135
    /**
1136
     * Create an new instance filled with a copy of values from a "Generator"-object.
1137
     *
1138
     * @param \Generator $generator
1139
     *
1140
     * @return static
1141
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1142
     */
1143 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1144
    {
1145 4
        return new static(\iterator_to_array($generator, true));
1146
    }
1147
1148
    /**
1149
     * Create an new Arrayy object via JSON.
1150
     *
1151
     * @param string $json
1152
     *
1153
     * @return static
1154
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1155
     */
1156 5
    public static function createFromJson(string $json): self
1157
    {
1158 5
        return static::create(\json_decode($json, true));
1159
    }
1160
1161
    /**
1162
     * Create an new instance filled with values from an object that is iterable.
1163
     *
1164
     * @param \Traversable $object <p>iterable object</p>
1165
     *
1166
     * @return static
1167
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1168
     */
1169 4
    public static function createFromObject(\Traversable $object): self
1170
    {
1171
        // init
1172 4
        $array = new static();
1173
1174 4
        if ($object instanceof self) {
1175 4
            $objectArray = $object->getGenerator();
1176
        } else {
1177
            $objectArray = $object;
1178
        }
1179
1180 4
        foreach ($objectArray as $key => $value) {
1181 3
            $array[$key] = $value;
1182
        }
1183
1184 4
        return $array;
1185
    }
1186
1187
    /**
1188
     * Create an new instance filled with values from an object.
1189
     *
1190
     * @param object $object
1191
     *
1192
     * @return static
1193
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1194
     */
1195 5
    public static function createFromObjectVars($object): self
1196
    {
1197 5
        return new static(self::objectToArray($object));
1198
    }
1199
1200
    /**
1201
     * Create an new Arrayy object via string.
1202
     *
1203
     * @param string      $str       <p>The input string.</p>
1204
     * @param string|null $delimiter <p>The boundary string.</p>
1205
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1206
     *                               used.</p>
1207
     *
1208
     * @return static
1209
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1210
     */
1211 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1212
    {
1213 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...
1214 1
            \preg_match_all($regEx, $str, $array);
1215
1216 1
            if (!empty($array)) {
1217 1
                $array = $array[0];
1218
            }
1219
        } else {
1220
            /** @noinspection NestedPositiveIfStatementsInspection */
1221 9
            if ($delimiter !== null) {
1222 7
                $array = \explode($delimiter, $str);
1223
            } else {
1224 2
                $array = [$str];
1225
            }
1226
        }
1227
1228
        // trim all string in the array
1229 10
        \array_walk(
1230 10
            $array,
1231
            static function (&$val) {
1232 10
                if (\is_string($val)) {
1233 10
                    $val = \trim($val);
1234
                }
1235 10
            }
1236
        );
1237
1238 10
        return static::create($array);
1239
    }
1240
1241
    /**
1242
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1243
     *
1244
     * @param \Traversable $traversable
1245
     *
1246
     * @return static
1247
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1248
     */
1249 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1250
    {
1251 1
        return new static(\iterator_to_array($traversable, true));
1252
    }
1253
1254
    /**
1255
     * Create an new instance containing a range of elements.
1256
     *
1257
     * @param mixed $low  <p>First value of the sequence.</p>
1258
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1259
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1260
     *
1261
     * @return static
1262
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1263
     */
1264 2
    public static function createWithRange($low, $high, int $step = 1): self
1265
    {
1266 2
        return static::create(\range($low, $high, $step));
1267
    }
1268
1269
    /**
1270
     * Custom sort by index via "uksort".
1271
     *
1272
     * @see http://php.net/manual/en/function.uksort.php
1273
     *
1274
     * @param callable $function
1275
     *
1276
     * @throws \InvalidArgumentException
1277
     *
1278
     * @return static
1279
     *                <p>(Mutable) Return this Arrayy object.</p>
1280
     */
1281 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...
1282
    {
1283 5
        if (!\is_callable($function)) {
1284
            throw new \InvalidArgumentException(
1285
                'Passed function must be callable'
1286
            );
1287
        }
1288
1289 5
        $this->generatorToArray();
1290
1291 5
        \uksort($this->array, $function);
1292
1293 5
        return $this;
1294
    }
1295
1296
    /**
1297
     * Custom sort by value via "usort".
1298
     *
1299
     * @see http://php.net/manual/en/function.usort.php
1300
     *
1301
     * @param callable $function
1302
     *
1303
     * @throws \InvalidArgumentException
1304
     *
1305
     * @return static
1306
     *                <p>(Mutable) Return this Arrayy object.</p>
1307
     */
1308 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...
1309
    {
1310 5
        if (!\is_callable($function)) {
1311
            throw new \InvalidArgumentException(
1312
                'Passed function must be callable'
1313
            );
1314
        }
1315
1316 5
        $this->generatorToArray();
1317
1318 5
        \usort($this->array, $function);
1319
1320 5
        return $this;
1321
    }
1322
1323
    /**
1324
     * Delete the given key or keys.
1325
     *
1326
     * @param int|int[]|string|string[] $keyOrKeys
1327
     */
1328 4
    public function delete($keyOrKeys)
1329
    {
1330 4
        $keyOrKeys = (array) $keyOrKeys;
1331
1332 4
        foreach ($keyOrKeys as $key) {
1333 4
            $this->offsetUnset($key);
1334
        }
1335 4
    }
1336
1337
    /**
1338
     * Return values that are only in the current array.
1339
     *
1340
     * @param array $array
1341
     *
1342
     * @return static
1343
     *                <p>(Immutable)</p>
1344
     */
1345 12
    public function diff(array $array = []): self
1346
    {
1347 12
        return static::create(
1348 12
            \array_diff($this->getArray(), $array),
1349 12
            $this->iteratorClass,
1350 12
            false
1351
        );
1352
    }
1353
1354
    /**
1355
     * Return values that are only in the current array.
1356
     *
1357
     * @param array $array
1358
     *
1359
     * @return static
1360
     *                <p>(Immutable)</p>
1361
     */
1362 8
    public function diffKey(array $array = []): self
1363
    {
1364 8
        return static::create(
1365 8
            \array_diff_key($this->getArray(), $array),
1366 8
            $this->iteratorClass,
1367 8
            false
1368
        );
1369
    }
1370
1371
    /**
1372
     * Return values and Keys that are only in the current array.
1373
     *
1374
     * @param array $array
1375
     *
1376
     * @return static
1377
     *                <p>(Immutable)</p>
1378
     */
1379 8
    public function diffKeyAndValue(array $array = []): self
1380
    {
1381 8
        return static::create(
1382 8
            \array_diff_assoc($this->getArray(), $array),
1383 8
            $this->iteratorClass,
1384 8
            false
1385
        );
1386
    }
1387
1388
    /**
1389
     * Return values that are only in the current multi-dimensional array.
1390
     *
1391
     * @param array      $array
1392
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1393
     *
1394
     * @return static
1395
     *                <p>(Immutable)</p>
1396
     */
1397 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1398
    {
1399
        // init
1400 1
        $result = [];
1401
1402
        if (
1403 1
            $helperVariableForRecursion !== null
1404
            &&
1405 1
            \is_array($helperVariableForRecursion)
1406
        ) {
1407
            $arrayForTheLoop = $helperVariableForRecursion;
1408
        } else {
1409 1
            $arrayForTheLoop = $this->getGenerator();
1410
        }
1411
1412 1
        foreach ($arrayForTheLoop as $key => $value) {
1413 1
            if ($value instanceof self) {
1414
                $value = $value->getArray();
1415
            }
1416
1417 1
            if (\array_key_exists($key, $array)) {
1418 1
                if ($value !== $array[$key]) {
1419 1
                    $result[$key] = $value;
1420
                }
1421
            } else {
1422 1
                $result[$key] = $value;
1423
            }
1424
        }
1425
1426 1
        return static::create(
1427 1
            $result,
1428 1
            $this->iteratorClass,
1429 1
            false
1430
        );
1431
    }
1432
1433
    /**
1434
     * Return values that are only in the new $array.
1435
     *
1436
     * @param array $array
1437
     *
1438
     * @return static
1439
     *                <p>(Immutable)</p>
1440
     */
1441 8
    public function diffReverse(array $array = []): self
1442
    {
1443 8
        return static::create(
1444 8
            \array_diff($array, $this->getArray()),
1445 8
            $this->iteratorClass,
1446 8
            false
1447
        );
1448
    }
1449
1450
    /**
1451
     * Divide an array into two arrays. One with keys and the other with values.
1452
     *
1453
     * @return static
1454
     *                <p>(Immutable)</p>
1455
     */
1456 1
    public function divide(): self
1457
    {
1458 1
        return static::create(
1459
            [
1460 1
                $this->keys(),
1461 1
                $this->values(),
1462
            ],
1463 1
            $this->iteratorClass,
1464 1
            false
1465
        );
1466
    }
1467
1468
    /**
1469
     * Iterate over the current array and modify the array's value.
1470
     *
1471
     * @param \Closure $closure
1472
     *
1473
     * @return static
1474
     *                <p>(Immutable)</p>
1475
     */
1476 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...
1477
    {
1478
        // init
1479 4
        $array = [];
1480
1481 4
        foreach ($this->getGenerator() as $key => $value) {
1482 4
            $array[$key] = $closure($value, $key);
1483
        }
1484
1485 4
        return static::create(
1486 4
            $array,
1487 4
            $this->iteratorClass,
1488 4
            false
1489
        );
1490
    }
1491
1492
    /**
1493
     * Check if a value is in the current array using a closure.
1494
     *
1495
     * @param \Closure $closure
1496
     *
1497
     * @return bool
1498
     *              <p>Returns true if the given value is found, false otherwise.</p>
1499
     */
1500 4
    public function exists(\Closure $closure): bool
1501
    {
1502
        // init
1503 4
        $isExists = false;
1504
1505 4
        foreach ($this->getGenerator() as $key => $value) {
1506 3
            if ($closure($value, $key)) {
1507 1
                $isExists = true;
1508
1509 3
                break;
1510
            }
1511
        }
1512
1513 4
        return $isExists;
1514
    }
1515
1516
    /**
1517
     * Fill the array until "$num" with "$default" values.
1518
     *
1519
     * @param int   $num
1520
     * @param mixed $default
1521
     *
1522
     * @return static
1523
     *                <p>(Immutable)</p>
1524
     */
1525 8
    public function fillWithDefaults(int $num, $default = null): self
1526
    {
1527 8
        if ($num < 0) {
1528 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1529
        }
1530
1531 7
        $this->generatorToArray();
1532
1533 7
        $tmpArray = $this->array;
1534
1535 7
        $count = \count($tmpArray);
1536
1537 7
        while ($count < $num) {
1538 4
            $tmpArray[] = $default;
1539 4
            ++$count;
1540
        }
1541
1542 7
        return static::create(
1543 7
            $tmpArray,
1544 7
            $this->iteratorClass,
1545 7
            false
1546
        );
1547
    }
1548
1549
    /**
1550
     * Find all items in an array that pass the truth test.
1551
     *
1552
     * @param \Closure|null $closure [optional] <p>
1553
     *                               The callback function to use
1554
     *                               </p>
1555
     *                               <p>
1556
     *                               If no callback is supplied, all entries of
1557
     *                               input equal to false (see
1558
     *                               converting to
1559
     *                               boolean) will be removed.
1560
     *                               </p>
1561
     *                               * @param int $flag [optional] <p>
1562
     *                               Flag determining what arguments are sent to <i>callback</i>:
1563
     *                               </p><ul>
1564
     *                               <li>
1565
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1566
     *                               to <i>callback</i> instead of the value</span>
1567
     *                               </li>
1568
     *                               <li>
1569
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1570
     *                               arguments to <i>callback</i> instead of the value</span>
1571
     *                               </li>
1572
     *                               </ul>
1573
     *
1574
     * @return static
1575
     *                <p>(Immutable)</p>
1576
     */
1577 11
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1578
    {
1579 11
        if (!$closure) {
1580 1
            return $this->clean();
1581
        }
1582
1583 11
        return static::create(
1584 11
            \array_filter($this->getArray(), $closure, $flag),
1585 11
            $this->iteratorClass,
1586 11
            false
1587
        );
1588
    }
1589
1590
    /**
1591
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1592
     * property within that.
1593
     *
1594
     * @param string          $property
1595
     * @param string|string[] $value
1596
     * @param string          $comparisonOp
1597
     *                                      <p>
1598
     *                                      'eq' (equals),<br />
1599
     *                                      'gt' (greater),<br />
1600
     *                                      'gte' || 'ge' (greater or equals),<br />
1601
     *                                      'lt' (less),<br />
1602
     *                                      'lte' || 'le' (less or equals),<br />
1603
     *                                      'ne' (not equals),<br />
1604
     *                                      'contains',<br />
1605
     *                                      'notContains',<br />
1606
     *                                      'newer' (via strtotime),<br />
1607
     *                                      'older' (via strtotime),<br />
1608
     *                                      </p>
1609
     *
1610
     * @return static
1611
     *                <p>(Immutable)</p>
1612
     */
1613 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1614
    {
1615 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...
1616 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1617
        }
1618
1619
        $ops = [
1620
            'eq' => static function ($item, $prop, $value) {
1621 1
                return $item[$prop] === $value;
1622 1
            },
1623
            'gt' => static function ($item, $prop, $value) {
1624
                return $item[$prop] > $value;
1625 1
            },
1626
            'ge' => static function ($item, $prop, $value) {
1627
                return $item[$prop] >= $value;
1628 1
            },
1629
            'gte' => static function ($item, $prop, $value) {
1630
                return $item[$prop] >= $value;
1631 1
            },
1632
            'lt' => static function ($item, $prop, $value) {
1633 1
                return $item[$prop] < $value;
1634 1
            },
1635
            'le' => static function ($item, $prop, $value) {
1636
                return $item[$prop] <= $value;
1637 1
            },
1638
            'lte' => static function ($item, $prop, $value) {
1639
                return $item[$prop] <= $value;
1640 1
            },
1641
            'ne' => static function ($item, $prop, $value) {
1642
                return $item[$prop] !== $value;
1643 1
            },
1644
            'contains' => static function ($item, $prop, $value) {
1645 1
                return \in_array($item[$prop], (array) $value, true);
1646 1
            },
1647
            'notContains' => static function ($item, $prop, $value) {
1648
                return !\in_array($item[$prop], (array) $value, true);
1649 1
            },
1650
            'newer' => static function ($item, $prop, $value) {
1651
                return \strtotime($item[$prop]) > \strtotime($value);
1652 1
            },
1653
            'older' => static function ($item, $prop, $value) {
1654
                return \strtotime($item[$prop]) < \strtotime($value);
1655 1
            },
1656
        ];
1657
1658 1
        $result = \array_values(
1659 1
            \array_filter(
1660 1
                $this->getArray(),
1661
                static function ($item) use (
1662 1
                    $property,
1663 1
                    $value,
1664 1
                    $ops,
1665 1
                    $comparisonOp
1666
                ) {
1667 1
                    $item = (array) $item;
1668 1
                    $itemArrayy = new static($item);
1669 1
                    $item[$property] = $itemArrayy->get($property, []);
1670
1671 1
                    return $ops[$comparisonOp]($item, $property, $value);
1672 1
                }
1673
            )
1674
        );
1675
1676 1
        return static::create(
1677 1
            $result,
1678 1
            $this->iteratorClass,
1679 1
            false
1680
        );
1681
    }
1682
1683
    /**
1684
     * Find the first item in an array that passes the truth test,
1685
     *  otherwise return false
1686
     *
1687
     * @param \Closure $closure
1688
     *
1689
     * @return false|mixed
1690
     *                     <p>Return false if we did not find the value.</p>
1691
     */
1692 8
    public function find(\Closure $closure)
1693
    {
1694 8
        foreach ($this->getGenerator() as $key => $value) {
1695 6
            if ($closure($value, $key)) {
1696 6
                return $value;
1697
            }
1698
        }
1699
1700 3
        return false;
1701
    }
1702
1703
    /**
1704
     * find by ...
1705
     *
1706
     * @param string $property
1707
     * @param string $value
1708
     * @param string $comparisonOp
1709
     *
1710
     * @return static
1711
     *                <p>(Immutable)</p>
1712
     */
1713 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1714
    {
1715 1
        return $this->filterBy($property, $value, $comparisonOp);
1716
    }
1717
1718
    /**
1719
     * Get the first value from the current array.
1720
     *
1721
     * @return mixed
1722
     *               <p>Return null if there wasn't a element.</p>
1723
     */
1724 17
    public function first()
1725
    {
1726 17
        $key_first = $this->firstKey();
1727 17
        if ($key_first === null) {
1728 3
            return null;
1729
        }
1730
1731 14
        return $this->get($key_first);
1732
    }
1733
1734
    /**
1735
     * Move an array element to the first place.
1736
     *
1737
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
1738
     *       loss the keys of an indexed array.
1739
     *
1740
     * @param int|string $key
1741
     *
1742
     * @return static
1743
     *                <p>(Immutable)</p>
1744
     */
1745 1 View Code Duplication
    public function moveElementToFirstPlace($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1746
    {
1747 1
        $array = $this->getArray();
1748
1749 1
        if ($this->offsetExists($key)) {
1750 1
            $tmpValue = $this->get($key);
1751 1
            unset($array[$key]);
1752 1
            $array = [$key => $tmpValue] + $array;
1753
        }
1754
1755 1
        return static::create(
1756 1
            $array,
1757 1
            $this->iteratorClass,
1758 1
            false
1759
        );
1760
    }
1761
1762
    /**
1763
     * Move an array element to the last place.
1764
     *
1765
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
1766
     *       loss the keys of an indexed array.
1767
     *
1768
     * @param int|string $key
1769
     *
1770
     * @return static
1771
     *                <p>(Immutable)</p>
1772
     */
1773 1 View Code Duplication
    public function moveElementToLastPlace($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1774
    {
1775 1
        $array = $this->getArray();
1776
1777 1
        if ($this->offsetExists($key)) {
1778 1
            $tmpValue = $this->get($key);
1779 1
            unset($array[$key]);
1780 1
            $array += [$key => $tmpValue];
1781
        }
1782
1783 1
        return static::create(
1784 1
            $array,
1785 1
            $this->iteratorClass,
1786 1
            false
1787
        );
1788
    }
1789
1790
    /**
1791
     * Get the first key from the current array.
1792
     *
1793
     * @return mixed
1794
     *               <p>Return null if there wasn't a element.</p>
1795
     */
1796 24
    public function firstKey()
1797
    {
1798 24
        $this->generatorToArray();
1799
1800 24
        return \array_key_first($this->array);
1801
    }
1802
1803
    /**
1804
     * Get the most used value from the array.
1805
     *
1806
     * @return mixed
1807
     *               <p>Return null if there wasn't a element.</p>
1808
     */
1809 3
    public function mostUsedValue()
1810
    {
1811 3
        return $this->countValues()->arsort()->firstKey();
1812
    }
1813
1814
    /**
1815
     * Get the most used value from the array.
1816
     *
1817
     * @param int|null $number <p>How many values you will take?</p>
1818
     *
1819
     * @return static
1820
     *                <p>(Immutable)</p>
1821
     */
1822 3
    public function mostUsedValues(int $number = null): self
1823
    {
1824 3
        return $this->countValues()->arsort()->firstsKeys($number);
1825
    }
1826
1827
    /**
1828
     * Get the first value(s) from the current array.
1829
     * And will return an empty array if there was no first entry.
1830
     *
1831
     * @param int|null $number <p>How many values you will take?</p>
1832
     *
1833
     * @return static
1834
     *                <p>(Immutable)</p>
1835
     */
1836 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...
1837
    {
1838 3
        $arrayTmp = $this->keys()->getArray();
1839
1840 3
        if ($number === null) {
1841
            $array = (array) \array_shift($arrayTmp);
1842
        } else {
1843 3
            $number = (int) $number;
1844 3
            $array = \array_splice($arrayTmp, 0, $number);
1845
        }
1846
1847 3
        return static::create(
1848 3
            $array,
1849 3
            $this->iteratorClass,
1850 3
            false
1851
        );
1852
    }
1853
1854
    /**
1855
     * Get the first value(s) from the current array.
1856
     * And will return an empty array if there was no first entry.
1857
     *
1858
     * @param int|null $number <p>How many values you will take?</p>
1859
     *
1860
     * @return static
1861
     *                <p>(Immutable)</p>
1862
     */
1863 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...
1864
    {
1865 36
        $arrayTmp = $this->getArray();
1866
1867 36
        if ($number === null) {
1868 14
            $array = (array) \array_shift($arrayTmp);
1869
        } else {
1870 22
            $number = (int) $number;
1871 22
            $array = \array_splice($arrayTmp, 0, $number);
1872
        }
1873
1874 36
        return static::create(
1875 36
            $array,
1876 36
            $this->iteratorClass,
1877 36
            false
1878
        );
1879
    }
1880
1881
    /**
1882
     * Get and rmove the first value(s) from the current array.
1883
     * And will return an empty array if there was no first entry.
1884
     *
1885
     * @param int|null $number <p>How many values you will take?</p>
1886
     *
1887
     * @return static
1888
     *                <p>(Mutable)</p>
1889
     */
1890 34
    public function firstsMutable(int $number = null): self
1891
    {
1892 34
        $this->generatorToArray();
1893
1894 34
        if ($number === null) {
1895 19
            $this->array = (array) \array_shift($this->array);
1896
        } else {
1897 15
            $number = (int) $number;
1898 15
            $this->array = \array_splice($this->array, 0, $number);
1899
        }
1900
1901 34
        return $this;
1902
    }
1903
1904
    /**
1905
     * Exchanges all keys with their associated values in an array.
1906
     *
1907
     * @return static
1908
     *                <p>(Immutable)</p>
1909
     */
1910 1
    public function flip(): self
1911
    {
1912 1
        return static::create(
1913 1
            \array_flip($this->getArray()),
1914 1
            $this->iteratorClass,
1915 1
            false
1916
        );
1917
    }
1918
1919
    /**
1920
     * Get a value from an array (optional using dot-notation).
1921
     *
1922
     * @param mixed $key      <p>The key to look for.</p>
1923
     * @param mixed $fallback <p>Value to fallback to.</p>
1924
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1925
     *                        class.</p>
1926
     *
1927
     * @return mixed|static
1928
     */
1929 104
    public function get($key, $fallback = null, array $array = null)
1930
    {
1931 104
        if ($array !== null) {
1932 5
            $usedArray = $array;
1933
        } else {
1934 99
            $this->generatorToArray();
1935
1936 99
            $usedArray = $this->array;
1937
        }
1938
1939 104
        if ($key === null) {
1940 1
            return static::create(
1941 1
                $usedArray,
1942 1
                $this->iteratorClass,
1943 1
                false
1944
            );
1945
        }
1946
1947
        // php cast "bool"-index into "int"-index
1948 104
        if ((bool) $key === $key) {
1949 3
            $key = (int) $key;
1950
        }
1951
1952 104
        if (\array_key_exists($key, $usedArray) === true) {
1953 93
            if (\is_array($usedArray[$key])) {
1954 11
                return static::create(
1955 11
                    $usedArray[$key],
1956 11
                    $this->iteratorClass,
1957 11
                    false
1958
                );
1959
            }
1960
1961 84
            return $usedArray[$key];
1962
        }
1963
1964
        // crawl through array, get key according to object or not
1965 24
        $usePath = false;
1966
        if (
1967 24
            $this->pathSeparator
1968
            &&
1969 24
            \is_string($key)
1970
            &&
1971 24
            \strpos($key, $this->pathSeparator) !== false
1972
        ) {
1973 7
            $segments = \explode($this->pathSeparator, (string) $key);
1974 7
            if ($segments !== false) {
1975 7
                $usePath = true;
1976
1977 7
                foreach ($segments as $segment) {
1978
                    if (
1979
                        (
1980 7
                            \is_array($usedArray)
1981
                            ||
1982 7
                            $usedArray instanceof \ArrayAccess
1983
                        )
1984
                        &&
1985 7
                        isset($usedArray[$segment])
1986
                    ) {
1987 7
                        $usedArray = $usedArray[$segment];
1988
1989 7
                        continue;
1990
                    }
1991
1992
                    if (
1993 6
                        \is_object($usedArray)
1994
                        &&
1995 6
                        \property_exists($usedArray, $segment)
1996
                    ) {
1997 1
                        $usedArray = $usedArray->{$segment};
1998
1999 1
                        continue;
2000
                    }
2001
2002 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2003
                }
2004
            }
2005
        }
2006
2007 24
        if (!$usePath && !isset($usedArray[$key])) {
2008 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2009
        }
2010
2011 7
        if (\is_array($usedArray)) {
2012 1
            return static::create(
2013 1
                $usedArray,
2014 1
                $this->iteratorClass,
2015 1
                false
2016
            );
2017
        }
2018
2019 7
        return $usedArray;
2020
    }
2021
2022
    /**
2023
     * alias: for "Arrayy->getArray()"
2024
     *
2025
     * @return array
2026
     *
2027
     * @see Arrayy::getArray()
2028
     */
2029 1
    public function getAll(): array
2030
    {
2031 1
        return $this->getArray();
2032
    }
2033
2034
    /**
2035
     * Get the current array from the "Arrayy"-object.
2036
     *
2037
     * @return array
2038
     */
2039 799
    public function getArray(): array
2040
    {
2041
        // init
2042 799
        $array = [];
2043
2044 799
        foreach ($this->getGenerator() as $key => $value) {
2045 691
            $array[$key] = $value;
2046
        }
2047
2048 799
        return $array;
2049
    }
2050
2051
    /**
2052
     * Returns the values from a single column of the input array, identified by
2053
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2054
     *
2055
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2056
     * array by the values from the $indexKey column in the input array.
2057
     *
2058
     * @param mixed $columnKey
2059
     * @param mixed $indexKey
2060
     *
2061
     * @return static
2062
     *                <p>(Immutable)</p>
2063
     */
2064 1
    public function getColumn($columnKey = null, $indexKey = null): self
2065
    {
2066 1
        return static::create(
2067 1
            \array_column($this->getArray(), $columnKey, $indexKey),
2068 1
            $this->iteratorClass,
2069 1
            false
2070
        );
2071
    }
2072
2073
    /**
2074
     * Get the current array from the "Arrayy"-object as generator.
2075
     *
2076
     * @return \Generator
2077
     */
2078 875
    public function getGenerator(): \Generator
2079
    {
2080 875
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2081 39
            yield from $this->generator;
2082
        }
2083
2084 875
        yield from $this->array;
2085 832
    }
2086
2087
    /**
2088
     * alias: for "Arrayy->keys()"
2089
     *
2090
     * @return static
2091
     *                <p>(Immutable)</p>
2092
     *
2093
     * @see Arrayy::keys()
2094
     */
2095 2
    public function getKeys(): self
2096
    {
2097 2
        return $this->keys();
2098
    }
2099
2100
    /**
2101
     * Get the current array from the "Arrayy"-object as object.
2102
     *
2103
     * @return \stdClass
2104
     */
2105 4
    public function getObject(): \stdClass
2106
    {
2107 4
        return self::arrayToObject($this->getArray());
2108
    }
2109
2110
    /**
2111
     * alias: for "Arrayy->randomImmutable()"
2112
     *
2113
     * @return static
2114
     *                <p>(Immutable)</p>
2115
     *
2116
     * @see Arrayy::randomImmutable()
2117
     */
2118 4
    public function getRandom(): self
2119
    {
2120 4
        return $this->randomImmutable();
2121
    }
2122
2123
    /**
2124
     * alias: for "Arrayy->randomKey()"
2125
     *
2126
     * @return mixed
2127
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2128
     *
2129
     * @see Arrayy::randomKey()
2130
     */
2131 3
    public function getRandomKey()
2132
    {
2133 3
        return $this->randomKey();
2134
    }
2135
2136
    /**
2137
     * alias: for "Arrayy->randomKeys()"
2138
     *
2139
     * @param int $number
2140
     *
2141
     * @return static
2142
     *                <p>(Immutable)</p>
2143
     *
2144
     * @see Arrayy::randomKeys()
2145
     */
2146 8
    public function getRandomKeys(int $number): self
2147
    {
2148 8
        return $this->randomKeys($number);
2149
    }
2150
2151
    /**
2152
     * alias: for "Arrayy->randomValue()"
2153
     *
2154
     * @return mixed
2155
     *               <p>Get a random value or null if there wasn't a value.</p>
2156
     *
2157
     * @see Arrayy::randomValue()
2158
     */
2159 3
    public function getRandomValue()
2160
    {
2161 3
        return $this->randomValue();
2162
    }
2163
2164
    /**
2165
     * alias: for "Arrayy->randomValues()"
2166
     *
2167
     * @param int $number
2168
     *
2169
     * @return static
2170
     *                <p>(Immutable)</p>
2171
     *
2172
     * @see Arrayy::randomValues()
2173
     */
2174 6
    public function getRandomValues(int $number): self
2175
    {
2176 6
        return $this->randomValues($number);
2177
    }
2178
2179
    /**
2180
     * Group values from a array according to the results of a closure.
2181
     *
2182
     * @param callable|string $grouper  <p>A callable function name.</p>
2183
     * @param bool            $saveKeys
2184
     *
2185
     * @return static
2186
     *                <p>(Immutable)</p>
2187
     */
2188 4
    public function group($grouper, bool $saveKeys = false): self
2189
    {
2190
        // init
2191 4
        $result = [];
2192
2193
        // Iterate over values, group by property/results from closure.
2194 4
        foreach ($this->getGenerator() as $key => $value) {
2195 4
            if (\is_callable($grouper)) {
2196 3
                $groupKey = $grouper($value, $key);
2197
            } else {
2198 1
                $groupKey = $this->get($grouper, null, $this->getArray());
2199
            }
2200
2201 4
            $newValue = $this->get($groupKey, null, $result);
2202
2203 4
            if ($groupKey instanceof self) {
2204
                $groupKey = $groupKey->getArray();
2205
            }
2206
2207 4
            if ($newValue instanceof self) {
2208 4
                $newValue = $newValue->getArray();
2209
            }
2210
2211
            // Add to results.
2212 4
            if ($groupKey !== null) {
2213 3
                if ($saveKeys) {
2214 2
                    $result[$groupKey] = $newValue;
2215 2
                    $result[$groupKey][$key] = $value;
2216
                } else {
2217 1
                    $result[$groupKey] = $newValue;
2218 4
                    $result[$groupKey][] = $value;
2219
                }
2220
            }
2221
        }
2222
2223 4
        return static::create(
2224 4
            $result,
2225 4
            $this->iteratorClass,
2226 4
            false
2227
        );
2228
    }
2229
2230
    /**
2231
     * Check if an array has a given key.
2232
     *
2233
     * @param mixed $key
2234
     *
2235
     * @return bool
2236
     */
2237 23
    public function has($key): bool
2238
    {
2239 23
        static $UN_FOUND = null;
2240
2241 23
        if ($UN_FOUND === null) {
2242
            // Generate unique string to use as marker.
2243 1
            $UN_FOUND = \uniqid('arrayy', true);
2244
        }
2245
2246 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2247
    }
2248
2249
    /**
2250
     * Implodes the values of this array.
2251
     *
2252
     * @param string $glue
2253
     *
2254
     * @return string
2255
     */
2256 28
    public function implode(string $glue = ''): string
2257
    {
2258 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2259
    }
2260
2261
    /**
2262
     * Implodes the keys of this array.
2263
     *
2264
     * @param string $glue
2265
     *
2266
     * @return string
2267
     */
2268 8
    public function implodeKeys(string $glue = ''): string
2269
    {
2270 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2271
    }
2272
2273
    /**
2274
     * Given a list and an iterate-function that returns
2275
     * a key for each element in the list (or a property name),
2276
     * returns an object with an index of each item.
2277
     *
2278
     * @param mixed $key
2279
     *
2280
     * @return static
2281
     *                <p>(Immutable)</p>
2282
     */
2283 4
    public function indexBy($key): self
2284
    {
2285
        // init
2286 4
        $results = [];
2287
2288 4
        foreach ($this->getGenerator() as $a) {
2289 4
            if (\array_key_exists($key, $a) === true) {
2290 4
                $results[$a[$key]] = $a;
2291
            }
2292
        }
2293
2294 4
        return static::create(
2295 4
            $results,
2296 4
            $this->iteratorClass,
2297 4
            false
2298
        );
2299
    }
2300
2301
    /**
2302
     * alias: for "Arrayy->searchIndex()"
2303
     *
2304
     * @param mixed $value <p>The value to search for.</p>
2305
     *
2306
     * @return mixed
2307
     *
2308
     * @see Arrayy::searchIndex()
2309
     */
2310 4
    public function indexOf($value)
2311
    {
2312 4
        return $this->searchIndex($value);
2313
    }
2314
2315
    /**
2316
     * Get everything but the last..$to items.
2317
     *
2318
     * @param int $to
2319
     *
2320
     * @return static
2321
     *                <p>(Immutable)</p>
2322
     */
2323 12
    public function initial(int $to = 1): self
2324
    {
2325 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2326
    }
2327
2328
    /**
2329
     * Return an array with all elements found in input array.
2330
     *
2331
     * @param array $search
2332
     * @param bool  $keepKeys
2333
     *
2334
     * @return static
2335
     *                <p>(Immutable)</p>
2336
     */
2337 3
    public function intersection(array $search, bool $keepKeys = false): self
2338
    {
2339 3
        if ($keepKeys) {
2340 1
            return static::create(
2341 1
                \array_uintersect(
2342 1
                    $this->array,
2343 1
                    $search,
2344
                    static function ($a, $b) {
2345 1
                        return $a === $b ? 0 : -1;
2346 1
                    }
2347
                ),
2348 1
                $this->iteratorClass,
2349 1
                false
2350
            );
2351
        }
2352
2353 2
        return static::create(
2354 2
            \array_values(\array_intersect($this->getArray(), $search)),
2355 2
            $this->iteratorClass,
2356 2
            false
2357
        );
2358
    }
2359
2360
    /**
2361
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2362
     *
2363
     * @param array $search
2364
     *
2365
     * @return bool
2366
     */
2367 1
    public function intersects(array $search): bool
2368
    {
2369 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2370
    }
2371
2372
    /**
2373
     * Invoke a function on all of an array's values.
2374
     *
2375
     * @param callable $callable
2376
     * @param mixed    $arguments
2377
     *
2378
     * @return static
2379
     *                <p>(Immutable)</p>
2380
     */
2381 1
    public function invoke($callable, $arguments = []): self
2382
    {
2383
        // If one argument given for each iteration, create an array for it.
2384 1
        if (!\is_array($arguments)) {
2385 1
            $arguments = \array_fill(
2386 1
                0,
2387 1
                \count($this->getArray(), \COUNT_NORMAL),
2388 1
                $arguments
2389
            );
2390
        }
2391
2392
        // If the callable has arguments, pass them.
2393 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...
2394 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2395
        } else {
2396 1
            $array = $this->map($callable);
2397
        }
2398
2399 1
        return static::create(
2400 1
            $array,
2401 1
            $this->iteratorClass,
2402 1
            false
2403
        );
2404
    }
2405
2406
    /**
2407
     * Check whether array is associative or not.
2408
     *
2409
     * @param bool $recursive
2410
     *
2411
     * @return bool
2412
     *              <p>Returns true if associative, false otherwise.</p>
2413
     */
2414 15
    public function isAssoc(bool $recursive = false): bool
2415
    {
2416 15
        if ($this->isEmpty()) {
2417 3
            return false;
2418
        }
2419
2420 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2421 13
            if (!\is_string($key)) {
2422 13
                return false;
2423
            }
2424
        }
2425
2426 3
        return true;
2427
    }
2428
2429
    /**
2430
     * Check if a given key or keys are empty.
2431
     *
2432
     * @param int|int[]|string|string[]|null $keys
2433
     *
2434
     * @return bool
2435
     *              <p>Returns true if empty, false otherwise.</p>
2436
     */
2437 38
    public function isEmpty($keys = null): bool
2438
    {
2439 38
        if ($this->generator) {
2440
            return $this->getArray() === [];
2441
        }
2442
2443 38
        if ($keys === null) {
2444 38
            return $this->array === [];
2445
        }
2446
2447
        foreach ((array) $keys as $key) {
2448
            if (!empty($this->get($key))) {
2449
                return false;
2450
            }
2451
        }
2452
2453
        return true;
2454
    }
2455
2456
    /**
2457
     * Check if the current array is equal to the given "$array" or not.
2458
     *
2459
     * @param array $array
2460
     *
2461
     * @return bool
2462
     */
2463 1
    public function isEqual(array $array): bool
2464
    {
2465 1
        return $this->getArray() === $array;
2466
    }
2467
2468
    /**
2469
     * Check if the current array is a multi-array.
2470
     *
2471
     * @return bool
2472
     */
2473 22
    public function isMultiArray(): bool
2474
    {
2475
        return !(
2476 22
            \count($this->getArray(), \COUNT_NORMAL)
2477
            ===
2478 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2479
        );
2480
    }
2481
2482
    /**
2483
     * Check whether array is numeric or not.
2484
     *
2485
     * @return bool
2486
     *              <p>Returns true if numeric, false otherwise.</p>
2487
     */
2488 5
    public function isNumeric(): bool
2489
    {
2490 5
        if ($this->isEmpty()) {
2491 2
            return false;
2492
        }
2493
2494 4
        foreach ($this->keys()->getGenerator() as $key) {
2495 4
            if (!\is_int($key)) {
2496 4
                return false;
2497
            }
2498
        }
2499
2500 2
        return true;
2501
    }
2502
2503
    /**
2504
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2505
     *
2506
     * @param bool $recursive
2507
     *
2508
     * @return bool
2509
     */
2510 1
    public function isSequential(bool $recursive = false): bool
2511
    {
2512
2513
        // recursive
2514
2515 1
        if ($recursive === true) {
2516
            return $this->array_keys_recursive($this->getArray())
2517
                   ===
2518
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2519
        }
2520
2521
        // non recursive
2522
2523 1
        return \array_keys($this->getArray())
2524
               ===
2525 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2526
    }
2527
2528
    /**
2529
     * @return array
2530
     */
2531
    public function jsonSerialize(): array
2532
    {
2533
        return $this->getArray();
2534
    }
2535
2536
    /**
2537
     * Checks if the given key exists in the provided array.
2538
     *
2539
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2540
     *       then you need to use "Arrayy->offsetExists()".
2541
     *
2542
     * @param int|string $key the key to look for
2543
     *
2544
     * @return bool
2545
     */
2546 58
    public function keyExists($key): bool
2547
    {
2548 58
        return \array_key_exists($key, $this->array);
2549
    }
2550
2551
    /**
2552
     * Get all keys from the current array.
2553
     *
2554
     * @param bool       $recursive     [optional] <p>
2555
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
2556
     *                                  </p>
2557
     * @param mixed|null $search_values [optional] <p>
2558
     *                                  If specified, then only keys containing these values are returned.
2559
     *                                  </p>
2560
     * @param bool       $strict        [optional] <p>
2561
     *                                  Determines if strict comparison (===) should be used during the search.
2562
     *                                  </p>
2563
     *
2564
     * @return static
2565
     *                <p>(Immutable) An array of all the keys in input.</p>
2566
     */
2567 29
    public function keys(
2568
        bool $recursive = false,
2569
        $search_values = null,
2570
        bool $strict = true
2571
    ): self {
2572
2573
        // recursive
2574
2575 29
        if ($recursive === true) {
2576 4
            $array = $this->array_keys_recursive(
2577 4
                null,
2578 4
                $search_values,
2579 4
                $strict
2580
            );
2581
2582 4
            return static::create(
2583 4
                $array,
2584 4
                $this->iteratorClass,
2585 4
                false
2586
            );
2587
        }
2588
2589
        // non recursive
2590
2591 28
        if ($search_values === null) {
2592
            $arrayFunction = function () {
2593 28
                foreach ($this->getGenerator() as $key => $value) {
2594 26
                    yield $key;
2595
                }
2596 28
            };
2597
        } else {
2598
            $arrayFunction = function () use ($search_values, $strict) {
2599 1
                $is_array_tmp = \is_array($search_values);
2600
2601 1
                foreach ($this->getGenerator() as $key => $value) {
2602 View Code Duplication
                    if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2603
                        (
2604 1
                            $is_array_tmp === false
2605
                            &&
2606 1
                            $strict === true
2607
                            &&
2608 1
                            $search_values === $value
2609
                        )
2610
                        ||
2611
                        (
2612 1
                            $is_array_tmp === false
2613
                            &&
2614 1
                            $strict === false
2615
                            &&
2616 1
                            $search_values == $value
2617
                        )
2618
                        ||
2619
                        (
2620 1
                            $is_array_tmp === true
2621
                            &&
2622 1
                            \in_array($value, $search_values, $strict)
2623
                        )
2624
                    ) {
2625 1
                        yield $key;
2626
                    }
2627
                }
2628 1
            };
2629
        }
2630
2631 28
        return static::create(
2632 28
            $arrayFunction,
2633 28
            $this->iteratorClass,
2634 28
            false
2635
        );
2636
    }
2637
2638
    /**
2639
     * Sort an array by key in reverse order.
2640
     *
2641
     * @param int $sort_flags [optional] <p>
2642
     *                        You may modify the behavior of the sort using the optional
2643
     *                        parameter sort_flags, for details
2644
     *                        see sort.
2645
     *                        </p>
2646
     *
2647
     * @return static
2648
     *                <p>(Mutable) Return this Arrayy object.</p>
2649
     */
2650 4
    public function krsort(int $sort_flags = 0): self
2651
    {
2652 4
        $this->generatorToArray();
2653
2654 4
        \krsort($this->array, $sort_flags);
2655
2656 4
        return $this;
2657
    }
2658
2659
    /**
2660
     * Get the last value from the current array.
2661
     *
2662
     * @return mixed
2663
     *               <p>Return null if there wasn't a element.</p>
2664
     */
2665 16
    public function last()
2666
    {
2667 16
        $key_last = $this->lastKey();
2668 16
        if ($key_last === null) {
2669 2
            return null;
2670
        }
2671
2672 14
        return $this->get($key_last);
2673
    }
2674
2675
    /**
2676
     * Get the last key from the current array.
2677
     *
2678
     * @return mixed
2679
     *               <p>Return null if there wasn't a element.</p>
2680
     */
2681 20
    public function lastKey()
2682
    {
2683 20
        $this->generatorToArray();
2684
2685 20
        return \array_key_last($this->array);
2686
    }
2687
2688
    /**
2689
     * Get the last value(s) from the current array.
2690
     *
2691
     * @param int|null $number
2692
     *
2693
     * @return static
2694
     *                <p>(Immutable)</p>
2695
     */
2696 13
    public function lastsImmutable(int $number = null): self
2697
    {
2698 13
        if ($this->isEmpty()) {
2699 1
            return static::create(
2700 1
                [],
2701 1
                $this->iteratorClass,
2702 1
                false
2703
            );
2704
        }
2705
2706 12
        if ($number === null) {
2707 8
            $poppedValue = $this->last();
2708
2709 8
            if ($poppedValue === null) {
2710 1
                $poppedValue = [$poppedValue];
2711
            } else {
2712 7
                $poppedValue = (array) $poppedValue;
2713
            }
2714
2715 8
            $arrayy = static::create(
2716 8
                $poppedValue,
2717 8
                $this->iteratorClass,
2718 8
                false
2719
            );
2720
        } else {
2721 4
            $number = (int) $number;
2722 4
            $arrayy = $this->rest(-$number);
2723
        }
2724
2725 12
        return $arrayy;
2726
    }
2727
2728
    /**
2729
     * Get the last value(s) from the current array.
2730
     *
2731
     * @param int|null $number
2732
     *
2733
     * @return static
2734
     *                <p>(Mutable)</p>
2735
     */
2736 13
    public function lastsMutable(int $number = null): self
2737
    {
2738 13
        if ($this->isEmpty()) {
2739 1
            return $this;
2740
        }
2741
2742 12
        if ($number === null) {
2743 8
            $poppedValue = $this->last();
2744
2745 8
            if ($poppedValue === null) {
2746 1
                $poppedValue = [$poppedValue];
2747
            } else {
2748 7
                $poppedValue = (array) $poppedValue;
2749
            }
2750
2751 8
            $this->array = static::create(
2752 8
                $poppedValue,
2753 8
                $this->iteratorClass,
2754 8
                false
2755 8
            )->getArray();
2756
        } else {
2757 4
            $number = (int) $number;
2758 4
            $this->array = $this->rest(-$number)->getArray();
2759
        }
2760
2761 12
        $this->generator = null;
2762
2763 12
        return $this;
2764
    }
2765
2766
    /**
2767
     * Count the values from the current array.
2768
     *
2769
     * alias: for "Arrayy->count()"
2770
     *
2771
     * @param int $mode
2772
     *
2773
     * @return int
2774
     *
2775
     * @see Arrayy::count()
2776
     */
2777 20
    public function length(int $mode = \COUNT_NORMAL): int
2778
    {
2779 20
        return $this->count($mode);
2780
    }
2781
2782
    /**
2783
     * Apply the given function to the every element of the array,
2784
     * collecting the results.
2785
     *
2786
     * @param callable $callable
2787
     * @param bool     $useKeyAsSecondParameter
2788
     * @param mixed    ...$arguments
2789
     *
2790
     * @return static
2791
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2792
     */
2793 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2794
    {
2795 5
        $useArguments = \func_num_args() > 2;
2796
2797 5
        return static::create(
2798
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2799 5
                foreach ($this->getGenerator() as $key => $value) {
2800 4
                    if ($useArguments) {
2801 3
                        if ($useKeyAsSecondParameter) {
2802
                            yield $key => $callable($value, $key, ...$arguments);
2803
                        } else {
2804 3
                            yield $key => $callable($value, ...$arguments);
2805
                        }
2806
                    } else {
2807
                        /** @noinspection NestedPositiveIfStatementsInspection */
2808 4
                        if ($useKeyAsSecondParameter) {
2809
                            yield $key => $callable($value, $key);
2810
                        } else {
2811 4
                            yield $key => $callable($value);
2812
                        }
2813
                    }
2814
                }
2815 5
            },
2816 5
            $this->iteratorClass,
2817 5
            false
2818
        );
2819
    }
2820
2821
    /**
2822
     * Check if all items in current array match a truth test.
2823
     *
2824
     * @param \Closure $closure
2825
     *
2826
     * @return bool
2827
     */
2828 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...
2829
    {
2830 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2831 2
            return false;
2832
        }
2833
2834 13
        foreach ($this->getGenerator() as $key => $value) {
2835 13
            $value = $closure($value, $key);
2836
2837 13
            if ($value === false) {
2838 13
                return false;
2839
            }
2840
        }
2841
2842 7
        return true;
2843
    }
2844
2845
    /**
2846
     * Check if any item in the current array matches a truth test.
2847
     *
2848
     * @param \Closure $closure
2849
     *
2850
     * @return bool
2851
     */
2852 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...
2853
    {
2854 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2855 2
            return false;
2856
        }
2857
2858 12
        foreach ($this->getGenerator() as $key => $value) {
2859 12
            $value = $closure($value, $key);
2860
2861 12
            if ($value === true) {
2862 12
                return true;
2863
            }
2864
        }
2865
2866 4
        return false;
2867
    }
2868
2869
    /**
2870
     * Get the max value from an array.
2871
     *
2872
     * @return mixed
2873
     */
2874 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...
2875
    {
2876 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2877 1
            return false;
2878
        }
2879
2880 9
        return \max($this->getArray());
2881
    }
2882
2883
    /**
2884
     * Merge the new $array into the current array.
2885
     *
2886
     * - keep key,value from the current array, also if the index is in the new $array
2887
     *
2888
     * @param array $array
2889
     * @param bool  $recursive
2890
     *
2891
     * @return static
2892
     *                <p>(Immutable)</p>
2893
     */
2894 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...
2895
    {
2896 25
        if ($recursive === true) {
2897 4
            $result = \array_replace_recursive($this->getArray(), $array);
2898
        } else {
2899 21
            $result = \array_replace($this->getArray(), $array);
2900
        }
2901
2902 25
        return static::create(
2903 25
            $result,
2904 25
            $this->iteratorClass,
2905 25
            false
2906
        );
2907
    }
2908
2909
    /**
2910
     * Merge the new $array into the current array.
2911
     *
2912
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2913
     * - create new indexes
2914
     *
2915
     * @param array $array
2916
     * @param bool  $recursive
2917
     *
2918
     * @return static
2919
     *                <p>(Immutable)</p>
2920
     */
2921 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...
2922
    {
2923 16
        if ($recursive === true) {
2924 4
            $result = \array_merge_recursive($this->getArray(), $array);
2925
        } else {
2926 12
            $result = \array_merge($this->getArray(), $array);
2927
        }
2928
2929 16
        return static::create(
2930 16
            $result,
2931 16
            $this->iteratorClass,
2932 16
            false
2933
        );
2934
    }
2935
2936
    /**
2937
     * Merge the the current array into the $array.
2938
     *
2939
     * - use key,value from the new $array, also if the index is in the current array
2940
     *
2941
     * @param array $array
2942
     * @param bool  $recursive
2943
     *
2944
     * @return static
2945
     *                <p>(Immutable)</p>
2946
     */
2947 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...
2948
    {
2949 16
        if ($recursive === true) {
2950 4
            $result = \array_replace_recursive($array, $this->getArray());
2951
        } else {
2952 12
            $result = \array_replace($array, $this->getArray());
2953
        }
2954
2955 16
        return static::create(
2956 16
            $result,
2957 16
            $this->iteratorClass,
2958 16
            false
2959
        );
2960
    }
2961
2962
    /**
2963
     * Merge the current array into the new $array.
2964
     *
2965
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2966
     * - create new indexes
2967
     *
2968
     * @param array $array
2969
     * @param bool  $recursive
2970
     *
2971
     * @return static
2972
     *                <p>(Immutable)</p>
2973
     */
2974 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...
2975
    {
2976 17
        if ($recursive === true) {
2977 4
            $result = \array_merge_recursive($array, $this->getArray());
2978
        } else {
2979 13
            $result = \array_merge($array, $this->getArray());
2980
        }
2981
2982 17
        return static::create(
2983 17
            $result,
2984 17
            $this->iteratorClass,
2985 17
            false
2986
        );
2987
    }
2988
2989
    /**
2990
     * @return ArrayyMeta|static
2991
     */
2992 14
    public static function meta()
2993
    {
2994 14
        return (new ArrayyMeta())->getMetaObject(static::class);
2995
    }
2996
2997
    /**
2998
     * Get the min value from an array.
2999
     *
3000
     * @return mixed
3001
     */
3002 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...
3003
    {
3004 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
3005 1
            return false;
3006
        }
3007
3008 9
        return \min($this->getArray());
3009
    }
3010
3011
    /**
3012
     * Move an array element to a new index.
3013
     *
3014
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3015
     *
3016
     * @param int|string $from
3017
     * @param int        $to
3018
     *
3019
     * @return static
3020
     *                <p>(Immutable)</p>
3021
     */
3022 1
    public function moveElement($from, $to): self
3023
    {
3024 1
        $array = $this->getArray();
3025
3026 1
        if (\is_int($from)) {
3027 1
            $tmp = \array_splice($array, $from, 1);
3028 1
            \array_splice($array, (int) $to, 0, $tmp);
3029 1
            $output = $array;
3030 1
        } elseif (\is_string($from)) {
3031 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3032 1
            $itemToMove = $array[$from];
3033 1
            if ($indexToMove !== false) {
3034 1
                \array_splice($array, $indexToMove, 1);
3035
            }
3036 1
            $i = 0;
3037 1
            $output = [];
3038 1
            foreach ($array as $key => $item) {
3039 1
                if ($i === $to) {
3040 1
                    $output[$from] = $itemToMove;
3041
                }
3042 1
                $output[$key] = $item;
3043 1
                ++$i;
3044
            }
3045
        } else {
3046
            $output = [];
3047
        }
3048
3049 1
        return static::create(
3050 1
            $output,
3051 1
            $this->iteratorClass,
3052 1
            false
3053
        );
3054
    }
3055
3056
    /**
3057
     * Get a subset of the items from the given array.
3058
     *
3059
     * @param mixed[] $keys
3060
     *
3061
     * @return static
3062
     *                <p>(Immutable)</p>
3063
     */
3064
    public function only(array $keys): self
3065
    {
3066
        $array = $this->getArray();
3067
3068
        return static::create(
3069
            \array_intersect_key($array, \array_flip($keys)),
3070
            $this->iteratorClass,
3071
            false
3072
        );
3073
    }
3074
3075
    /**
3076
     * Pad array to the specified size with a given value.
3077
     *
3078
     * @param int   $size  <p>Size of the result array.</p>
3079
     * @param mixed $value <p>Empty value by default.</p>
3080
     *
3081
     * @return static
3082
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3083
     */
3084 4
    public function pad(int $size, $value): self
3085
    {
3086 4
        return static::create(
3087 4
            \array_pad($this->getArray(), $size, $value),
3088 4
            $this->iteratorClass,
3089 4
            false
3090
        );
3091
    }
3092
3093
    /**
3094
     * Pop a specified value off the end of the current array.
3095
     *
3096
     * @return mixed
3097
     *               <p>(Mutable) The popped element from the current array.</p>
3098
     */
3099 4
    public function pop()
3100
    {
3101 4
        $this->generatorToArray();
3102
3103 4
        return \array_pop($this->array);
3104
    }
3105
3106
    /**
3107
     * Prepend a (key) + value to the current array.
3108
     *
3109
     * @param mixed $value
3110
     * @param mixed $key
3111
     *
3112
     * @return static
3113
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3114
     */
3115 9
    public function prepend($value, $key = null): self
3116
    {
3117 9
        $this->generatorToArray();
3118
3119 9
        if ($key === null) {
3120 8
            \array_unshift($this->array, $value);
3121
        } else {
3122 2
            $this->array = [$key => $value] + $this->array;
3123
        }
3124
3125 9
        return $this;
3126
    }
3127
3128
    /**
3129
     * Add a suffix to each key.
3130
     *
3131
     * @param mixed $suffix
3132
     *
3133
     * @return static
3134
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3135
     */
3136 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...
3137
    {
3138
        // init
3139 10
        $result = [];
3140
3141 10
        foreach ($this->getGenerator() as $key => $item) {
3142 9
            if ($item instanceof self) {
3143
                $result[$key] = $item->prependToEachKey($suffix);
3144 9
            } elseif (\is_array($item)) {
3145
                $result[$key] = self::create(
3146
                    $item,
3147
                    $this->iteratorClass,
3148
                    false
3149
                )->prependToEachKey($suffix)
3150
                    ->toArray();
3151
            } else {
3152 9
                $result[$key . $suffix] = $item;
3153
            }
3154
        }
3155
3156 10
        return self::create(
3157 10
            $result,
3158 10
            $this->iteratorClass,
3159 10
            false
3160
        );
3161
    }
3162
3163
    /**
3164
     * Add a suffix to each value.
3165
     *
3166
     * @param mixed $suffix
3167
     *
3168
     * @return static
3169
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3170
     */
3171 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...
3172
    {
3173
        // init
3174 10
        $result = [];
3175
3176 10
        foreach ($this->getGenerator() as $key => $item) {
3177 9
            if ($item instanceof self) {
3178
                $result[$key] = $item->prependToEachValue($suffix);
3179 9
            } elseif (\is_array($item)) {
3180
                $result[$key] = self::create(
3181
                    $item,
3182
                    $this->iteratorClass,
3183
                    false
3184
                )->prependToEachValue($suffix)
3185
                    ->toArray();
3186 9
            } elseif (\is_object($item)) {
3187 1
                $result[$key] = $item;
3188
            } else {
3189 9
                $result[$key] = $item . $suffix;
3190
            }
3191
        }
3192
3193 10
        return self::create(
3194 10
            $result,
3195 10
            $this->iteratorClass,
3196 10
            false
3197
        );
3198
    }
3199
3200
    /**
3201
     * Return the value of a given key and
3202
     * delete the key.
3203
     *
3204
     * @param int|int[]|string|string[]|null $keyOrKeys
3205
     * @param mixed                          $fallback
3206
     *
3207
     * @return mixed
3208
     */
3209 1
    public function pull($keyOrKeys = null, $fallback = null)
3210
    {
3211 1
        if ($keyOrKeys === null) {
3212
            $array = $this->getArray();
3213
            $this->clear();
3214
3215
            return $array;
3216
        }
3217
3218 1
        if (\is_array($keyOrKeys)) {
3219 1
            $valueOrValues = [];
3220 1
            foreach ($keyOrKeys as $key) {
3221 1
                $valueOrValues[] = $this->get($key, $fallback);
3222 1
                $this->offsetUnset($key);
3223
            }
3224
        } else {
3225 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3226 1
            $this->offsetUnset($keyOrKeys);
3227
        }
3228
3229 1
        return $valueOrValues;
3230
    }
3231
3232
    /**
3233
     * Push one or more values onto the end of array at once.
3234
     *
3235
     * @return static
3236
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3237
     */
3238 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...
3239
    {
3240 4
        $this->generatorToArray();
3241
3242 4
        if (\func_num_args()) {
3243 4
            $args = \func_get_args();
3244 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...
3245
        }
3246
3247 4
        return $this;
3248
    }
3249
3250
    /**
3251
     * Get a random value from the current array.
3252
     *
3253
     * @param int|null $number <p>How many values you will take?</p>
3254
     *
3255
     * @return static
3256
     *                <p>(Immutable)</p>
3257
     */
3258 18
    public function randomImmutable(int $number = null): self
3259
    {
3260 18
        $this->generatorToArray();
3261
3262 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...
3263 1
            return static::create(
3264 1
                [],
3265 1
                $this->iteratorClass,
3266 1
                false
3267
            );
3268
        }
3269
3270 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...
3271
            /** @noinspection NonSecureArrayRandUsageInspection */
3272 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3273
3274 13
            return static::create(
3275 13
                $arrayRandValue,
3276 13
                $this->iteratorClass,
3277 13
                false
3278
            );
3279
        }
3280
3281 5
        $arrayTmp = $this->array;
3282
        /** @noinspection NonSecureShuffleUsageInspection */
3283 5
        \shuffle($arrayTmp);
3284
3285 5
        return static::create(
3286 5
            $arrayTmp,
3287 5
            $this->iteratorClass,
3288 5
            false
3289 5
        )->firstsImmutable($number);
3290
    }
3291
3292
    /**
3293
     * Pick a random key/index from the keys of this array.
3294
     *
3295
     * @throws \RangeException If array is empty
3296
     *
3297
     * @return mixed
3298
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3299
     */
3300 4
    public function randomKey()
3301
    {
3302 4
        $result = $this->randomKeys(1);
3303
3304 4
        if (!isset($result[0])) {
3305
            $result[0] = null;
3306
        }
3307
3308 4
        return $result[0];
3309
    }
3310
3311
    /**
3312
     * Pick a given number of random keys/indexes out of this array.
3313
     *
3314
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3315
     *
3316
     * @throws \RangeException If array is empty
3317
     *
3318
     * @return static
3319
     *                <p>(Immutable)</p>
3320
     */
3321 13
    public function randomKeys(int $number): self
3322
    {
3323 13
        $this->generatorToArray();
3324
3325 13
        $count = \count($this->array, \COUNT_NORMAL);
3326
3327 13
        if ($number === 0 || $number > $count) {
3328 2
            throw new \RangeException(
3329 2
                \sprintf(
3330 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3331 2
                    $number,
3332 2
                    $count
3333
                )
3334
            );
3335
        }
3336
3337 11
        $result = (array) \array_rand($this->array, $number);
3338
3339 11
        return static::create(
3340 11
            $result,
3341 11
            $this->iteratorClass,
3342 11
            false
3343
        );
3344
    }
3345
3346
    /**
3347
     * Get a random value from the current array.
3348
     *
3349
     * @param int|null $number <p>How many values you will take?</p>
3350
     *
3351
     * @return static
3352
     *                <p>(Mutable)</p>
3353
     */
3354 17
    public function randomMutable(int $number = null): self
3355
    {
3356 17
        $this->generatorToArray();
3357
3358 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...
3359
            return static::create(
3360
                [],
3361
                $this->iteratorClass,
3362
                false
3363
            );
3364
        }
3365
3366 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...
3367
            /** @noinspection NonSecureArrayRandUsageInspection */
3368 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3369 7
            $this->array = $arrayRandValue;
3370
3371 7
            return $this;
3372
        }
3373
3374
        /** @noinspection NonSecureShuffleUsageInspection */
3375 11
        \shuffle($this->array);
3376
3377 11
        return $this->firstsMutable($number);
3378
    }
3379
3380
    /**
3381
     * Pick a random value from the values of this array.
3382
     *
3383
     * @return mixed
3384
     *               <p>Get a random value or null if there wasn't a value.</p>
3385
     */
3386 4
    public function randomValue()
3387
    {
3388 4
        $result = $this->randomImmutable();
3389
3390 4
        if (!isset($result[0])) {
3391
            $result[0] = null;
3392
        }
3393
3394 4
        return $result[0];
3395
    }
3396
3397
    /**
3398
     * Pick a given number of random values out of this array.
3399
     *
3400
     * @param int $number
3401
     *
3402
     * @return static
3403
     *                <p>(Mutable)</p>
3404
     */
3405 7
    public function randomValues(int $number): self
3406
    {
3407 7
        return $this->randomMutable($number);
3408
    }
3409
3410
    /**
3411
     * Get a random value from an array, with the ability to skew the results.
3412
     *
3413
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3414
     *
3415
     * @param array    $array
3416
     * @param int|null $number <p>How many values you will take?</p>
3417
     *
3418
     * @return static
3419
     *                <p>(Immutable)</p>
3420
     */
3421 9
    public function randomWeighted(array $array, int $number = null): self
3422
    {
3423
        // init
3424 9
        $options = [];
3425
3426 9
        foreach ($array as $option => $weight) {
3427 9
            if ($this->searchIndex($option) !== false) {
3428 9
                for ($i = 0; $i < $weight; ++$i) {
3429 1
                    $options[] = $option;
3430
                }
3431
            }
3432
        }
3433
3434 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3435
    }
3436
3437
    /**
3438
     * Reduce the current array via callable e.g. anonymous-function.
3439
     *
3440
     * @param callable $callable
3441
     * @param array    $init
3442
     *
3443
     * @return static
3444
     *                <p>(Immutable)</p>
3445
     */
3446 16
    public function reduce($callable, array $init = []): self
3447
    {
3448 16
        if ($this->generator) {
3449 1
            $result = $init;
3450
3451 1
            foreach ($this->getGenerator() as $value) {
3452 1
                $result = $callable($result, $value);
3453
            }
3454
3455 1
            return static::create(
3456 1
                $result,
3457 1
                $this->iteratorClass,
3458 1
                false
3459
            );
3460
        }
3461
3462 16
        $result = \array_reduce($this->array, $callable, $init);
3463
3464 16
        if ($result === null) {
3465
            $this->array = [];
3466
        } else {
3467 16
            $this->array = (array) $result;
3468
        }
3469
3470 16
        return static::create(
3471 16
            $this->array,
3472 16
            $this->iteratorClass,
3473 16
            false
3474
        );
3475
    }
3476
3477
    /**
3478
     * @param bool $unique
3479
     *
3480
     * @return static
3481
     *                <p>(Immutable)</p>
3482
     */
3483 14
    public function reduce_dimension(bool $unique = true): self
3484
    {
3485
        // init
3486 14
        $result = [[]];
3487
3488 14
        foreach ($this->getGenerator() as $val) {
3489 12
            if (\is_array($val)) {
3490 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
3491
            } else {
3492 12
                $result[] = [$val];
3493
            }
3494
        }
3495 14
        $result = \array_merge(...$result);
3496
3497 14
        $resultArrayy = new self($result);
3498
3499 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3500
    }
3501
3502
    /**
3503
     * Create a numerically re-indexed Arrayy object.
3504
     *
3505
     * @return static
3506
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3507
     */
3508 9
    public function reindex(): self
3509
    {
3510 9
        $this->generatorToArray();
3511
3512 9
        $this->array = \array_values($this->array);
3513
3514 9
        return $this;
3515
    }
3516
3517
    /**
3518
     * Return all items that fail the truth test.
3519
     *
3520
     * @param \Closure $closure
3521
     *
3522
     * @return static
3523
     *                <p>(Immutable)</p>
3524
     */
3525 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...
3526
    {
3527
        // init
3528 1
        $filtered = [];
3529
3530 1
        foreach ($this->getGenerator() as $key => $value) {
3531 1
            if (!$closure($value, $key)) {
3532 1
                $filtered[$key] = $value;
3533
            }
3534
        }
3535
3536 1
        return static::create(
3537 1
            $filtered,
3538 1
            $this->iteratorClass,
3539 1
            false
3540
        );
3541
    }
3542
3543
    /**
3544
     * Remove a value from the current array (optional using dot-notation).
3545
     *
3546
     * @param mixed $key
3547
     *
3548
     * @return static
3549
     *                <p>(Mutable)</p>
3550
     */
3551 18
    public function remove($key): self
3552
    {
3553
        // recursive call
3554 18
        if (\is_array($key)) {
3555
            foreach ($key as $k) {
3556
                $this->internalRemove($k);
3557
            }
3558
3559
            return static::create(
3560
                $this->getArray(),
3561
                $this->iteratorClass,
3562
                false
3563
            );
3564
        }
3565
3566 18
        $this->internalRemove($key);
3567
3568 18
        return static::create(
3569 18
            $this->getArray(),
3570 18
            $this->iteratorClass,
3571 18
            false
3572
        );
3573
    }
3574
3575
    /**
3576
     * Remove the first value from the current array.
3577
     *
3578
     * @return static
3579
     *                <p>(Immutable)</p>
3580
     */
3581 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...
3582
    {
3583 7
        $tmpArray = $this->getArray();
3584
3585 7
        \array_shift($tmpArray);
3586
3587 7
        return static::create(
3588 7
            $tmpArray,
3589 7
            $this->iteratorClass,
3590 7
            false
3591
        );
3592
    }
3593
3594
    /**
3595
     * Remove the last value from the current array.
3596
     *
3597
     * @return static
3598
     *                <p>(Immutable)</p>
3599
     */
3600 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...
3601
    {
3602 7
        $tmpArray = $this->getArray();
3603
3604 7
        \array_pop($tmpArray);
3605
3606 7
        return static::create(
3607 7
            $tmpArray,
3608 7
            $this->iteratorClass,
3609 7
            false
3610
        );
3611
    }
3612
3613
    /**
3614
     * Removes a particular value from an array (numeric or associative).
3615
     *
3616
     * @param mixed $value
3617
     *
3618
     * @return static
3619
     *                <p>(Immutable)</p>
3620
     */
3621 7
    public function removeValue($value): self
3622
    {
3623 7
        $this->generatorToArray();
3624
3625
        // init
3626 7
        $isNumericArray = true;
3627
3628 7
        foreach ($this->getGenerator() as $key => $item) {
3629 6
            if ($item === $value) {
3630 6
                if (!\is_int($key)) {
3631
                    $isNumericArray = false;
3632
                }
3633 6
                unset($this->array[$key]);
3634
            }
3635
        }
3636
3637 7
        if ($isNumericArray) {
3638 7
            $this->array = \array_values($this->array);
3639
        }
3640
3641 7
        return static::create(
3642 7
            $this->array,
3643 7
            $this->iteratorClass,
3644 7
            false
3645
        );
3646
    }
3647
3648
    /**
3649
     * Generate array of repeated arrays.
3650
     *
3651
     * @param int $times <p>How many times has to be repeated.</p>
3652
     *
3653
     * @return static
3654
     *                <p>(Immutable)</p>
3655
     */
3656 1
    public function repeat($times): self
3657
    {
3658 1
        if ($times === 0) {
3659 1
            return new static();
3660
        }
3661
3662 1
        return static::create(
3663 1
            \array_fill(0, (int) $times, $this->getArray()),
3664 1
            $this->iteratorClass,
3665 1
            false
3666
        );
3667
    }
3668
3669
    /**
3670
     * Replace a key with a new key/value pair.
3671
     *
3672
     * @param mixed $replace
3673
     * @param mixed $key
3674
     * @param mixed $value
3675
     *
3676
     * @return static
3677
     *                <p>(Immutable)</p>
3678
     */
3679 2
    public function replace($replace, $key, $value): self
3680
    {
3681 2
        $that = clone $this;
3682
3683 2
        return $that->remove($replace)
3684 2
            ->set($key, $value);
3685
    }
3686
3687
    /**
3688
     * Create an array using the current array as values and the other array as keys.
3689
     *
3690
     * @param array $keys <p>An array of keys.</p>
3691
     *
3692
     * @return static
3693
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3694
     */
3695 2
    public function replaceAllKeys(array $keys): self
3696
    {
3697 2
        return static::create(
3698 2
            \array_combine($keys, $this->getArray()),
3699 2
            $this->iteratorClass,
3700 2
            false
3701
        );
3702
    }
3703
3704
    /**
3705
     * Create an array using the current array as keys and the other array as values.
3706
     *
3707
     * @param array $array <p>An array o values.</p>
3708
     *
3709
     * @return static
3710
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3711
     */
3712 2
    public function replaceAllValues(array $array): self
3713
    {
3714 2
        return static::create(
3715 2
            \array_combine($this->array, $array),
3716 2
            $this->iteratorClass,
3717 2
            false
3718
        );
3719
    }
3720
3721
    /**
3722
     * Replace the keys in an array with another set.
3723
     *
3724
     * @param array $keys <p>An array of keys matching the array's size</p>
3725
     *
3726
     * @return static
3727
     *                <p>(Immutable)</p>
3728
     */
3729 1
    public function replaceKeys(array $keys): self
3730
    {
3731 1
        $values = \array_values($this->getArray());
3732 1
        $result = \array_combine($keys, $values);
3733
3734 1
        return static::create(
3735 1
            $result,
3736 1
            $this->iteratorClass,
3737 1
            false
3738
        );
3739
    }
3740
3741
    /**
3742
     * Replace the first matched value in an array.
3743
     *
3744
     * @param mixed $search      <p>The value to replace.</p>
3745
     * @param mixed $replacement <p>The value to replace.</p>
3746
     *
3747
     * @return static
3748
     *                <p>(Immutable)</p>
3749
     */
3750 3
    public function replaceOneValue($search, $replacement = ''): self
3751
    {
3752 3
        $array = $this->getArray();
3753 3
        $key = \array_search($search, $array, true);
3754
3755 3
        if ($key !== false) {
3756 3
            $array[$key] = $replacement;
3757
        }
3758
3759 3
        return static::create(
3760 3
            $array,
3761 3
            $this->iteratorClass,
3762 3
            false
3763
        );
3764
    }
3765
3766
    /**
3767
     * Replace values in the current array.
3768
     *
3769
     * @param mixed $search      <p>The value to replace.</p>
3770
     * @param mixed $replacement <p>What to replace it with.</p>
3771
     *
3772
     * @return static
3773
     *                <p>(Immutable)</p>
3774
     */
3775 1
    public function replaceValues($search, $replacement = ''): self
3776
    {
3777 1
        return $this->each(
3778
            static function ($value) use ($search, $replacement) {
3779 1
                return \str_replace($search, $replacement, $value);
3780 1
            }
3781
        );
3782
    }
3783
3784
    /**
3785
     * Get the last elements from index $from until the end of this array.
3786
     *
3787
     * @param int $from
3788
     *
3789
     * @return static
3790
     *                <p>(Immutable)</p>
3791
     */
3792 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...
3793
    {
3794 15
        $tmpArray = $this->getArray();
3795
3796 15
        return static::create(
3797 15
            \array_splice($tmpArray, $from),
3798 15
            $this->iteratorClass,
3799 15
            false
3800
        );
3801
    }
3802
3803
    /**
3804
     * Return the array in the reverse order.
3805
     *
3806
     * @return static
3807
     *                <p>(Mutable) Return this Arrayy object.</p>
3808
     */
3809 8
    public function reverse(): self
3810
    {
3811 8
        $this->generatorToArray();
3812
3813 8
        $this->array = \array_reverse($this->array);
3814
3815 8
        return $this;
3816
    }
3817
3818
    /**
3819
     * Sort an array in reverse order.
3820
     *
3821
     * @param int $sort_flags [optional] <p>
3822
     *                        You may modify the behavior of the sort using the optional
3823
     *                        parameter sort_flags, for details
3824
     *                        see sort.
3825
     *                        </p>
3826
     *
3827
     * @return static
3828
     *                <p>(Mutable) Return this Arrayy object.</p>
3829
     */
3830 4
    public function rsort(int $sort_flags = 0): self
3831
    {
3832 4
        $this->generatorToArray();
3833
3834 4
        \rsort($this->array, $sort_flags);
3835
3836 4
        return $this;
3837
    }
3838
3839
    /**
3840
     * Search for the first index of the current array via $value.
3841
     *
3842
     * @param mixed $value
3843
     *
3844
     * @return false|float|int|string
3845
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3846
     */
3847 20
    public function searchIndex($value)
3848
    {
3849 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3850 19
            if ($value === $valueFromArray) {
3851 19
                return $keyFromArray;
3852
            }
3853
        }
3854
3855 11
        return false;
3856
    }
3857
3858
    /**
3859
     * Search for the value of the current array via $index.
3860
     *
3861
     * @param mixed $index
3862
     *
3863
     * @return static
3864
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3865
     */
3866 9
    public function searchValue($index): self
3867
    {
3868 9
        $this->generatorToArray();
3869
3870
        // init
3871 9
        $return = [];
3872
3873 9
        if ($this->array === []) {
3874
            return static::create(
3875
                [],
3876
                $this->iteratorClass,
3877
                false
3878
            );
3879
        }
3880
3881
        // php cast "bool"-index into "int"-index
3882 9
        if ((bool) $index === $index) {
3883 1
            $index = (int) $index;
3884
        }
3885
3886 9
        if ($this->offsetExists($index)) {
3887 7
            $return = [$this->array[$index]];
3888
        }
3889
3890 9
        return static::create(
3891 9
            $return,
3892 9
            $this->iteratorClass,
3893 9
            false
3894
        );
3895
    }
3896
3897
    /**
3898
     * Set a value for the current array (optional using dot-notation).
3899
     *
3900
     * @param string $key   <p>The key to set.</p>
3901
     * @param mixed  $value <p>Its value.</p>
3902
     *
3903
     * @return static
3904
     *                <p>(Mutable)</p>
3905
     */
3906 18
    public function set($key, $value): self
3907
    {
3908 18
        $this->generatorToArray();
3909
3910 18
        $this->internalSet($key, $value);
3911
3912 18
        return $this;
3913
    }
3914
3915
    /**
3916
     * Get a value from a array and set it if it was not.
3917
     *
3918
     * WARNING: this method only set the value, if the $key is not already set
3919
     *
3920
     * @param mixed $key      <p>The key</p>
3921
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3922
     *
3923
     * @return mixed
3924
     *               <p>(Mutable)</p>
3925
     */
3926 11
    public function setAndGet($key, $fallback = null)
3927
    {
3928 11
        $this->generatorToArray();
3929
3930
        // If the key doesn't exist, set it.
3931 11
        if (!$this->has($key)) {
3932 4
            $this->array = $this->set($key, $fallback)->getArray();
3933
        }
3934
3935 11
        return $this->get($key);
3936
    }
3937
3938
    /**
3939
     * Shifts a specified value off the beginning of array.
3940
     *
3941
     * @return mixed
3942
     *               <p>(Mutable) A shifted element from the current array.</p>
3943
     */
3944 4
    public function shift()
3945
    {
3946 4
        $this->generatorToArray();
3947
3948 4
        return \array_shift($this->array);
3949
    }
3950
3951
    /**
3952
     * Shuffle the current array.
3953
     *
3954
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3955
     * @param array $array  [optional]
3956
     *
3957
     * @return static
3958
     *                <p>(Immutable)</p>
3959
     */
3960 1
    public function shuffle(bool $secure = false, array $array = null): self
3961
    {
3962 1
        if ($array === null) {
3963 1
            $array = $this->getArray();
3964
        }
3965
3966 1
        if ($secure !== true) {
3967
            /** @noinspection NonSecureShuffleUsageInspection */
3968 1
            \shuffle($array);
3969
        } else {
3970 1
            $size = \count($array, \COUNT_NORMAL);
3971 1
            $keys = \array_keys($array);
3972 1
            for ($i = $size - 1; $i > 0; --$i) {
3973
                try {
3974 1
                    $r = \random_int(0, $i);
3975
                } catch (\Exception $e) {
3976
                    /** @noinspection RandomApiMigrationInspection */
3977
                    $r = \mt_rand(0, $i);
3978
                }
3979 1
                if ($r !== $i) {
3980
                    $temp = $array[$keys[$r]];
3981
                    $array[$keys[$r]] = $array[$keys[$i]];
3982
                    $array[$keys[$i]] = $temp;
3983
                }
3984
            }
3985
3986
            // reset indices
3987 1
            $array = \array_values($array);
3988
        }
3989
3990 1
        foreach ($array as $key => $value) {
3991
            // check if recursive is needed
3992 1
            if (\is_array($value) === true) {
3993 1
                $array[$key] = $this->shuffle($secure, $value);
3994
            }
3995
        }
3996
3997 1
        return static::create(
3998 1
            $array,
3999 1
            $this->iteratorClass,
4000 1
            false
4001
        );
4002
    }
4003
4004
    /**
4005
     * Count the values from the current array.
4006
     *
4007
     * alias: for "Arrayy->count()"
4008
     *
4009
     * @param int $mode
4010
     *
4011
     * @return int
4012
     */
4013 20
    public function size(int $mode = \COUNT_NORMAL): int
4014
    {
4015 20
        return $this->count($mode);
4016
    }
4017
4018
    /**
4019
     * Checks whether array has exactly $size items.
4020
     *
4021
     * @param int $size
4022
     *
4023
     * @return bool
4024
     */
4025 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...
4026
    {
4027
        // init
4028 1
        $itemsTempCount = 0;
4029
4030 1
        foreach ($this->getGenerator() as $key => $value) {
4031 1
            ++$itemsTempCount;
4032 1
            if ($itemsTempCount > $size) {
4033 1
                return false;
4034
            }
4035
        }
4036
4037 1
        return $itemsTempCount === $size;
4038
    }
4039
4040
    /**
4041
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4042
     * smaller than $fromSize.
4043
     *
4044
     * @param int $fromSize
4045
     * @param int $toSize
4046
     *
4047
     * @return bool
4048
     */
4049 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4050
    {
4051 1
        if ($fromSize > $toSize) {
4052 1
            $tmp = $toSize;
4053 1
            $toSize = $fromSize;
4054 1
            $fromSize = $tmp;
4055
        }
4056
4057
        // init
4058 1
        $itemsTempCount = 0;
4059
4060 1
        foreach ($this->getGenerator() as $key => $value) {
4061 1
            ++$itemsTempCount;
4062 1
            if ($itemsTempCount > $toSize) {
4063 1
                return false;
4064
            }
4065
        }
4066
4067 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4068
    }
4069
4070
    /**
4071
     * Checks whether array has more than $size items.
4072
     *
4073
     * @param int $size
4074
     *
4075
     * @return bool
4076
     */
4077 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...
4078
    {
4079
        // init
4080 1
        $itemsTempCount = 0;
4081
4082 1
        foreach ($this->getGenerator() as $key => $value) {
4083 1
            ++$itemsTempCount;
4084 1
            if ($itemsTempCount > $size) {
4085 1
                return true;
4086
            }
4087
        }
4088
4089 1
        return $itemsTempCount > $size;
4090
    }
4091
4092
    /**
4093
     * Checks whether array has less than $size items.
4094
     *
4095
     * @param int $size
4096
     *
4097
     * @return bool
4098
     */
4099 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...
4100
    {
4101
        // init
4102 1
        $itemsTempCount = 0;
4103
4104 1
        foreach ($this->getGenerator() as $key => $value) {
4105 1
            ++$itemsTempCount;
4106 1
            if ($itemsTempCount > $size) {
4107 1
                return false;
4108
            }
4109
        }
4110
4111 1
        return $itemsTempCount < $size;
4112
    }
4113
4114
    /**
4115
     * Counts all elements in an array, or something in an object.
4116
     *
4117
     * <p>
4118
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4119
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4120
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4121
     * implemented and used in PHP.
4122
     * </p>
4123
     *
4124
     * @return int
4125
     *             <p>
4126
     *             The number of elements in var, which is
4127
     *             typically an array, since anything else will have one
4128
     *             element.
4129
     *             </p>
4130
     *             <p>
4131
     *             If var is not an array or an object with
4132
     *             implemented Countable interface,
4133
     *             1 will be returned.
4134
     *             There is one exception, if var is &null;,
4135
     *             0 will be returned.
4136
     *             </p>
4137
     *             <p>
4138
     *             Caution: count may return 0 for a variable that isn't set,
4139
     *             but it may also return 0 for a variable that has been initialized with an
4140
     *             empty array. Use isset to test if a variable is set.
4141
     *             </p>
4142
     */
4143 10
    public function sizeRecursive(): int
4144
    {
4145 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4146
    }
4147
4148
    /**
4149
     * Extract a slice of the array.
4150
     *
4151
     * @param int      $offset       <p>Slice begin index.</p>
4152
     * @param int|null $length       <p>Length of the slice.</p>
4153
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4154
     *
4155
     * @return static
4156
     *                <p>A slice of the original array with length $length.</p>
4157
     */
4158 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
4159
    {
4160 4
        return static::create(
4161 4
            \array_slice(
4162 4
                $this->getArray(),
4163 4
                $offset,
4164 4
                $length,
4165 4
                $preserveKeys
4166
            ),
4167 4
            $this->iteratorClass,
4168 4
            false
4169
        );
4170
    }
4171
4172
    /**
4173
     * Sort the current array and optional you can keep the keys.
4174
     *
4175
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4176
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4177
     *                              <strong>SORT_NATURAL</strong></p>
4178
     * @param bool       $keepKeys
4179
     *
4180
     * @return static
4181
     *                <p>(Mutable) Return this Arrayy object.</p>
4182
     */
4183 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4184
    {
4185 20
        $this->generatorToArray();
4186
4187 20
        return $this->sorting(
4188 20
            $this->array,
4189 20
            $direction,
4190 20
            $strategy,
4191 20
            $keepKeys
4192
        );
4193
    }
4194
4195
    /**
4196
     * Sort the current array by key.
4197
     *
4198
     * @see http://php.net/manual/en/function.ksort.php
4199
     * @see http://php.net/manual/en/function.krsort.php
4200
     *
4201
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4202
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4203
     *                              <strong>SORT_NATURAL</strong></p>
4204
     *
4205
     * @return static
4206
     *                <p>(Mutable) Return this Arrayy object.</p>
4207
     */
4208 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4209
    {
4210 18
        $this->generatorToArray();
4211
4212 18
        $this->sorterKeys($this->array, $direction, $strategy);
4213
4214 18
        return $this;
4215
    }
4216
4217
    /**
4218
     * Sort the current array by value.
4219
     *
4220
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4221
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4222
     *                              <strong>SORT_NATURAL</strong></p>
4223
     *
4224
     * @return static
4225
     *                <p>(Mutable)</p>
4226
     */
4227 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4228
    {
4229 1
        return $this->sort($direction, $strategy, true);
4230
    }
4231
4232
    /**
4233
     * Sort the current array by value.
4234
     *
4235
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4236
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4237
     *                              <strong>SORT_NATURAL</strong></p>
4238
     *
4239
     * @return static
4240
     *                <p>(Mutable)</p>
4241
     */
4242 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4243
    {
4244 1
        return $this->sort($direction, $strategy, false);
4245
    }
4246
4247
    /**
4248
     * Sort a array by value, by a closure or by a property.
4249
     *
4250
     * - If the sorter is null, the array is sorted naturally.
4251
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4252
     *
4253
     * @param callable|string|null $sorter
4254
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4255
     *                                        <strong>SORT_DESC</strong></p>
4256
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4257
     *                                        <strong>SORT_NATURAL</strong></p>
4258
     *
4259
     * @return static
4260
     *                <p>(Immutable)</p>
4261
     */
4262 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4263
    {
4264 1
        $array = $this->getArray();
4265 1
        $direction = $this->getDirection($direction);
4266
4267
        // Transform all values into their results.
4268 1
        if ($sorter) {
4269 1
            $arrayy = static::create(
4270 1
                $array,
4271 1
                $this->iteratorClass,
4272 1
                false
4273
            );
4274
4275 1
            $results = $arrayy->each(
4276
                function ($value) use ($sorter) {
4277 1
                    if (\is_callable($sorter)) {
4278 1
                        return $sorter($value);
4279
                    }
4280
4281 1
                    return $this->get($sorter, null, $this->getArray());
4282 1
                }
4283
            );
4284
4285 1
            $results = $results->getArray();
4286
        } else {
4287 1
            $results = $array;
4288
        }
4289
4290
        // Sort by the results and replace by original values
4291 1
        \array_multisort($results, $direction, $strategy, $array);
4292
4293 1
        return static::create(
4294 1
            $array,
4295 1
            $this->iteratorClass,
4296 1
            false
4297
        );
4298
    }
4299
4300
    /**
4301
     * Split an array in the given amount of pieces.
4302
     *
4303
     * @param int  $numberOfPieces
4304
     * @param bool $keepKeys
4305
     *
4306
     * @return static
4307
     *                <p>(Immutable)</p>
4308
     */
4309 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4310
    {
4311 1
        $this->generatorToArray();
4312
4313 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4314
4315 1
        if ($arrayCount === 0) {
4316 1
            $result = [];
4317
        } else {
4318 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4319 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4320
        }
4321
4322 1
        return static::create(
4323 1
            $result,
4324 1
            $this->iteratorClass,
4325 1
            false
4326
        );
4327
    }
4328
4329
    /**
4330
     * Stripe all empty items.
4331
     *
4332
     * @return static
4333
     *                <p>(Immutable)</p>
4334
     */
4335 1
    public function stripEmpty(): self
4336
    {
4337 1
        return $this->filter(
4338
            static function ($item) {
4339 1
                if ($item === null) {
4340 1
                    return false;
4341
                }
4342
4343 1
                return (bool) \trim((string) $item);
4344 1
            }
4345
        );
4346
    }
4347
4348
    /**
4349
     * Swap two values between positions by key.
4350
     *
4351
     * @param int|string $swapA <p>a key in the array</p>
4352
     * @param int|string $swapB <p>a key in the array</p>
4353
     *
4354
     * @return static
4355
     *                <p>(Immutable)</p>
4356
     */
4357 1
    public function swap($swapA, $swapB): self
4358
    {
4359 1
        $array = $this->getArray();
4360
4361 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4362
4363 1
        return static::create(
4364 1
            $array,
4365 1
            $this->iteratorClass,
4366 1
            false
4367
        );
4368
    }
4369
4370
    /**
4371
     * alias: for "Arrayy->getArray()"
4372
     *
4373
     * @see Arrayy::getArray()
4374
     */
4375 205
    public function toArray()
4376
    {
4377 205
        return $this->getArray();
4378
    }
4379
4380
    /**
4381
     * Convert the current array to JSON.
4382
     *
4383
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4384
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4385
     *
4386
     * @return string
4387
     */
4388 6
    public function toJson(int $options = 0, int $depth = 512): string
4389
    {
4390 6
        $return = \json_encode($this->getArray(), $options, $depth);
4391 6
        if ($return === false) {
4392
            return '';
4393
        }
4394
4395 6
        return $return;
4396
    }
4397
4398
    /**
4399
     * Implodes array to a string with specified separator.
4400
     *
4401
     * @param string $separator [optional] <p>The element's separator.</p>
4402
     *
4403
     * @return string
4404
     *                <p>The string representation of array, separated by ",".</p>
4405
     */
4406 20
    public function toString(string $separator = ','): string
4407
    {
4408 20
        return $this->implode($separator);
4409
    }
4410
4411
    /**
4412
     * Return a duplicate free copy of the current array.
4413
     *
4414
     * @return static
4415
     *                <p>(Mutable)</p>
4416
     */
4417 12
    public function unique(): self
4418
    {
4419
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4420
4421 12
        $this->array = $this->reduce(
4422
            static function ($resultArray, $value) {
4423 11
                if (!\in_array($value, $resultArray, true)) {
4424 11
                    $resultArray[] = $value;
4425
                }
4426
4427 11
                return $resultArray;
4428 12
            },
4429 12
            []
4430 12
        )->getArray();
4431 12
        $this->generator = null;
4432
4433 12
        return $this;
4434
    }
4435
4436
    /**
4437
     * Return a duplicate free copy of the current array. (with the old keys)
4438
     *
4439
     * @return static
4440
     *                <p>(Mutable)</p>
4441
     */
4442 11
    public function uniqueKeepIndex(): self
4443
    {
4444
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4445
4446
        // init
4447 11
        $array = $this->getArray();
4448
4449 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...
4450 11
            \array_keys($array),
4451
            static function ($resultArray, $key) use ($array) {
4452 10
                if (!\in_array($array[$key], $resultArray, true)) {
4453 10
                    $resultArray[$key] = $array[$key];
4454
                }
4455
4456 10
                return $resultArray;
4457 11
            },
4458 11
            []
4459
        );
4460 11
        $this->generator = null;
4461
4462 11
        if ($this->array === null) {
4463
            $this->array = [];
4464
        } else {
4465 11
            $this->array = (array) $this->array;
4466
        }
4467
4468 11
        return $this;
4469
    }
4470
4471
    /**
4472
     * alias: for "Arrayy->unique()"
4473
     *
4474
     * @return static
4475
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4476
     *
4477
     * @see Arrayy::unique()
4478
     */
4479 10
    public function uniqueNewIndex(): self
4480
    {
4481 10
        return $this->unique();
4482
    }
4483
4484
    /**
4485
     * Prepends one or more values to the beginning of array at once.
4486
     *
4487
     * @return static
4488
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4489
     */
4490 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...
4491
    {
4492 4
        $this->generatorToArray();
4493
4494 4
        if (\func_num_args()) {
4495 4
            $args = \func_get_args();
4496 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...
4497
        }
4498
4499 4
        return $this;
4500
    }
4501
4502
    /**
4503
     * Get all values from a array.
4504
     *
4505
     * @return static
4506
     *                <p>(Immutable)</p>
4507
     */
4508 2
    public function values(): self
4509
    {
4510 2
        return static::create(
4511
            function () {
4512
                /** @noinspection YieldFromCanBeUsedInspection */
4513 2
                foreach ($this->getGenerator() as $value) {
4514 2
                    yield $value;
4515
                }
4516 2
            },
4517 2
            $this->iteratorClass,
4518 2
            false
4519
        );
4520
    }
4521
4522
    /**
4523
     * Apply the given function to every element in the array, discarding the results.
4524
     *
4525
     * @param callable $callable
4526
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4527
     *
4528
     * @return static
4529
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4530
     */
4531 11
    public function walk($callable, bool $recursive = false): self
4532
    {
4533 11
        $this->generatorToArray();
4534
4535 11
        if ($recursive === true) {
4536 6
            \array_walk_recursive($this->array, $callable);
4537
        } else {
4538 5
            \array_walk($this->array, $callable);
4539
        }
4540
4541 11
        return $this;
4542
    }
4543
4544
    /**
4545
     * Convert an array into a object.
4546
     *
4547
     * @param array $array PHP array
4548
     *
4549
     * @return \stdClass
4550
     */
4551 4
    protected static function arrayToObject(array $array = []): \stdClass
4552
    {
4553
        // init
4554 4
        $object = new \stdClass();
4555
4556 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4557 1
            return $object;
4558
        }
4559
4560 3
        foreach ($array as $name => $value) {
4561 3
            if (\is_array($value)) {
4562 1
                $object->{$name} = self::arrayToObject($value);
4563
            } else {
4564 3
                $object->{$name} = $value;
4565
            }
4566
        }
4567
4568 3
        return $object;
4569
    }
4570
4571
    /**
4572
     * @param array|\Generator|null $input         <p>
4573
     *                                             An array containing keys to return.
4574
     *                                             </p>
4575
     * @param mixed|null            $search_values [optional] <p>
4576
     *                                             If specified, then only keys containing these values are returned.
4577
     *                                             </p>
4578
     * @param bool                  $strict        [optional] <p>
4579
     *                                             Determines if strict comparison (===) should be used during the
4580
     *                                             search.
4581
     *                                             </p>
4582
     *
4583
     * @return array
4584
     *               <p>an array of all the keys in input</p>
4585
     */
4586 11
    protected function array_keys_recursive(
4587
        $input = null,
4588
        $search_values = null,
4589
        bool $strict = true
4590
    ): array {
4591
        // init
4592 11
        $keys = [];
4593 11
        $keysTmp = [];
4594
4595 11
        if ($input === null) {
4596 4
            $input = $this->getGenerator();
4597
        }
4598
4599 11
        if ($search_values === null) {
4600 11
            foreach ($input as $key => $value) {
4601 11
                $keys[] = $key;
4602
4603
                // check if recursive is needed
4604 11
                if (\is_array($value) === true) {
4605 11
                    $keysTmp[] = $this->array_keys_recursive($value);
4606
                }
4607
            }
4608
        } else {
4609 1
            $is_array_tmp = \is_array($search_values);
4610
4611 1
            foreach ($input as $key => $value) {
4612 View Code Duplication
                if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4613
                    (
4614 1
                        $is_array_tmp === false
4615
                        &&
4616 1
                        $strict === true
4617
                        &&
4618 1
                        $search_values === $value
4619
                    )
4620
                    ||
4621
                    (
4622 1
                        $is_array_tmp === false
4623
                        &&
4624 1
                        $strict === false
4625
                        &&
4626 1
                        $search_values == $value
4627
                    )
4628
                    ||
4629
                    (
4630 1
                        $is_array_tmp === true
4631
                        &&
4632 1
                        \in_array($value, $search_values, $strict)
4633
                    )
4634
                ) {
4635 1
                    $keys[] = $key;
4636
                }
4637
4638
                // check if recursive is needed
4639 1
                if (\is_array($value) === true) {
4640 1
                    $keysTmp[] = $this->array_keys_recursive($value);
4641
                }
4642
            }
4643
        }
4644
4645 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
4646
    }
4647
4648
    /**
4649
     * @param mixed      $path
4650
     * @param callable   $callable
4651
     * @param array|null $currentOffset
4652
     */
4653 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4654
    {
4655 4
        $this->generatorToArray();
4656
4657 4
        if ($currentOffset === null) {
4658 4
            $currentOffset = &$this->array;
4659
        }
4660
4661 4
        $explodedPath = \explode($this->pathSeparator, $path);
4662 4
        if ($explodedPath === false) {
4663
            return;
4664
        }
4665
4666 4
        $nextPath = \array_shift($explodedPath);
4667
4668 4
        if (!isset($currentOffset[$nextPath])) {
4669
            return;
4670
        }
4671
4672 4
        if (!empty($explodedPath)) {
4673 1
            $this->callAtPath(
4674 1
                \implode($this->pathSeparator, $explodedPath),
4675 1
                $callable,
4676 1
                $currentOffset[$nextPath]
4677
            );
4678
        } else {
4679 4
            $callable($currentOffset[$nextPath]);
4680
        }
4681 4
    }
4682
4683
    /**
4684
     * create a fallback for array
4685
     *
4686
     * 1. use the current array, if it's a array
4687
     * 2. fallback to empty array, if there is nothing
4688
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4689
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4690
     * 5. call "__toArray()" on object, if the method exists
4691
     * 6. cast a string or object with "__toString()" into an array
4692
     * 7. throw a "InvalidArgumentException"-Exception
4693
     *
4694
     * @param mixed $data
4695
     *
4696
     * @throws \InvalidArgumentException
4697
     *
4698
     * @return array
4699
     */
4700 991
    protected function fallbackForArray(&$data): array
4701
    {
4702 991
        $data = $this->internalGetArray($data);
4703
4704 991
        if ($data === null) {
4705 2
            throw new \InvalidArgumentException(
4706 2
                'Passed value should be a array'
4707
            );
4708
        }
4709
4710 989
        return $data;
4711
    }
4712
4713
    /**
4714
     * Get correct PHP constant for direction.
4715
     *
4716
     * @param int|string $direction
4717
     *
4718
     * @return int
4719
     */
4720 39
    protected function getDirection($direction): int
4721
    {
4722 39
        if (\is_string($direction)) {
4723 10
            $direction = \strtolower($direction);
4724
4725 10
            if ($direction === 'desc') {
4726 2
                $direction = \SORT_DESC;
4727
            } else {
4728 8
                $direction = \SORT_ASC;
4729
            }
4730
        }
4731
4732
        if (
4733 39
            $direction !== \SORT_DESC
4734
            &&
4735 39
            $direction !== \SORT_ASC
4736
        ) {
4737
            $direction = \SORT_ASC;
4738
        }
4739
4740 39
        return $direction;
4741
    }
4742
4743
    /**
4744
     * @param mixed $glue
4745
     * @param mixed $pieces
4746
     * @param bool  $useKeys
4747
     *
4748
     * @return string
4749
     */
4750 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4751
    {
4752 36
        if ($pieces instanceof self) {
4753 1
            $pieces = $pieces->getArray();
4754
        }
4755
4756 36
        if (\is_array($pieces)) {
4757 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4758 36
            $pieces_count_not_zero = $pieces_count > 0;
4759
4760 36
            return \implode(
4761 36
                $glue,
4762 36
                \array_map(
4763 36
                    [$this, 'implode_recursive'],
4764 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4765 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4766
                )
4767
            );
4768
        }
4769
4770
        if (
4771 36
            \is_scalar($pieces)
4772
            ||
4773 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
4774
        ) {
4775 31
            return (string) $pieces;
4776
        }
4777
4778 9
        return '';
4779
    }
4780
4781
    /**
4782
     * @param mixed                 $needle   <p>
4783
     *                                        The searched value.
4784
     *                                        </p>
4785
     *                                        <p>
4786
     *                                        If needle is a string, the comparison is done
4787
     *                                        in a case-sensitive manner.
4788
     *                                        </p>
4789
     * @param array|\Generator|null $haystack <p>
4790
     *                                        The array.
4791
     *                                        </p>
4792
     * @param bool                  $strict   [optional] <p>
4793
     *                                        If the third parameter strict is set to true
4794
     *                                        then the in_array function will also check the
4795
     *                                        types of the
4796
     *                                        needle in the haystack.
4797
     *                                        </p>
4798
     *
4799
     * @return bool
4800
     *              <p>true if needle is found in the array, false otherwise</p>
4801
     */
4802 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4803
    {
4804 18
        if ($haystack === null) {
4805
            $haystack = $this->getGenerator();
4806
        }
4807
4808 18
        foreach ($haystack as $item) {
4809 14
            if (\is_array($item) === true) {
4810 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4811
            } else {
4812
                /** @noinspection NestedPositiveIfStatementsInspection */
4813 14
                if ($strict === true) {
4814 14
                    $returnTmp = $item === $needle;
4815
                } else {
4816
                    $returnTmp = $item == $needle;
4817
                }
4818
            }
4819
4820 14
            if ($returnTmp === true) {
4821 14
                return true;
4822
            }
4823
        }
4824
4825 8
        return false;
4826
    }
4827
4828
    /**
4829
     * @param mixed $data
4830
     *
4831
     * @return array|null
4832
     */
4833 991
    protected function internalGetArray(&$data)
4834
    {
4835 991
        if (\is_array($data)) {
4836 988
            return $data;
4837
        }
4838
4839 49
        if (!$data) {
4840 6
            return [];
4841
        }
4842
4843 48
        $isObject = \is_object($data);
4844
4845 48
        if ($isObject && $data instanceof self) {
4846 2
            return $data->getArray();
4847
        }
4848
4849 47
        if ($isObject && $data instanceof \ArrayObject) {
4850
            return $data->getArrayCopy();
4851
        }
4852
4853 47
        if ($isObject && $data instanceof \Generator) {
4854
            return static::createFromGeneratorImmutable($data)->getArray();
4855
        }
4856
4857 47
        if ($isObject && $data instanceof \Traversable) {
4858
            return static::createFromObject($data)->getArray();
4859
        }
4860
4861 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...
4862
            return (array) $data->jsonSerialize();
4863
        }
4864
4865 47
        if (\is_callable($data)) {
4866 40
            $this->generator = new ArrayyRewindableGenerator($data);
4867
4868 40
            return [];
4869
        }
4870
4871 9
        if ($isObject && \method_exists($data, '__toArray')) {
4872
            return (array) $data->__toArray();
4873
        }
4874
4875 9
        if (\is_scalar($data)) {
4876 7
            return [$data];
4877
        }
4878
4879 2
        if ($isObject && \method_exists($data, '__toString')) {
4880
            return [(string) $data];
4881
        }
4882
4883 2
        return null;
4884
    }
4885
4886
    /**
4887
     * Internal mechanics of remove method.
4888
     *
4889
     * @param mixed $key
4890
     *
4891
     * @return bool
4892
     */
4893 18
    protected function internalRemove($key): bool
4894
    {
4895 18
        $this->generatorToArray();
4896
4897
        if (
4898 18
            $this->pathSeparator
4899
            &&
4900 18
            \is_string($key)
4901
            &&
4902 18
            \strpos($key, $this->pathSeparator) !== false
4903
        ) {
4904
            $path = \explode($this->pathSeparator, (string) $key);
4905
4906
            if ($path !== false) {
4907
                // crawl though the keys
4908
                while (\count($path, \COUNT_NORMAL) > 1) {
4909
                    $key = \array_shift($path);
4910
4911
                    if (!$this->has($key)) {
4912
                        return false;
4913
                    }
4914
4915
                    $this->array = &$this->array[$key];
4916
                }
4917
4918
                $key = \array_shift($path);
4919
            }
4920
        }
4921
4922 18
        unset($this->array[$key]);
4923
4924 18
        return true;
4925
    }
4926
4927
    /**
4928
     * Internal mechanic of set method.
4929
     *
4930
     * @param int|string|null $key
4931
     * @param mixed           $value
4932
     * @param bool            $checkProperties
4933
     *
4934
     * @return bool
4935
     */
4936 865
    protected function internalSet($key, $value, $checkProperties = true): bool
4937
    {
4938
        if (
4939 865
            $checkProperties === true
4940
            &&
4941 865
            $this->properties !== []
4942
        ) {
4943 13
            if (isset($this->properties[$key]) === false) {
4944
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4945
            }
4946
4947 13
            $this->properties[$key]->checkType($value);
4948
        }
4949
4950 864
        if ($key === null) {
4951
            return false;
4952
        }
4953
4954 864
        $this->generatorToArray();
4955
4956 864
        $array = &$this->array;
4957
4958
        if (
4959 864
            $this->pathSeparator
4960
            &&
4961 864
            \is_string($key)
4962
            &&
4963 864
            \strpos($key, $this->pathSeparator) !== false
4964
        ) {
4965 3
            $path = \explode($this->pathSeparator, (string) $key);
4966
4967 3
            if ($path !== false) {
4968
                // crawl through the keys
4969 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4970 3
                    $key = \array_shift($path);
4971
4972 3
                    $array = &$array[$key];
4973
                }
4974
4975 3
                $key = \array_shift($path);
4976
            }
4977
        }
4978
4979 864
        $array[$key] = $value;
4980
4981 864
        return true;
4982
    }
4983
4984
    /**
4985
     * Convert a object into an array.
4986
     *
4987
     * @param object $object
4988
     *
4989
     * @return mixed
4990
     */
4991 5
    protected static function objectToArray($object)
4992
    {
4993 5
        if (!\is_object($object)) {
4994 4
            return $object;
4995
        }
4996
4997 5
        if (\is_object($object)) {
4998 5
            $object = \get_object_vars($object);
4999
        }
5000
5001 5
        return \array_map(['static', 'objectToArray'], $object);
5002
    }
5003
5004
    /**
5005
     * sorting keys
5006
     *
5007
     * @param array      $elements
5008
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5009
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5010
     *                              <strong>SORT_NATURAL</strong></p>
5011
     *
5012
     * @return static
5013
     *                <p>(Mutable) Return this Arrayy object.</p>
5014
     */
5015 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5016
    {
5017 18
        $direction = $this->getDirection($direction);
5018
5019
        switch ($direction) {
5020 18
            case 'desc':
5021 18
            case \SORT_DESC:
5022 6
                \krsort($elements, $strategy);
5023
5024 6
                break;
5025 13
            case 'asc':
5026 13
            case \SORT_ASC:
5027
            default:
5028 13
                \ksort($elements, $strategy);
5029
        }
5030
5031 18
        return $this;
5032
    }
5033
5034
    /**
5035
     * @param array      $elements  <p>Warning: used as reference</p>
5036
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5037
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5038
     *                              <strong>SORT_NATURAL</strong></p>
5039
     * @param bool       $keepKeys
5040
     *
5041
     * @return static
5042
     *                <p>(Mutable) Return this Arrayy object.</p>
5043
     */
5044 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
5045
    {
5046 20
        $direction = $this->getDirection($direction);
5047
5048 20
        if (!$strategy) {
5049 20
            $strategy = \SORT_REGULAR;
5050
        }
5051
5052
        switch ($direction) {
5053 20
            case 'desc':
5054 20
            case \SORT_DESC:
5055 9
                if ($keepKeys) {
5056 5
                    \arsort($elements, $strategy);
5057
                } else {
5058 4
                    \rsort($elements, $strategy);
5059
                }
5060
5061 9
                break;
5062 11
            case 'asc':
5063 11
            case \SORT_ASC:
5064
            default:
5065 11
                if ($keepKeys) {
5066 4
                    \asort($elements, $strategy);
5067
                } else {
5068 7
                    \sort($elements, $strategy);
5069
                }
5070
        }
5071
5072 20
        return $this;
5073
    }
5074
5075
    /**
5076
     * @return bool
5077
     */
5078 909
    private function generatorToArray(): bool
5079
    {
5080 909
        if ($this->generator) {
5081 1
            $this->array = $this->getArray();
5082 1
            $this->generator = null;
5083
5084 1
            return true;
5085
        }
5086
5087 909
        return false;
5088
    }
5089
5090
    /**
5091
     * @return Property[]
5092
     */
5093 15
    private function getPropertiesFromPhpDoc(): array
5094
    {
5095 15
        static $PROPERTY_CACHE = [];
5096 15
        $cacheKey = 'Class::' . static::class;
5097
5098 15
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5099 14
            return $PROPERTY_CACHE[$cacheKey];
5100
        }
5101
5102
        // init
5103 2
        $properties = [];
5104
5105 2
        $reflector = new \ReflectionClass($this);
5106 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5107 2
        $docComment = $reflector->getDocComment();
5108 2
        if ($docComment) {
5109 2
            $docblock = $factory->create($docComment);
5110
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
5111 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5112 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
5113
            }
5114
        }
5115
5116 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5117
    }
5118
}
5119