Completed
Push — master ( e24a15...2102da )
by Lars
14:37
created

Arrayy::getArrayCopy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
/** @noinspection ClassReImplementsParentInterfaceInspection */
8
/** @noinspection PhpComposerExtensionStubsInspection */
9
10
/**
11
 * Methods to manage arrays.
12
 *
13
 * For the full copyright and license information, please view the LICENSE
14
 * file that was distributed with this source code.
15
 */
16
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
17
{
18
    /**
19
     * @var array
20
     */
21
    protected $array = [];
22
23
    /**
24
     * @var ArrayyRewindableGenerator|null
25
     */
26
    protected $generator;
27
28
    /**
29
     * @var string
30
     */
31
    protected $iteratorClass = ArrayyIterator::class;
32
33
    /**
34
     * @var string
35
     */
36
    protected $pathSeparator = '.';
37
38
    /**
39
     * @var bool
40
     */
41
    protected $checkPropertyTypes = false;
42
43
    /**
44
     * @var bool
45
     */
46
    protected $checkForMissingPropertiesInConstructor = false;
47
48
    /**
49
     * @var bool
50
     */
51
    protected $checkPropertiesMismatchInConstructor = false;
52
53
    /**
54
     * @var Property[]
55
     */
56
    protected $properties = [];
57
58
    /** @noinspection MagicMethodsValidityInspection */
59
    /** @noinspection PhpMissingParentConstructorInspection */
60
61
    /**
62
     * Initializes
63
     *
64
     * @param mixed  $data                                   <p>
65
     *                                                       Should be an array or a generator, otherwise it will try
66
     *                                                       to convert it into an array.
67
     *                                                       </p>
68
     * @param string $iteratorClass                          optional <p>
69
     *                                                       You can overwrite the ArrayyIterator, but mostly you don't
70
     *                                                       need this option.
71
     *                                                       </p>
72
     * @param bool   $checkForMissingPropertiesInConstructor optional <p>
73
     *                                                       You need to extend the "Arrayy"-class and you need to set
74
     *                                                       the $checkPropertiesMismatchInConstructor class property
75
     *                                                       to
76
     *                                                       true, otherwise this option didn't not work anyway.
77
     *                                                       </p>
78
     */
79 940
    public function __construct(
80
        $data = [],
81
        string $iteratorClass = ArrayyIterator::class,
82
        bool $checkForMissingPropertiesInConstructor = true
83
    ) {
84 940
        $data = $this->fallbackForArray($data);
85
86
        // used only for serialize + unserialize, all other methods are overwritten
87 938
        parent::__construct([], 0, $iteratorClass);
88
89 938
        $checkForMissingPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
90
                                                  &&
91 938
                                                  $checkForMissingPropertiesInConstructor === true;
92
93
        if (
94 938
            $this->checkPropertyTypes === true
95
            ||
96 938
            $checkForMissingPropertiesInConstructor === true
97
        ) {
98 15
            $this->properties = $this->getPropertiesFromPhpDoc();
99
        }
100
101
        if (
102 938
            $this->checkPropertiesMismatchInConstructor === true
103
            &&
104 938
            \count($data) !== 0
105
            &&
106 938
            \count(\array_diff_key($this->properties, $data)) > 0
107
        ) {
108 1
            throw new \InvalidArgumentException('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
109
        }
110
111
        /** @noinspection AlterInForeachInspection */
112 937
        foreach ($data as $key => &$value) {
113 818
            $this->internalSet(
114 818
                $key,
115 818
                $value,
116 818
                $checkForMissingPropertiesInConstructor
117
            );
118
        }
119
120 933
        $this->setIteratorClass($iteratorClass);
121 933
    }
122
123
    /**
124
     * Call object as function.
125
     *
126
     * @param mixed $key
127
     *
128
     * @return mixed
129
     */
130 1
    public function __invoke($key = null)
131
    {
132 1
        if ($key !== null) {
133 1
            $this->generatorToArray();
134
135 1
            return $this->array[$key] ?? false;
136
        }
137
138
        return $this->getArray();
139
    }
140
141
    /**
142
     * Whether or not an element exists by key.
143
     *
144
     * @param mixed $key
145
     *
146
     * @return bool
147
     *              <p>True is the key/index exists, otherwise false.</p>
148
     */
149
    public function __isset($key): bool
150
    {
151
        return $this->offsetExists($key);
152
    }
153
154
    /**
155
     * Assigns a value to the specified element.
156
     *
157
     * @param mixed $key
158
     * @param mixed $value
159
     */
160 2
    public function __set($key, $value)
161
    {
162 2
        $this->internalSet($key, $value);
163 2
    }
164
165
    /**
166
     * magic to string
167
     *
168
     * @return string
169
     */
170 15
    public function __toString(): string
171
    {
172 15
        return $this->toString();
173
    }
174
175
    /**
176
     * Unset element by key.
177
     *
178
     * @param mixed $key
179
     */
180
    public function __unset($key)
181
    {
182
        $this->internalRemove($key);
183
    }
184
185
    /**
186
     * Get a value by key.
187
     *
188
     * @param mixed $key
189
     *
190
     * @return mixed
191
     *               <p>Get a Value from the current array.</p>
192
     */
193 4
    public function &__get($key)
194
    {
195 4
        $return = $this->get($key);
196
197 4
        if (\is_array($return)) {
198
            return static::create($return, $this->iteratorClass, false);
199
        }
200
201 4
        return $return;
202
    }
203
204
    /**
205
     * alias: for "Arrayy->append()"
206
     *
207
     * @param mixed $value
208
     *
209
     * @return static
210
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
211
     *
212
     * @see Arrayy::append()
213
     */
214 3
    public function add($value): self
215
    {
216 3
        return $this->append($value);
217
    }
218
219
    /**
220
     * Append a (key) + value to the current array.
221
     *
222
     * @param mixed $value
223
     * @param mixed $key
224
     *
225
     * @return static
226
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
227
     */
228 11
    public function append($value, $key = null): self
229
    {
230 11
        $this->generatorToArray();
231
232 11
        if ($key !== null) {
233
            if (
234
                isset($this->array[$key])
235
                &&
236
                \is_array($this->array[$key])
237
            ) {
238
                $this->array[$key][] = $value;
239
            } else {
240
                $this->array[$key] = $value;
241
            }
242
        } else {
243 11
            $this->array[] = $value;
244
        }
245
246 11
        return $this;
247
    }
248
249
    /**
250
     * Sort the entries by value.
251
     *
252
     * @param int $sort_flags [optional] <p>
253
     *                        You may modify the behavior of the sort using the optional
254
     *                        parameter sort_flags, for details
255
     *                        see sort.
256
     *                        </p>
257
     *
258
     * @return static
259
     *                <p>(Mutable) Return this Arrayy object.</p>
260
     */
261 4
    public function asort(int $sort_flags = 0): self
262
    {
263 4
        $this->generatorToArray();
264
265 4
        \asort($this->array, $sort_flags);
266
267 4
        return $this;
268
    }
269
270
    /**
271
     * Counts all elements in an array, or something in an object.
272
     *
273
     * <p>
274
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
275
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
276
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
277
     * implemented and used in PHP.
278
     * </p>
279
     *
280
     * @see http://php.net/manual/en/function.count.php
281
     *
282
     * @param int $mode [optional] If the optional mode parameter is set to
283
     *                  COUNT_RECURSIVE (or 1), count
284
     *                  will recursively count the array. This is particularly useful for
285
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
286
     *
287
     * @return int
288
     *             <p>
289
     *             The number of elements in var, which is
290
     *             typically an array, since anything else will have one
291
     *             element.
292
     *             </p>
293
     *             <p>
294
     *             If var is not an array or an object with
295
     *             implemented Countable interface,
296
     *             1 will be returned.
297
     *             There is one exception, if var is &null;,
298
     *             0 will be returned.
299
     *             </p>
300
     *             <p>
301
     *             Caution: count may return 0 for a variable that isn't set,
302
     *             but it may also return 0 for a variable that has been initialized with an
303
     *             empty array. Use isset to test if a variable is set.
304
     *             </p>
305
     */
306 46
    public function count(int $mode = \COUNT_NORMAL): int
307
    {
308 46
        return \count($this->getArray(), $mode);
309
    }
310
311
    /**
312
     * Exchange the array for another one.
313
     *
314
     * @param array|static $data
315
     *
316
     * @return array
317
     */
318 1
    public function exchangeArray($data): array
319
    {
320 1
        $this->array = $this->fallbackForArray($data);
321
322 1
        return $this->array;
323
    }
324
325
    /**
326
     * Creates a copy of the ArrayyObject.
327
     *
328
     * @return array
329
     */
330 1
    public function getArrayCopy(): array
331
    {
332 1
        $this->generatorToArray();
333
334 1
        return $this->array;
335
    }
336
337
    /**
338
     * Returns a new iterator, thus implementing the \Iterator interface.
339
     *
340
     * @return \Iterator
341
     *                   <p>An iterator for the values in the array.</p>
342
     */
343 23
    public function getIterator(): \Iterator
344
    {
345 23
        if ($this->generator instanceof ArrayyRewindableGenerator) {
346 1
            return $this->generator;
347
        }
348
349 22
        $iterator = $this->getIteratorClass();
350
351 22
        if ($iterator === ArrayyIterator::class) {
352 22
            return new $iterator($this->getArray(), 0, static::class);
353
        }
354
355
        return new $iterator($this->getArray());
356
    }
357
358
    /**
359
     * Gets the iterator classname for the ArrayObject.
360
     *
361
     * @return string
362
     */
363 22
    public function getIteratorClass(): string
364
    {
365 22
        return $this->iteratorClass;
366
    }
367
368
    /**
369
     * Sort the entries by key
370
     *
371
     * @param int $sort_flags [optional] <p>
372
     *                        You may modify the behavior of the sort using the optional
373
     *                        parameter sort_flags, for details
374
     *                        see sort.
375
     *                        </p>
376
     *
377
     * @return static
378
     *                <p>(Mutable) Return this Arrayy object.</p>
379
     */
380 4
    public function ksort(int $sort_flags = 0): self
381
    {
382 4
        $this->generatorToArray();
383
384 4
        \ksort($this->array, $sort_flags);
385
386 4
        return $this;
387
    }
388
389
    /**
390
     * Sort an array using a case insensitive "natural order" algorithm
391
     *
392
     * @return static
393
     *                <p>(Mutable) Return this Arrayy object.</p>
394
     */
395
    public function natcasesort(): self
396
    {
397
        $this->generatorToArray();
398
399
        \natcasesort($this->array);
400
401
        return $this;
402
    }
403
404
    /**
405
     * Sort entries using a "natural order" algorithm
406
     *
407
     * @return static
408
     *                <p>(Mutable) Return this Arrayy object.</p>
409
     */
410 1
    public function natsort(): self
411
    {
412 1
        $this->generatorToArray();
413
414 1
        \natsort($this->array);
415
416 1
        return $this;
417
    }
418
419
    /**
420
     * Whether or not an offset exists.
421
     *
422
     * @param bool|int|string $offset
423
     *
424
     * @return bool
425
     */
426 46
    public function offsetExists($offset): bool
427
    {
428 46
        $this->generatorToArray();
429
430 46
        if ($this->array === []) {
431 4
            return false;
432
        }
433
434
        // php cast "bool"-index into "int"-index
435 42
        if (\is_bool($offset)) {
436 1
            $offset = (int) $offset;
437
        }
438
439 42
        $tmpReturn = \array_key_exists($offset, $this->array);
440
441
        if (
442 42
            $tmpReturn === true
443
            ||
444
            (
445 16
                $tmpReturn === false
446
                &&
447 42
                \strpos((string) $offset, $this->pathSeparator) === false
448
            )
449
        ) {
450 40
            return $tmpReturn;
451
        }
452
453 3
        $offsetExists = false;
454
455
        if (
456 3
            $this->pathSeparator
457
            &&
458 3
            \is_string($offset)
459
            &&
460 3
            \strpos($offset, $this->pathSeparator) !== false
461
        ) {
462 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
463 3
            if ($explodedPath !== false) {
464 3
                $lastOffset = \array_pop($explodedPath);
465 3
                if ($lastOffset !== null) {
466 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
467
468 3
                    $this->callAtPath(
469 3
                        $containerPath,
470
                        static function ($container) use ($lastOffset, &$offsetExists) {
471 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
472 3
                        }
473
                    );
474
                }
475
            }
476
        }
477
478 3
        return $offsetExists;
479
    }
480
481
    /**
482
     * Returns the value at specified offset.
483
     *
484
     * @param int|string $offset
485
     *
486
     * @return mixed
487
     *               <p>Will return null if the offset did not exists.</p>
488
     */
489 30
    public function offsetGet($offset)
490
    {
491 30
        return $this->offsetExists($offset) ? $this->get($offset) : null;
492
    }
493
494
    /**
495
     * Assigns a value to the specified offset.
496
     *
497
     * @param int|string|null $offset
498
     * @param mixed           $value
499
     */
500 20
    public function offsetSet($offset, $value)
501
    {
502 20
        $this->generatorToArray();
503
504 20
        if ($offset === null) {
505 4
            $this->array[] = $value;
506
        } else {
507 16
            $this->internalSet($offset, $value);
508
        }
509 20
    }
510
511
    /**
512
     * Unset an offset.
513
     *
514
     * @param int|string $offset
515
     */
516 7
    public function offsetUnset($offset)
517
    {
518 7
        $this->generatorToArray();
519
520 7
        if ($this->array === []) {
521 1
            return;
522
        }
523
524 6
        if (\array_key_exists($offset, $this->array)) {
525 4
            unset($this->array[$offset]);
526
527 4
            return;
528
        }
529
530
        if (
531 3
            $this->pathSeparator
532
            &&
533 3
            \is_string($offset)
534
            &&
535 3
            \strpos($offset, $this->pathSeparator) !== false
536
        ) {
537 2
            $path = \explode($this->pathSeparator, (string) $offset);
538
539 2
            if ($path !== false) {
540 2
                $pathToUnset = \array_pop($path);
541
542 2
                $this->callAtPath(
543 2
                    \implode($this->pathSeparator, $path),
544
                    static function (&$offset) use ($pathToUnset) {
545 2
                        unset($offset[$pathToUnset]);
546 2
                    }
547
                );
548
            }
549
        }
550
551 3
        unset($this->array[$offset]);
552 3
    }
553
554
    /** @noinspection SenselessProxyMethodInspection | can not add return type, because of the "Serializable" interface */
555
556
    /**
557
     * Serialize the current "Arrayy"-object.
558
     *
559
     * @return string
560
     */
561 2
    public function serialize(): string
562
    {
563 2
        $this->generatorToArray();
564
565 2
        return parent::serialize();
566
    }
567
568
    /**
569
     * Sets the iterator classname for the current "Arrayy"-object.
570
     *
571
     * @param string $class
572
     *
573
     * @throws \InvalidArgumentException
574
     */
575 933
    public function setIteratorClass($class)
576
    {
577 933
        if (\class_exists($class)) {
578 933
            $this->iteratorClass = $class;
579
580 933
            return;
581
        }
582
583
        if (\strpos($class, '\\') === 0) {
584
            $class = '\\' . $class;
585
            if (\class_exists($class)) {
586
                $this->iteratorClass = $class;
587
588
                return;
589
            }
590
        }
591
592
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
593
    }
594
595
    /**
596
     * Sort the entries with a user-defined comparison function and maintain key association.
597
     *
598
     * @param callable $function
599
     *
600
     * @throws \InvalidArgumentException
601
     *
602
     * @return static
603
     *                <p>(Mutable) Return this Arrayy object.</p>
604
     */
605 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...
606
    {
607
        if (!\is_callable($function)) {
608
            throw new \InvalidArgumentException(
609
                'Passed function must be callable'
610
            );
611
        }
612
613
        $this->generatorToArray();
614
615
        \uasort($this->array, $function);
616
617
        return $this;
618
    }
619
620
    /**
621
     * Sort the entries by keys using a user-defined comparison function.
622
     *
623
     * @param callable $function
624
     *
625
     * @throws \InvalidArgumentException
626
     *
627
     * @return static
628
     *                <p>(Mutable) Return this Arrayy object.</p>
629
     */
630 5
    public function uksort($function): self
631
    {
632 5
        return $this->customSortKeys($function);
633
    }
634
635
    /**
636
     * Unserialize an string and return the instance of the "Arrayy"-class.
637
     *
638
     * @param string $string
639
     *
640
     * @return static
641
     */
642 2
    public function unserialize($string): self
643
    {
644 2
        parent::unserialize($string);
645
646 2
        return $this;
647
    }
648
649
    /**
650
     * Append a (key) + values to the current array.
651
     *
652
     * @param array $values
653
     * @param mixed $key
654
     *
655
     * @return static
656
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
657
     */
658 1
    public function appendArrayValues(array $values, $key = null): self
659
    {
660 1
        $this->generatorToArray();
661
662 1
        if ($key !== null) {
663
            if (
664 1
                isset($this->array[$key])
665
                &&
666 1
                \is_array($this->array[$key])
667
            ) {
668 1
                foreach ($values as $value) {
669 1
                    $this->array[$key][] = $value;
670
                }
671
            } else {
672
                foreach ($values as $value) {
673 1
                    $this->array[$key] = $value;
674
                }
675
            }
676
        } else {
677
            foreach ($values as $value) {
678
                $this->array[] = $value;
679
            }
680
        }
681
682 1
        return $this;
683
    }
684
685
    /**
686
     * Add a suffix to each key.
687
     *
688
     * @param mixed $prefix
689
     *
690
     * @return static
691
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
692
     */
693 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...
694
    {
695
        // init
696 10
        $result = [];
697
698 10
        foreach ($this->getGenerator() as $key => $item) {
699 9
            if ($item instanceof self) {
700
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
701 9
            } elseif (\is_array($item)) {
702
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
703
                    ->appendToEachKey($prefix)
704
                    ->toArray();
705
            } else {
706 9
                $result[$prefix . $key] = $item;
707
            }
708
        }
709
710 10
        return self::create($result, $this->iteratorClass, false);
711
    }
712
713
    /**
714
     * Add a prefix to each value.
715
     *
716
     * @param mixed $prefix
717
     *
718
     * @return static
719
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
720
     */
721 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...
722
    {
723
        // init
724 10
        $result = [];
725
726 10
        foreach ($this->getGenerator() as $key => $item) {
727 9
            if ($item instanceof self) {
728
                $result[$key] = $item->appendToEachValue($prefix);
729 9
            } elseif (\is_array($item)) {
730
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
731 9
            } elseif (\is_object($item)) {
732 1
                $result[$key] = $item;
733
            } else {
734 9
                $result[$key] = $prefix . $item;
735
            }
736
        }
737
738 10
        return self::create($result, $this->iteratorClass, false);
739
    }
740
741
    /**
742
     * Sort an array in reverse order and maintain index association.
743
     *
744
     * @return static
745
     *                <p>(Mutable) Return this Arrayy object.</p>
746
     */
747 4
    public function arsort(): self
748
    {
749 4
        $this->generatorToArray();
750
751 4
        \arsort($this->array);
752
753 4
        return $this;
754
    }
755
756
    /**
757
     * Iterate over the current array and execute a callback for each loop.
758
     *
759
     * @param \Closure $closure
760
     *
761
     * @return static
762
     *                <p>(Immutable)</p>
763
     */
764 2
    public function at(\Closure $closure): self
765
    {
766 2
        $arrayy = clone $this;
767
768 2
        foreach ($arrayy->getGenerator() as $key => $value) {
769 2
            $closure($value, $key);
770
        }
771
772 2
        return static::create(
773 2
            $arrayy->toArray(),
774 2
            $this->iteratorClass,
775 2
            false
776
        );
777
    }
778
779
    /**
780
     * Returns the average value of the current array.
781
     *
782
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
783
     *
784
     * @return float|int
785
     *                   <p>The average value.</p>
786
     */
787 10
    public function average($decimals = 0)
788
    {
789 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
790
791 10
        if (!$count) {
792 2
            return 0;
793
        }
794
795 8
        if (!\is_int($decimals)) {
796 3
            $decimals = 0;
797
        }
798
799 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
800
    }
801
802
    /**
803
     * Changes all keys in an array.
804
     *
805
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
806
     *                  or <strong>CASE_LOWER</strong> (default)</p>
807
     *
808
     * @return static
809
     *                <p>(Immutable)</p>
810
     */
811 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
812
    {
813
        if (
814 1
            $case !== \CASE_LOWER
815
            &&
816 1
            $case !== \CASE_UPPER
817
        ) {
818
            $case = \CASE_LOWER;
819
        }
820
821 1
        $return = [];
822 1
        foreach ($this->getGenerator() as $key => $value) {
823 1
            if ($case === \CASE_LOWER) {
824
                /** @noinspection PhpComposerExtensionStubsInspection */
825 1
                $key = \mb_strtolower((string) $key);
826
            } else {
827
                /** @noinspection PhpComposerExtensionStubsInspection */
828 1
                $key = \mb_strtoupper((string) $key);
829
            }
830
831 1
            $return[$key] = $value;
832
        }
833
834 1
        return static::create(
835 1
            $return,
836 1
            $this->iteratorClass,
837 1
            false
838
        );
839
    }
840
841
    /**
842
     * Change the path separator of the array wrapper.
843
     *
844
     * By default, the separator is: "."
845
     *
846
     * @param string $separator <p>Separator to set.</p>
847
     *
848
     * @return static
849
     *                <p>Mutable</p>
850
     */
851 10
    public function changeSeparator($separator): self
852
    {
853 10
        $this->pathSeparator = $separator;
854
855 10
        return $this;
856
    }
857
858
    /**
859
     * Create a chunked version of the current array.
860
     *
861
     * @param int  $size         <p>Size of each chunk.</p>
862
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
863
     *
864
     * @return static
865
     *                <p>(Immutable) A new array of chunks from the original array.</p>
866
     */
867 4
    public function chunk($size, $preserveKeys = false): self
868
    {
869 4
        return static::create(
870 4
            \array_chunk($this->getArray(), $size, $preserveKeys),
871 4
            $this->iteratorClass,
872 4
            false
873
        );
874
    }
875
876
    /**
877
     * Clean all falsy values from the current array.
878
     *
879
     * @return static
880
     *                <p>(Immutable)</p>
881
     */
882 8
    public function clean(): self
883
    {
884 8
        return $this->filter(
885
            static function ($value) {
886 7
                return (bool) $value;
887 8
            }
888
        );
889
    }
890
891
    /**
892
     * WARNING!!! -> Clear the current array.
893
     *
894
     * @return static
895
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
896
     */
897 4
    public function clear(): self
898
    {
899 4
        $this->array = [];
900 4
        $this->generator = null;
901
902 4
        return $this;
903
    }
904
905
    /**
906
     * Check if an item is in the current array.
907
     *
908
     * @param float|int|string $value
909
     * @param bool             $recursive
910
     * @param bool             $strict
911
     *
912
     * @return bool
913
     */
914 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
915
    {
916 23
        if ($recursive === true) {
917 19
            return $this->in_array_recursive($value, $this->getArray(), $strict);
918
        }
919
920 13
        foreach ($this->getGenerator() as $valueFromArray) {
921 10
            if ($strict) {
922 10
                if ($value === $valueFromArray) {
923 10
                    return true;
924
                }
925
            } else {
926
                /** @noinspection NestedPositiveIfStatementsInspection */
927
                if ($value == $valueFromArray) {
928 6
                    return true;
929
                }
930
            }
931
        }
932
933 6
        return false;
934
    }
935
936
    /**
937
     * Check if an (case-insensitive) string is in the current array.
938
     *
939
     * @param string $value
940
     * @param bool   $recursive
941
     *
942
     * @return bool
943
     */
944 26
    public function containsCaseInsensitive($value, $recursive = false): bool
945
    {
946 26
        if ($recursive === true) {
947 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
948 22
                if (\is_array($valueTmp)) {
949 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
950 5
                    if ($return === true) {
951 5
                        return $return;
952
                    }
953 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
954 22
                    return true;
955
                }
956
            }
957
958 10
            return false;
959
        }
960
961 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
962 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
963 11
                return true;
964
            }
965
        }
966
967 5
        return false;
968
    }
969
970
    /**
971
     * Check if the given key/index exists in the array.
972
     *
973
     * @param int|string $key <p>key/index to search for</p>
974
     *
975
     * @return bool
976
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
977
     */
978 4
    public function containsKey($key): bool
979
    {
980 4
        return $this->offsetExists($key);
981
    }
982
983
    /**
984
     * Check if all given needles are present in the array as key/index.
985
     *
986
     * @param array $needles   <p>The keys you are searching for.</p>
987
     * @param bool  $recursive
988
     *
989
     * @return bool
990
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
991
     */
992 2
    public function containsKeys(array $needles, $recursive = false): bool
993
    {
994 2
        if ($recursive === true) {
995 2
            return \count(
996 2
                       \array_intersect($needles, $this->keys(true)->getArray()),
997 2
                       \COUNT_RECURSIVE
998
                   )
999
                   ===
1000 2
                   \count(
1001 2
                       $needles,
1002 2
                       \COUNT_RECURSIVE
1003
                   );
1004
        }
1005
1006 1
        return \count(
1007 1
                   \array_intersect($needles, $this->keys()->getArray()),
1008 1
                   \COUNT_NORMAL
1009
               )
1010
               ===
1011 1
               \count(
1012 1
                   $needles,
1013 1
                   \COUNT_NORMAL
1014
               );
1015
    }
1016
1017
    /**
1018
     * Check if all given needles are present in the array as key/index.
1019
     *
1020
     * @param array $needles <p>The keys you are searching for.</p>
1021
     *
1022
     * @return bool
1023
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1024
     */
1025 1
    public function containsKeysRecursive(array $needles): bool
1026
    {
1027 1
        return $this->containsKeys($needles, true);
1028
    }
1029
1030
    /**
1031
     * alias: for "Arrayy->contains()"
1032
     *
1033
     * @param float|int|string $value
1034
     *
1035
     * @return bool
1036
     *
1037
     * @see Arrayy::contains()
1038
     */
1039 9
    public function containsValue($value): bool
1040
    {
1041 9
        return $this->contains($value);
1042
    }
1043
1044
    /**
1045
     * alias: for "Arrayy->contains($value, true)"
1046
     *
1047
     * @param float|int|string $value
1048
     *
1049
     * @return bool
1050
     *
1051
     * @see Arrayy::contains()
1052
     */
1053 18
    public function containsValueRecursive($value): bool
1054
    {
1055 18
        return $this->contains($value, true);
1056
    }
1057
1058
    /**
1059
     * Check if all given needles are present in the array.
1060
     *
1061
     * @param array $needles
1062
     *
1063
     * @return bool
1064
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1065
     */
1066 1
    public function containsValues(array $needles): bool
1067
    {
1068 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1069
               ===
1070 1
               \count($needles, \COUNT_NORMAL);
1071
    }
1072
1073
    /**
1074
     * Counts all the values of an array
1075
     *
1076
     * @see http://php.net/manual/en/function.array-count-values.php
1077
     *
1078
     * @return static
1079
     *                <p>
1080
     *                (Immutable)
1081
     *                An associative Arrayy-object of values from input as
1082
     *                keys and their count as value.
1083
     *                </p>
1084
     */
1085 1
    public function countValues(): self
1086
    {
1087 1
        return new static(\array_count_values($this->getArray()));
1088
    }
1089
1090
    /**
1091
     * Creates an Arrayy object.
1092
     *
1093
     * @param mixed  $array
1094
     * @param string $iteratorClass
1095
     * @param bool   $checkForMissingPropertiesInConstructor
1096
     *
1097
     * @return static
1098
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1099
     */
1100 557
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1101
    {
1102 557
        return new static(
1103 557
            $array,
1104 557
            $iteratorClass,
1105 557
            $checkForMissingPropertiesInConstructor
1106
        );
1107
    }
1108
1109
    /**
1110
     * WARNING: Creates an Arrayy object by reference.
1111
     *
1112
     * @param array $array
1113
     *
1114
     * @return static
1115
     *                <p>(Mutable) Return this Arrayy object.</p>
1116
     */
1117 1
    public function createByReference(array &$array = []): self
1118
    {
1119 1
        $array = $this->fallbackForArray($array);
1120
1121 1
        $this->array = &$array;
1122
1123 1
        return $this;
1124
    }
1125
1126
    /**
1127
     * Create an new instance from a callable function which will return an Generator.
1128
     *
1129
     * @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...
1130
     *
1131
     * @return static
1132
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1133
     */
1134 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1135
    {
1136 5
        $arrayy = new static($generatorFunction);
1137
1138 5
        return $arrayy;
1139
    }
1140
1141
    /**
1142
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1143
     *
1144
     * @param \Traversable $traversable
1145
     *
1146
     * @return static
1147
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1148
     */
1149
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1150
    {
1151
        return new static(\iterator_to_array($traversable, true));
1152
    }
1153
1154
    /**
1155
     * Create an new instance filled with a copy of values from a "Generator"-object.
1156
     *
1157
     * @param \Generator $generator
1158
     *
1159
     * @return static
1160
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1161
     */
1162 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1163
    {
1164 4
        return new static(\iterator_to_array($generator, true));
1165
    }
1166
1167
    /**
1168
     * Create an new Arrayy object via JSON.
1169
     *
1170
     * @param string $json
1171
     *
1172
     * @return static
1173
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1174
     */
1175 5
    public static function createFromJson(string $json): self
1176
    {
1177
        /** @noinspection PhpComposerExtensionStubsInspection */
1178 5
        return static::create(\json_decode($json, true));
1179
    }
1180
1181
    /**
1182
     * Create an new instance filled with values from an object that is iterable.
1183
     *
1184
     * @param \Traversable $object <p>iterable object</p>
1185
     *
1186
     * @return static
1187
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1188
     */
1189 4
    public static function createFromObject(\Traversable $object): self
1190
    {
1191
        // init
1192 4
        $array = new static();
1193
1194 4
        if ($object instanceof self) {
1195 4
            $objectArray = $object->getGenerator();
1196
        } else {
1197
            $objectArray = $object;
1198
        }
1199
1200 4
        foreach ($objectArray as $key => $value) {
1201 3
            $array[$key] = $value;
1202
        }
1203
1204 4
        return $array;
1205
    }
1206
1207
    /**
1208
     * Create an new instance filled with values from an object.
1209
     *
1210
     * @param object $object
1211
     *
1212
     * @return static
1213
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1214
     */
1215 5
    public static function createFromObjectVars($object): self
1216
    {
1217 5
        return new static(self::objectToArray($object));
1218
    }
1219
1220
    /**
1221
     * Create an new Arrayy object via string.
1222
     *
1223
     * @param string      $str       <p>The input string.</p>
1224
     * @param string|null $delimiter <p>The boundary string.</p>
1225
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1226
     *                               used.</p>
1227
     *
1228
     * @return static
1229
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1230
     */
1231 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1232
    {
1233 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...
1234 1
            \preg_match_all($regEx, $str, $array);
1235
1236 1
            if (!empty($array)) {
1237 1
                $array = $array[0];
1238
            }
1239
        } else {
1240
            /** @noinspection NestedPositiveIfStatementsInspection */
1241 9
            if ($delimiter !== null) {
1242 7
                $array = \explode($delimiter, $str);
1243
            } else {
1244 2
                $array = [$str];
1245
            }
1246
        }
1247
1248
        // trim all string in the array
1249 10
        \array_walk(
1250
            $array,
1251
            static function (&$val) {
1252 10
                if (\is_string($val)) {
1253 10
                    $val = \trim($val);
1254
                }
1255 10
            }
1256
        );
1257
1258 10
        return static::create($array);
1259
    }
1260
1261
    /**
1262
     * Create an new instance containing a range of elements.
1263
     *
1264
     * @param mixed $low  <p>First value of the sequence.</p>
1265
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1266
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1267
     *
1268
     * @return static
1269
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1270
     */
1271 2
    public static function createWithRange($low, $high, int $step = 1): self
1272
    {
1273 2
        return static::create(\range($low, $high, $step));
1274
    }
1275
1276
    /**
1277
     * Custom sort by index via "uksort".
1278
     *
1279
     * @see http://php.net/manual/en/function.uksort.php
1280
     *
1281
     * @param callable $function
1282
     *
1283
     * @throws \InvalidArgumentException
1284
     *
1285
     * @return static
1286
     *                <p>(Mutable) Return this Arrayy object.</p>
1287
     */
1288 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...
1289
    {
1290 5
        if (!\is_callable($function)) {
1291
            throw new \InvalidArgumentException(
1292
                'Passed function must be callable'
1293
            );
1294
        }
1295
1296 5
        $this->generatorToArray();
1297
1298 5
        \uksort($this->array, $function);
1299
1300 5
        return $this;
1301
    }
1302
1303
    /**
1304
     * Custom sort by value via "usort".
1305
     *
1306
     * @see http://php.net/manual/en/function.usort.php
1307
     *
1308
     * @param callable $function
1309
     *
1310
     * @throws \InvalidArgumentException
1311
     *
1312
     * @return static
1313
     *                <p>(Mutable) Return this Arrayy object.</p>
1314
     */
1315 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...
1316
    {
1317 5
        if (!\is_callable($function)) {
1318
            throw new \InvalidArgumentException(
1319
                'Passed function must be callable'
1320
            );
1321
        }
1322
1323 5
        $this->generatorToArray();
1324
1325 5
        \usort($this->array, $function);
1326
1327 5
        return $this;
1328
    }
1329
1330
    /**
1331
     * Return values that are only in the current array.
1332
     *
1333
     * @param array $array
1334
     *
1335
     * @return static
1336
     *                <p>(Immutable)</p>
1337
     */
1338 12
    public function diff(array $array = []): self
1339
    {
1340 12
        return static::create(
1341 12
            \array_diff($this->getArray(), $array),
1342 12
            $this->iteratorClass,
1343 12
            false
1344
        );
1345
    }
1346
1347
    /**
1348
     * Return values that are only in the current multi-dimensional array.
1349
     *
1350
     * @param array      $array
1351
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1352
     *
1353
     * @return static
1354
     *                <p>(Immutable)</p>
1355
     */
1356 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1357
    {
1358
        // init
1359 1
        $result = [];
1360
1361
        if (
1362 1
            $helperVariableForRecursion !== null
1363
            &&
1364 1
            \is_array($helperVariableForRecursion)
1365
        ) {
1366
            $arrayForTheLoop = $helperVariableForRecursion;
1367
        } else {
1368 1
            $arrayForTheLoop = $this->getGenerator();
1369
        }
1370
1371 1
        foreach ($arrayForTheLoop as $key => $value) {
1372 1
            if ($value instanceof self) {
1373
                $value = $value->getArray();
1374
            }
1375
1376 1
            if (\array_key_exists($key, $array)) {
1377 1
                if ($value !== $array[$key]) {
1378 1
                    $result[$key] = $value;
1379
                }
1380
            } else {
1381 1
                $result[$key] = $value;
1382
            }
1383
        }
1384
1385 1
        return static::create(
1386 1
            $result,
1387 1
            $this->iteratorClass,
1388 1
            false
1389
        );
1390
    }
1391
1392
    /**
1393
     * Return values that are only in the new $array.
1394
     *
1395
     * @param array $array
1396
     *
1397
     * @return static
1398
     *                <p>(Immutable)</p>
1399
     */
1400 8
    public function diffReverse(array $array = []): self
1401
    {
1402 8
        return static::create(
1403 8
            \array_diff($array, $this->getArray()),
1404 8
            $this->iteratorClass,
1405 8
            false
1406
        );
1407
    }
1408
1409
    /**
1410
     * Divide an array into two arrays. One with keys and the other with values.
1411
     *
1412
     * @return static
1413
     *                <p>(Immutable)</p>
1414
     */
1415 1
    public function divide(): self
1416
    {
1417 1
        return static::create(
1418
            [
1419 1
                $this->keys(),
1420 1
                $this->values(),
1421
            ],
1422 1
            $this->iteratorClass,
1423 1
            false
1424
        );
1425
    }
1426
1427
    /**
1428
     * Iterate over the current array and modify the array's value.
1429
     *
1430
     * @param \Closure $closure
1431
     *
1432
     * @return static
1433
     *                <p>(Immutable)</p>
1434
     */
1435 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...
1436
    {
1437
        // init
1438 4
        $array = [];
1439
1440 4
        foreach ($this->getGenerator() as $key => $value) {
1441 4
            $array[$key] = $closure($value, $key);
1442
        }
1443
1444 4
        return static::create(
1445 4
            $array,
1446 4
            $this->iteratorClass,
1447 4
            false
1448
        );
1449
    }
1450
1451
    /**
1452
     * Check if a value is in the current array using a closure.
1453
     *
1454
     * @param \Closure $closure
1455
     *
1456
     * @return bool
1457
     *              <p>Returns true if the given value is found, false otherwise.</p>
1458
     */
1459 4
    public function exists(\Closure $closure): bool
1460
    {
1461
        // init
1462 4
        $isExists = false;
1463
1464 4
        foreach ($this->getGenerator() as $key => $value) {
1465 3
            if ($closure($value, $key)) {
1466 1
                $isExists = true;
1467
1468 3
                break;
1469
            }
1470
        }
1471
1472 4
        return $isExists;
1473
    }
1474
1475
    /**
1476
     * Fill the array until "$num" with "$default" values.
1477
     *
1478
     * @param int   $num
1479
     * @param mixed $default
1480
     *
1481
     * @return static
1482
     *                <p>(Immutable)</p>
1483
     */
1484 8
    public function fillWithDefaults(int $num, $default = null): self
1485
    {
1486 8
        if ($num < 0) {
1487 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1488
        }
1489
1490 7
        $this->generatorToArray();
1491
1492 7
        $tmpArray = $this->array;
1493
1494 7
        $count = \count($tmpArray);
1495
1496 7
        while ($count < $num) {
1497 4
            $tmpArray[] = $default;
1498 4
            ++$count;
1499
        }
1500
1501 7
        return static::create(
1502 7
            $tmpArray,
1503 7
            $this->iteratorClass,
1504 7
            false
1505
        );
1506
    }
1507
1508
    /**
1509
     * Find all items in an array that pass the truth test.
1510
     *
1511
     * @param \Closure|null $closure [optional] <p>
1512
     *                               The callback function to use
1513
     *                               </p>
1514
     *                               <p>
1515
     *                               If no callback is supplied, all entries of
1516
     *                               input equal to false (see
1517
     *                               converting to
1518
     *                               boolean) will be removed.
1519
     *                               </p>
1520
     *                               * @param int $flag [optional] <p>
1521
     *                               Flag determining what arguments are sent to <i>callback</i>:
1522
     *                               </p><ul>
1523
     *                               <li>
1524
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1525
     *                               to <i>callback</i> instead of the value</span>
1526
     *                               </li>
1527
     *                               <li>
1528
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1529
     *                               arguments to <i>callback</i> instead of the value</span>
1530
     *                               </li>
1531
     *                               </ul>
1532
     *
1533
     * @return static
1534
     *                <p>(Immutable)</p>
1535
     */
1536 11
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1537
    {
1538 11
        if (!$closure) {
1539 1
            return $this->clean();
1540
        }
1541
1542 11
        return static::create(
1543 11
            \array_filter($this->getArray(), $closure, $flag),
1544 11
            $this->iteratorClass,
1545 11
            false
1546
        );
1547
    }
1548
1549
    /**
1550
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1551
     * property within that.
1552
     *
1553
     * @param string          $property
1554
     * @param string|string[] $value
1555
     * @param string          $comparisonOp
1556
     *                                      <p>
1557
     *                                      'eq' (equals),<br />
1558
     *                                      'gt' (greater),<br />
1559
     *                                      'gte' || 'ge' (greater or equals),<br />
1560
     *                                      'lt' (less),<br />
1561
     *                                      'lte' || 'le' (less or equals),<br />
1562
     *                                      'ne' (not equals),<br />
1563
     *                                      'contains',<br />
1564
     *                                      'notContains',<br />
1565
     *                                      'newer' (via strtotime),<br />
1566
     *                                      'older' (via strtotime),<br />
1567
     *                                      </p>
1568
     *
1569
     * @return static
1570
     *                <p>(Immutable)</p>
1571
     */
1572 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1573
    {
1574 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...
1575 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1576
        }
1577
1578
        $ops = [
1579
            'eq' => static function ($item, $prop, $value) {
1580 1
                return $item[$prop] === $value;
1581 1
            },
1582
            'gt' => static function ($item, $prop, $value) {
1583
                return $item[$prop] > $value;
1584 1
            },
1585
            'ge' => static function ($item, $prop, $value) {
1586
                return $item[$prop] >= $value;
1587 1
            },
1588
            'gte' => static function ($item, $prop, $value) {
1589
                return $item[$prop] >= $value;
1590 1
            },
1591
            'lt' => static function ($item, $prop, $value) {
1592 1
                return $item[$prop] < $value;
1593 1
            },
1594
            'le' => static function ($item, $prop, $value) {
1595
                return $item[$prop] <= $value;
1596 1
            },
1597
            'lte' => static function ($item, $prop, $value) {
1598
                return $item[$prop] <= $value;
1599 1
            },
1600
            'ne' => static function ($item, $prop, $value) {
1601
                return $item[$prop] !== $value;
1602 1
            },
1603
            'contains' => static function ($item, $prop, $value) {
1604 1
                return \in_array($item[$prop], (array) $value, true);
1605 1
            },
1606
            'notContains' => static function ($item, $prop, $value) {
1607
                return !\in_array($item[$prop], (array) $value, true);
1608 1
            },
1609
            'newer' => static function ($item, $prop, $value) {
1610
                return \strtotime($item[$prop]) > \strtotime($value);
1611 1
            },
1612
            'older' => static function ($item, $prop, $value) {
1613
                return \strtotime($item[$prop]) < \strtotime($value);
1614 1
            },
1615
        ];
1616
1617 1
        $result = \array_values(
1618 1
            \array_filter(
1619 1
                $this->getArray(),
1620
                static function ($item) use (
1621 1
                    $property,
1622 1
                    $value,
1623 1
                    $ops,
1624 1
                    $comparisonOp
1625
                ) {
1626 1
                    $item = (array) $item;
1627 1
                    $itemArrayy = new static($item);
1628 1
                    $item[$property] = $itemArrayy->get($property, []);
1629
1630 1
                    return $ops[$comparisonOp]($item, $property, $value);
1631 1
                }
1632
            )
1633
        );
1634
1635 1
        return static::create(
1636 1
            $result,
1637 1
            $this->iteratorClass,
1638 1
            false
1639
        );
1640
    }
1641
1642
    /**
1643
     * Find the first item in an array that passes the truth test,
1644
     *  otherwise return false
1645
     *
1646
     * @param \Closure $closure
1647
     *
1648
     * @return false|mixed
1649
     *                     <p>Return false if we did not find the value.</p>
1650
     */
1651 8
    public function find(\Closure $closure)
1652
    {
1653 8
        foreach ($this->getGenerator() as $key => $value) {
1654 6
            if ($closure($value, $key)) {
1655 6
                return $value;
1656
            }
1657
        }
1658
1659 3
        return false;
1660
    }
1661
1662
    /**
1663
     * find by ...
1664
     *
1665
     * @param string          $property
1666
     * @param string|string[] $value
1667
     * @param string          $comparisonOp
1668
     *
1669
     * @return static
1670
     *                <p>(Immutable)</p>
1671
     */
1672
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1673
    {
1674
        return $this->filterBy($property, $value, $comparisonOp);
1675
    }
1676
1677
    /**
1678
     * Get the first value from the current array.
1679
     *
1680
     * @return mixed
1681
     *               <p>Return null if there wasn't a element.</p>
1682
     */
1683 14
    public function first()
1684
    {
1685 14
        $tmpArray = $this->getArray();
1686
1687 14
        $key_first = array_key_first($tmpArray);
1688 14
        if ($key_first === null) {
1689 2
            return null;
1690
        }
1691
1692 12
        return $tmpArray[$key_first];
1693
    }
1694
1695
    /**
1696
     * Get the first value(s) from the current array.
1697
     * And will return an empty array if there was no first entry.
1698
     *
1699
     * @param int|null $number <p>How many values you will take?</p>
1700
     *
1701
     * @return static
1702
     *                <p>(Immutable)</p>
1703
     */
1704 36
    public function firstsImmutable(int $number = null): self
1705
    {
1706 36
        $arrayTmp = $this->getArray();
1707
1708 36
        if ($number === null) {
1709 14
            $array = (array) \array_shift($arrayTmp);
1710
        } else {
1711 22
            $number = (int) $number;
1712 22
            $array = \array_splice($arrayTmp, 0, $number);
1713
        }
1714
1715 36
        return static::create(
1716 36
            $array,
1717 36
            $this->iteratorClass,
1718 36
            false
1719
        );
1720
    }
1721
1722
    /**
1723
     * Get and rmove the first value(s) from the current array.
1724
     * And will return an empty array if there was no first entry.
1725
     *
1726
     * @param int|null $number <p>How many values you will take?</p>
1727
     *
1728
     * @return static
1729
     *                <p>(Mutable)</p>
1730
     */
1731 34
    public function firstsMutable(int $number = null): self
1732
    {
1733 34
        $this->generatorToArray();
1734
1735 34
        if ($number === null) {
1736 19
            $this->array = (array) \array_shift($this->array);
1737
        } else {
1738 15
            $number = (int) $number;
1739 15
            $this->array = \array_splice($this->array, 0, $number);
1740
        }
1741
1742 34
        return $this;
1743
    }
1744
1745
    /**
1746
     * Exchanges all keys with their associated values in an array.
1747
     *
1748
     * @return static
1749
     *                <p>(Immutable)</p>
1750
     */
1751 1
    public function flip(): self
1752
    {
1753 1
        return static::create(
1754 1
            \array_flip($this->getArray()),
1755 1
            $this->iteratorClass,
1756 1
            false
1757
        );
1758
    }
1759
1760
    /**
1761
     * Get a value from an array (optional using dot-notation).
1762
     *
1763
     * @param mixed $key      <p>The key to look for.</p>
1764
     * @param mixed $fallback <p>Value to fallback to.</p>
1765
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1766
     *                        class.</p>
1767
     *
1768
     * @return mixed|static
1769
     */
1770 84
    public function get($key, $fallback = null, array $array = null)
1771
    {
1772 84
        if ($array !== null) {
1773 5
            $usedArray = $array;
1774
        } else {
1775 79
            $this->generatorToArray();
1776
1777 79
            $usedArray = $this->array;
1778
        }
1779
1780 84
        if ($key === null) {
1781 1
            return static::create(
1782 1
                $usedArray,
1783 1
                $this->iteratorClass,
1784 1
                false
1785
            );
1786
        }
1787
1788
        // php cast "bool"-index into "int"-index
1789 84
        if ((bool) $key === $key) {
1790 3
            $key = (int) $key;
1791
        }
1792
1793 84
        if (\array_key_exists($key, $usedArray) === true) {
1794 73
            if (\is_array($usedArray[$key])) {
1795 11
                return static::create(
1796 11
                    $usedArray[$key],
1797 11
                    $this->iteratorClass,
1798 11
                    false
1799
                );
1800
            }
1801
1802 64
            return $usedArray[$key];
1803
        }
1804
1805
        // crawl through array, get key according to object or not
1806 23
        $usePath = false;
1807
        if (
1808 23
            $this->pathSeparator
1809
            &&
1810 23
            \is_string($key)
1811
            &&
1812 23
            \strpos($key, $this->pathSeparator) !== false
1813
        ) {
1814 7
            $segments = \explode($this->pathSeparator, (string) $key);
1815 7
            if ($segments !== false) {
1816 7
                $usePath = true;
1817
1818 7
                foreach ($segments as $segment) {
1819
                    if (
1820
                        (
1821 7
                            \is_array($usedArray)
1822
                            ||
1823 7
                            $usedArray instanceof \ArrayAccess
1824
                        )
1825
                        &&
1826 7
                        isset($usedArray[$segment])
1827
                    ) {
1828 7
                        $usedArray = $usedArray[$segment];
1829
1830 7
                        continue;
1831
                    }
1832
1833
                    if (
1834 6
                        \is_object($usedArray)
1835
                        &&
1836 6
                        \property_exists($usedArray, $segment)
1837
                    ) {
1838 1
                        $usedArray = $usedArray->{$segment};
1839
1840 1
                        continue;
1841
                    }
1842
1843 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
1844
                }
1845
            }
1846
        }
1847
1848 23
        if (!$usePath && !isset($usedArray[$key])) {
1849 16
            return $fallback instanceof \Closure ? $fallback() : $fallback;
1850
        }
1851
1852 7
        if (\is_array($usedArray)) {
1853 1
            return static::create(
1854 1
                $usedArray,
1855 1
                $this->iteratorClass,
1856 1
                false
1857
            );
1858
        }
1859
1860 7
        return $usedArray;
1861
    }
1862
1863
    /**
1864
     * Get the current array from the "Arrayy"-object.
1865
     *
1866
     * @return array
1867
     */
1868 778
    public function getArray(): array
1869
    {
1870
        // init
1871 778
        $array = [];
1872
1873 778
        foreach ($this->getGenerator() as $key => $value) {
1874 673
            $array[$key] = $value;
1875
        }
1876
1877 778
        return $array;
1878
    }
1879
1880
    /**
1881
     * Returns the values from a single column of the input array, identified by
1882
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1883
     *
1884
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1885
     * array by the values from the $indexKey column in the input array.
1886
     *
1887
     * @param mixed $columnKey
1888
     * @param mixed $indexKey
1889
     *
1890
     * @return static
1891
     *                <p>(Immutable)</p>
1892
     */
1893 1
    public function getColumn($columnKey = null, $indexKey = null): self
1894
    {
1895 1
        return static::create(
1896 1
            \array_column($this->getArray(), $columnKey, $indexKey),
1897 1
            $this->iteratorClass,
1898 1
            false
1899
        );
1900
    }
1901
1902
    /**
1903
     * Get the current array from the "Arrayy"-object as generator.
1904
     *
1905
     * @return \Generator
1906
     */
1907 853
    public function getGenerator(): \Generator
1908
    {
1909 853
        if ($this->generator instanceof ArrayyRewindableGenerator) {
1910 36
            yield from $this->generator;
1911
        }
1912
1913 853
        yield from $this->array;
1914 810
    }
1915
1916
    /**
1917
     * alias: for "Arrayy->keys()"
1918
     *
1919
     * @return static
1920
     *                <p>(Immutable)</p>
1921
     *
1922
     * @see Arrayy::keys()
1923
     */
1924 1
    public function getKeys(): self
1925
    {
1926 1
        return $this->keys();
1927
    }
1928
1929
    /**
1930
     * Get the current array from the "Arrayy"-object as object.
1931
     *
1932
     * @return \stdClass
1933
     */
1934 4
    public function getObject(): \stdClass
1935
    {
1936 4
        return self::arrayToObject($this->getArray());
1937
    }
1938
1939
    /**
1940
     * alias: for "Arrayy->randomImmutable()"
1941
     *
1942
     * @return static
1943
     *                <p>(Immutable)</p>
1944
     *
1945
     * @see Arrayy::randomImmutable()
1946
     */
1947 4
    public function getRandom(): self
1948
    {
1949 4
        return $this->randomImmutable();
1950
    }
1951
1952
    /**
1953
     * alias: for "Arrayy->randomKey()"
1954
     *
1955
     * @return mixed
1956
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
1957
     *
1958
     * @see Arrayy::randomKey()
1959
     */
1960 3
    public function getRandomKey()
1961
    {
1962 3
        return $this->randomKey();
1963
    }
1964
1965
    /**
1966
     * alias: for "Arrayy->randomKeys()"
1967
     *
1968
     * @param int $number
1969
     *
1970
     * @return static
1971
     *                <p>(Immutable)</p>
1972
     *
1973
     * @see Arrayy::randomKeys()
1974
     */
1975 8
    public function getRandomKeys(int $number): self
1976
    {
1977 8
        return $this->randomKeys($number);
1978
    }
1979
1980
    /**
1981
     * alias: for "Arrayy->randomValue()"
1982
     *
1983
     * @return mixed
1984
     *               <p>Get a random value or null if there wasn't a value.</p>
1985
     *
1986
     * @see Arrayy::randomValue()
1987
     */
1988 3
    public function getRandomValue()
1989
    {
1990 3
        return $this->randomValue();
1991
    }
1992
1993
    /**
1994
     * alias: for "Arrayy->randomValues()"
1995
     *
1996
     * @param int $number
1997
     *
1998
     * @return static
1999
     *                <p>(Immutable)</p>
2000
     *
2001
     * @see Arrayy::randomValues()
2002
     */
2003 6
    public function getRandomValues(int $number): self
2004
    {
2005 6
        return $this->randomValues($number);
2006
    }
2007
2008
    /**
2009
     * Group values from a array according to the results of a closure.
2010
     *
2011
     * @param callable|string $grouper  <p>A callable function name.</p>
2012
     * @param bool            $saveKeys
2013
     *
2014
     * @return static
2015
     *                <p>(Immutable)</p>
2016
     */
2017 4
    public function group($grouper, bool $saveKeys = false): self
2018
    {
2019
        // init
2020 4
        $result = [];
2021
2022
        // Iterate over values, group by property/results from closure.
2023 4
        foreach ($this->getGenerator() as $key => $value) {
2024 4
            if (\is_callable($grouper)) {
2025 3
                $groupKey = $grouper($value, $key);
2026
            } else {
2027 1
                $groupKey = $this->get($grouper, null, $this->getArray());
2028
            }
2029
2030 4
            $newValue = $this->get($groupKey, null, $result);
2031
2032 4
            if ($groupKey instanceof self) {
2033
                $groupKey = $groupKey->getArray();
2034
            }
2035
2036 4
            if ($newValue instanceof self) {
2037 4
                $newValue = $newValue->getArray();
2038
            }
2039
2040
            // Add to results.
2041 4
            if ($groupKey !== null) {
2042 3
                if ($saveKeys) {
2043 2
                    $result[$groupKey] = $newValue;
2044 2
                    $result[$groupKey][$key] = $value;
2045
                } else {
2046 1
                    $result[$groupKey] = $newValue;
2047 4
                    $result[$groupKey][] = $value;
2048
                }
2049
            }
2050
        }
2051
2052 4
        return static::create(
2053 4
            $result,
2054 4
            $this->iteratorClass,
2055 4
            false
2056
        );
2057
    }
2058
2059
    /**
2060
     * Check if an array has a given key.
2061
     *
2062
     * @param mixed $key
2063
     *
2064
     * @return bool
2065
     */
2066 23
    public function has($key): bool
2067
    {
2068 23
        static $UN_FOUND = null;
2069
2070 23
        if ($UN_FOUND === null) {
2071
            // Generate unique string to use as marker.
2072 1
            $UN_FOUND = \uniqid('arrayy', true);
2073
        }
2074
2075 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2076
    }
2077
2078
    /**
2079
     * Implodes the values of this array.
2080
     *
2081
     * @param string $glue
2082
     *
2083
     * @return string
2084
     */
2085 27
    public function implode(string $glue = ''): string
2086
    {
2087 27
        return $this->implode_recursive($glue, $this->getArray(), false);
2088
    }
2089
2090
    /**
2091
     * Implodes the keys of this array.
2092
     *
2093
     * @param string $glue
2094
     *
2095
     * @return string
2096
     */
2097 8
    public function implodeKeys(string $glue = ''): string
2098
    {
2099 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2100
    }
2101
2102
    /**
2103
     * Given a list and an iterate-function that returns
2104
     * a key for each element in the list (or a property name),
2105
     * returns an object with an index of each item.
2106
     *
2107
     * @param mixed $key
2108
     *
2109
     * @return static
2110
     *                <p>(Immutable)</p>
2111
     */
2112 4
    public function indexBy($key): self
2113
    {
2114
        // init
2115 4
        $results = [];
2116
2117 4
        foreach ($this->getGenerator() as $a) {
2118 4
            if (\array_key_exists($key, $a) === true) {
2119 4
                $results[$a[$key]] = $a;
2120
            }
2121
        }
2122
2123 4
        return static::create(
2124 4
            $results,
2125 4
            $this->iteratorClass,
2126 4
            false
2127
        );
2128
    }
2129
2130
    /**
2131
     * alias: for "Arrayy->searchIndex()"
2132
     *
2133
     * @param mixed $value <p>The value to search for.</p>
2134
     *
2135
     * @return mixed
2136
     *
2137
     * @see Arrayy::searchIndex()
2138
     */
2139 4
    public function indexOf($value)
2140
    {
2141 4
        return $this->searchIndex($value);
2142
    }
2143
2144
    /**
2145
     * Get everything but the last..$to items.
2146
     *
2147
     * @param int $to
2148
     *
2149
     * @return static
2150
     *                <p>(Immutable)</p>
2151
     */
2152 12
    public function initial(int $to = 1): self
2153
    {
2154 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2155
    }
2156
2157
    /**
2158
     * Return an array with all elements found in input array.
2159
     *
2160
     * @param array $search
2161
     * @param bool  $keepKeys
2162
     *
2163
     * @return static
2164
     *                <p>(Immutable)</p>
2165
     */
2166 3
    public function intersection(array $search, bool $keepKeys = false): self
2167
    {
2168 3
        if ($keepKeys) {
2169 1
            return static::create(
2170 1
                \array_uintersect(
2171 1
                    $this->array,
2172 1
                    $search,
2173
                    static function ($a, $b) {
2174 1
                        return $a === $b ? 0 : -1;
2175 1
                    }
2176
                ),
2177 1
                $this->iteratorClass,
2178 1
                false
2179
            );
2180
        }
2181
2182 2
        return static::create(
2183 2
            \array_values(\array_intersect($this->getArray(), $search)),
2184 2
            $this->iteratorClass,
2185 2
            false
2186
        );
2187
    }
2188
2189
    /**
2190
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2191
     *
2192
     * @param array $search
2193
     *
2194
     * @return bool
2195
     */
2196 1
    public function intersects(array $search): bool
2197
    {
2198 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2199
    }
2200
2201
    /**
2202
     * Invoke a function on all of an array's values.
2203
     *
2204
     * @param callable $callable
2205
     * @param mixed    $arguments
2206
     *
2207
     * @return static
2208
     *                <p>(Immutable)</p>
2209
     */
2210 1
    public function invoke($callable, $arguments = []): self
2211
    {
2212
        // If one argument given for each iteration, create an array for it.
2213 1
        if (!\is_array($arguments)) {
2214 1
            $arguments = \array_fill(
2215 1
                0,
2216 1
                \count($this->getArray(), \COUNT_NORMAL),
2217 1
                $arguments
2218
            );
2219
        }
2220
2221
        // If the callable has arguments, pass them.
2222 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...
2223 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2224
        } else {
2225 1
            $array = $this->map($callable);
2226
        }
2227
2228 1
        return static::create(
2229 1
            $array,
2230 1
            $this->iteratorClass,
2231 1
            false
2232
        );
2233
    }
2234
2235
    /**
2236
     * Check whether array is associative or not.
2237
     *
2238
     * @param bool $recursive
2239
     *
2240
     * @return bool
2241
     *              <p>Returns true if associative, false otherwise.</p>
2242
     */
2243 15
    public function isAssoc(bool $recursive = false): bool
2244
    {
2245 15
        if ($this->isEmpty()) {
2246 3
            return false;
2247
        }
2248
2249 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2250 13
            if (!\is_string($key)) {
2251 13
                return false;
2252
            }
2253
        }
2254
2255 3
        return true;
2256
    }
2257
2258
    /**
2259
     * Check whether the array is empty or not.
2260
     *
2261
     * @return bool
2262
     *              <p>Returns true if empty, false otherwise.</p>
2263
     */
2264 38
    public function isEmpty(): bool
2265
    {
2266 38
        if ($this->generator) {
2267
            return $this->getArray() === [];
2268
        }
2269
2270 38
        return $this->array === [];
2271
    }
2272
2273
    /**
2274
     * Check if the current array is equal to the given "$array" or not.
2275
     *
2276
     * @param array $array
2277
     *
2278
     * @return bool
2279
     */
2280
    public function isEqual(array $array): bool
2281
    {
2282
        return $this->getArray() === $array;
2283
    }
2284
2285
    /**
2286
     * Check if the current array is a multi-array.
2287
     *
2288
     * @return bool
2289
     */
2290 14
    public function isMultiArray(): bool
2291
    {
2292
        return !(
2293 14
            \count($this->getArray(), \COUNT_NORMAL)
2294
            ===
2295 14
            \count($this->getArray(), \COUNT_RECURSIVE)
2296
        );
2297
    }
2298
2299
    /**
2300
     * Check whether array is numeric or not.
2301
     *
2302
     * @return bool
2303
     *              <p>Returns true if numeric, false otherwise.</p>
2304
     */
2305 5
    public function isNumeric(): bool
2306
    {
2307 5
        if ($this->isEmpty()) {
2308 2
            return false;
2309
        }
2310
2311 4
        foreach ($this->keys()->getGenerator() as $key) {
2312 4
            if (!\is_int($key)) {
2313 4
                return false;
2314
            }
2315
        }
2316
2317 2
        return true;
2318
    }
2319
2320
    /**
2321
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2322
     *
2323
     * @param bool $recursive
2324
     *
2325
     * @return bool
2326
     */
2327 1
    public function isSequential(bool $recursive = false): bool
2328
    {
2329
2330
        // recursive
2331
2332 1
        if ($recursive === true) {
2333
            return $this->array_keys_recursive($this->getArray())
2334
                   ===
2335
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2336
        }
2337
2338
        // non recursive
2339
2340 1
        return \array_keys($this->getArray())
2341
               ===
2342 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2343
    }
2344
2345
    /**
2346
     * @return array
2347
     */
2348
    public function jsonSerialize(): array
2349
    {
2350
        return $this->getArray();
2351
    }
2352
2353
    /**
2354
     * Get all keys from the current array.
2355
     *
2356
     * @param bool  $recursive    [optional] <p>
2357
     *                            Get all keys, also from all sub-arrays from an multi-dimensional array.
2358
     *                            </p>
2359
     * @param mixed $search_value [optional] <p>
2360
     *                            If specified, then only keys containing these values are returned.
2361
     *                            </p>
2362
     * @param bool  $strict       [optional] <p>
2363
     *                            Determines if strict comparison (===) should be used during the search.
2364
     *                            </p>
2365
     *
2366
     * @return static
2367
     *                <p>(Immutable) An array of all the keys in input.</p>
2368
     */
2369 26
    public function keys(bool $recursive = false, $search_value = null, bool $strict = true): self
2370
    {
2371
2372
        // recursive
2373
2374 26
        if ($recursive === true) {
2375 3
            if ($search_value === null) {
2376 3
                $array = $this->array_keys_recursive($this->getArray());
2377
            } else {
2378
                $array = $this->array_keys_recursive($this->getArray(), $search_value, $strict);
2379
            }
2380
2381 3
            return static::create(
2382 3
                $array,
2383 3
                $this->iteratorClass,
2384 3
                false
2385
            );
2386
        }
2387
2388
        // non recursive
2389
2390 25
        if ($search_value === null) {
2391
            $arrayFunction = function () {
2392 25
                foreach ($this->getGenerator() as $key => $value) {
2393 24
                    yield $key;
2394
                }
2395 25
            };
2396
        } else {
2397
            $arrayFunction = function () use ($search_value, $strict) {
2398
                foreach ($this->getGenerator() as $key => $value) {
2399
                    if ($strict) {
2400
                        if ($search_value === $value) {
2401
                            yield $key;
2402
                        }
2403
                    } else {
2404
                        /** @noinspection NestedPositiveIfStatementsInspection */
2405
                        if ($search_value == $value) {
2406
                            yield $key;
2407
                        }
2408
                    }
2409
                }
2410
            };
2411
        }
2412
2413 25
        return static::create(
2414 25
            $arrayFunction,
2415 25
            $this->iteratorClass,
2416 25
            false
2417
        );
2418
    }
2419
2420
    /**
2421
     * Sort an array by key in reverse order.
2422
     *
2423
     * @param int $sort_flags [optional] <p>
2424
     *                        You may modify the behavior of the sort using the optional
2425
     *                        parameter sort_flags, for details
2426
     *                        see sort.
2427
     *                        </p>
2428
     *
2429
     * @return static
2430
     *                <p>(Mutable) Return this Arrayy object.</p>
2431
     */
2432 4
    public function krsort(int $sort_flags = 0): self
2433
    {
2434 4
        $this->generatorToArray();
2435
2436 4
        \krsort($this->array, $sort_flags);
2437
2438 4
        return $this;
2439
    }
2440
2441
    /**
2442
     * Get the last value from the current array.
2443
     *
2444
     * @return mixed
2445
     *               <p>Return null if there wasn't a element.</p>
2446
     */
2447 12
    public function last()
2448
    {
2449 12
        $this->generatorToArray();
2450
2451 12
        $key_last = \array_key_last($this->array);
2452
2453 12
        if ($key_last === null) {
2454 1
            return null;
2455
        }
2456
2457 11
        return $this->get($key_last);
2458
    }
2459
2460
    /**
2461
     * Get the last value(s) from the current array.
2462
     *
2463
     * @param int|null $number
2464
     *
2465
     * @return static
2466
     *                <p>(Immutable)</p>
2467
     */
2468 13
    public function lastsImmutable(int $number = null): self
2469
    {
2470 13
        if ($this->isEmpty()) {
2471 1
            return static::create(
2472 1
                [],
2473 1
                $this->iteratorClass,
2474 1
                false
2475
            );
2476
        }
2477
2478 12
        if ($number === null) {
2479 8
            $poppedValue = $this->last();
2480
2481 8
            if ($poppedValue === null) {
2482 1
                $poppedValue = [$poppedValue];
2483
            } else {
2484 7
                $poppedValue = (array) $poppedValue;
2485
            }
2486
2487 8
            $arrayy = static::create(
2488 8
                $poppedValue,
2489 8
                $this->iteratorClass,
2490 8
                false
2491
            );
2492
        } else {
2493 4
            $number = (int) $number;
2494 4
            $arrayy = $this->rest(-$number);
2495
        }
2496
2497 12
        return $arrayy;
2498
    }
2499
2500
    /**
2501
     * Get the last value(s) from the current array.
2502
     *
2503
     * @param int|null $number
2504
     *
2505
     * @return static
2506
     *                <p>(Mutable)</p>
2507
     */
2508 13
    public function lastsMutable(int $number = null): self
2509
    {
2510 13
        if ($this->isEmpty()) {
2511 1
            return $this;
2512
        }
2513
2514 12
        if ($number === null) {
2515 8
            $poppedValue = $this->last();
2516
2517 8
            if ($poppedValue === null) {
2518 1
                $poppedValue = [$poppedValue];
2519
            } else {
2520 7
                $poppedValue = (array) $poppedValue;
2521
            }
2522
2523 8
            $this->array = static::create(
2524 8
                $poppedValue,
2525 8
                $this->iteratorClass,
2526 8
                false
2527 8
            )->getArray();
2528
        } else {
2529 4
            $number = (int) $number;
2530 4
            $this->array = $this->rest(-$number)->getArray();
2531
        }
2532
2533 12
        $this->generator = null;
2534
2535 12
        return $this;
2536
    }
2537
2538
    /**
2539
     * Count the values from the current array.
2540
     *
2541
     * alias: for "Arrayy->count()"
2542
     *
2543
     * @param int $mode
2544
     *
2545
     * @return int
2546
     *
2547
     * @see Arrayy::count()
2548
     */
2549 20
    public function length(int $mode = \COUNT_NORMAL): int
2550
    {
2551 20
        return $this->count($mode);
2552
    }
2553
2554
    /**
2555
     * Apply the given function to the every element of the array,
2556
     * collecting the results.
2557
     *
2558
     * @param callable $callable
2559
     * @param bool     $useKeyAsSecondParameter
2560
     * @param mixed    ...$arguments
2561
     *
2562
     * @return static
2563
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2564
     */
2565 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2566
    {
2567 5
        $useArguments = \func_num_args() > 2;
2568
2569 5
        return static::create(
2570
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2571 5
                foreach ($this->getGenerator() as $key => $value) {
2572 4
                    if ($useArguments) {
2573 3
                        if ($useKeyAsSecondParameter) {
2574
                            yield $key => $callable($value, $key, ...$arguments);
2575
                        } else {
2576 3
                            yield $key => $callable($value, ...$arguments);
2577
                        }
2578
                    } else {
2579
                        /** @noinspection NestedPositiveIfStatementsInspection */
2580 4
                        if ($useKeyAsSecondParameter) {
2581
                            yield $key => $callable($value, $key);
2582
                        } else {
2583 4
                            yield $key => $callable($value);
2584
                        }
2585
                    }
2586
                }
2587 5
            },
2588 5
            $this->iteratorClass,
2589 5
            false
2590
        );
2591
    }
2592
2593
    /**
2594
     * Check if all items in current array match a truth test.
2595
     *
2596
     * @param \Closure $closure
2597
     *
2598
     * @return bool
2599
     */
2600 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...
2601
    {
2602 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2603 2
            return false;
2604
        }
2605
2606 13
        foreach ($this->getGenerator() as $key => $value) {
2607 13
            $value = $closure($value, $key);
2608
2609 13
            if ($value === false) {
2610 13
                return false;
2611
            }
2612
        }
2613
2614 7
        return true;
2615
    }
2616
2617
    /**
2618
     * Check if any item in the current array matches a truth test.
2619
     *
2620
     * @param \Closure $closure
2621
     *
2622
     * @return bool
2623
     */
2624 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...
2625
    {
2626 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2627 2
            return false;
2628
        }
2629
2630 12
        foreach ($this->getGenerator() as $key => $value) {
2631 12
            $value = $closure($value, $key);
2632
2633 12
            if ($value === true) {
2634 12
                return true;
2635
            }
2636
        }
2637
2638 4
        return false;
2639
    }
2640
2641
    /**
2642
     * Get the max value from an array.
2643
     *
2644
     * @return mixed
2645
     */
2646 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...
2647
    {
2648 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2649 1
            return false;
2650
        }
2651
2652 9
        return \max($this->getArray());
2653
    }
2654
2655
    /**
2656
     * Merge the new $array into the current array.
2657
     *
2658
     * - keep key,value from the current array, also if the index is in the new $array
2659
     *
2660
     * @param array $array
2661
     * @param bool  $recursive
2662
     *
2663
     * @return static
2664
     *                <p>(Immutable)</p>
2665
     */
2666 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...
2667
    {
2668 25
        if ($recursive === true) {
2669 4
            $result = \array_replace_recursive($this->getArray(), $array);
2670
        } else {
2671 21
            $result = \array_replace($this->getArray(), $array);
2672
        }
2673
2674 25
        return static::create(
2675 25
            $result,
2676 25
            $this->iteratorClass,
2677 25
            false
2678
        );
2679
    }
2680
2681
    /**
2682
     * Merge the new $array into the current array.
2683
     *
2684
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2685
     * - create new indexes
2686
     *
2687
     * @param array $array
2688
     * @param bool  $recursive
2689
     *
2690
     * @return static
2691
     *                <p>(Immutable)</p>
2692
     */
2693 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...
2694
    {
2695 16
        if ($recursive === true) {
2696 4
            $result = \array_merge_recursive($this->getArray(), $array);
2697
        } else {
2698 12
            $result = \array_merge($this->getArray(), $array);
2699
        }
2700
2701 16
        return static::create(
2702 16
            $result,
2703 16
            $this->iteratorClass,
2704 16
            false
2705
        );
2706
    }
2707
2708
    /**
2709
     * Merge the the current array into the $array.
2710
     *
2711
     * - use key,value from the new $array, also if the index is in the current array
2712
     *
2713
     * @param array $array
2714
     * @param bool  $recursive
2715
     *
2716
     * @return static
2717
     *                <p>(Immutable)</p>
2718
     */
2719 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...
2720
    {
2721 16
        if ($recursive === true) {
2722 4
            $result = \array_replace_recursive($array, $this->getArray());
2723
        } else {
2724 12
            $result = \array_replace($array, $this->getArray());
2725
        }
2726
2727 16
        return static::create(
2728 16
            $result,
2729 16
            $this->iteratorClass,
2730 16
            false
2731
        );
2732
    }
2733
2734
    /**
2735
     * Merge the current array into the new $array.
2736
     *
2737
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2738
     * - create new indexes
2739
     *
2740
     * @param array $array
2741
     * @param bool  $recursive
2742
     *
2743
     * @return static
2744
     *                <p>(Immutable)</p>
2745
     */
2746 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...
2747
    {
2748 17
        if ($recursive === true) {
2749 4
            $result = \array_merge_recursive($array, $this->getArray());
2750
        } else {
2751 13
            $result = \array_merge($array, $this->getArray());
2752
        }
2753
2754 17
        return static::create(
2755 17
            $result,
2756 17
            $this->iteratorClass,
2757 17
            false
2758
        );
2759
    }
2760
2761
    /**
2762
     * @return ArrayyMeta|static
2763
     */
2764 14
    public static function meta()
2765
    {
2766 14
        return (new ArrayyMeta())->getMetaObject(static::class);
2767
    }
2768
2769
    /**
2770
     * Get the min value from an array.
2771
     *
2772
     * @return mixed
2773
     */
2774 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...
2775
    {
2776 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2777 1
            return false;
2778
        }
2779
2780 9
        return \min($this->getArray());
2781
    }
2782
2783
    /**
2784
     * Move an array element to a new index.
2785
     *
2786
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2787
     *
2788
     * @param int|string $from
2789
     * @param int        $to
2790
     *
2791
     * @return static
2792
     *                <p>(Immutable)</p>
2793
     */
2794 1
    public function moveElement($from, $to): self
2795
    {
2796 1
        $array = $this->getArray();
2797
2798 1
        if (\is_int($from)) {
2799 1
            $tmp = \array_splice($array, $from, 1);
2800 1
            \array_splice($array, (int) $to, 0, $tmp);
2801 1
            $output = $array;
2802 1
        } elseif (\is_string($from)) {
2803 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2804 1
            $itemToMove = $array[$from];
2805 1
            if ($indexToMove !== false) {
2806 1
                \array_splice($array, $indexToMove, 1);
2807
            }
2808 1
            $i = 0;
2809 1
            $output = [];
2810 1
            foreach ($array as $key => $item) {
2811 1
                if ($i === $to) {
2812 1
                    $output[$from] = $itemToMove;
2813
                }
2814 1
                $output[$key] = $item;
2815 1
                ++$i;
2816
            }
2817
        } else {
2818
            $output = [];
2819
        }
2820
2821 1
        return static::create(
2822 1
            $output,
2823 1
            $this->iteratorClass,
2824 1
            false
2825
        );
2826
    }
2827
2828
    /**
2829
     * Get a subset of the items from the given array.
2830
     *
2831
     * @param mixed[] $keys
2832
     *
2833
     * @return static
2834
     *                <p>(Immutable)</p>
2835
     */
2836
    public function only(array $keys): self
2837
    {
2838
        $array = $this->getArray();
2839
2840
        return static::create(
2841
            \array_intersect_key($array, \array_flip($keys)),
2842
            $this->iteratorClass,
2843
            false
2844
        );
2845
    }
2846
2847
    /**
2848
     * Pad array to the specified size with a given value.
2849
     *
2850
     * @param int   $size  <p>Size of the result array.</p>
2851
     * @param mixed $value <p>Empty value by default.</p>
2852
     *
2853
     * @return static
2854
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
2855
     */
2856 4
    public function pad(int $size, $value): self
2857
    {
2858 4
        return static::create(
2859 4
            \array_pad($this->getArray(), $size, $value),
2860 4
            $this->iteratorClass,
2861 4
            false
2862
        );
2863
    }
2864
2865
    /**
2866
     * Pop a specified value off the end of the current array.
2867
     *
2868
     * @return mixed
2869
     *               <p>(Mutable) The popped element from the current array.</p>
2870
     */
2871 4
    public function pop()
2872
    {
2873 4
        $this->generatorToArray();
2874
2875 4
        return \array_pop($this->array);
2876
    }
2877
2878
    /**
2879
     * Prepend a (key) + value to the current array.
2880
     *
2881
     * @param mixed $value
2882
     * @param mixed $key
2883
     *
2884
     * @return static
2885
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
2886
     */
2887 8
    public function prepend($value, $key = null): self
2888
    {
2889 8
        $this->generatorToArray();
2890
2891 8
        if ($key === null) {
2892 8
            \array_unshift($this->array, $value);
2893
        } else {
2894
            /** @noinspection AdditionOperationOnArraysInspection */
2895 1
            $this->array = [$key => $value] + $this->array;
2896
        }
2897
2898 8
        return $this;
2899
    }
2900
2901
    /**
2902
     * Add a suffix to each key.
2903
     *
2904
     * @param mixed $suffix
2905
     *
2906
     * @return static
2907
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
2908
     */
2909 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...
2910
    {
2911
        // init
2912 10
        $result = [];
2913
2914 10
        foreach ($this->getGenerator() as $key => $item) {
2915 9
            if ($item instanceof self) {
2916
                $result[$key] = $item->prependToEachKey($suffix);
2917 9
            } elseif (\is_array($item)) {
2918
                $result[$key] = self::create(
2919
                    $item,
2920
                    $this->iteratorClass,
2921
                    false
2922
                )->prependToEachKey($suffix)
2923
                    ->toArray();
2924
            } else {
2925 9
                $result[$key . $suffix] = $item;
2926
            }
2927
        }
2928
2929 10
        return self::create(
2930 10
            $result,
2931 10
            $this->iteratorClass,
2932 10
            false
2933
        );
2934
    }
2935
2936
    /**
2937
     * Add a suffix to each value.
2938
     *
2939
     * @param mixed $suffix
2940
     *
2941
     * @return static
2942
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
2943
     */
2944 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...
2945
    {
2946
        // init
2947 10
        $result = [];
2948
2949 10
        foreach ($this->getGenerator() as $key => $item) {
2950 9
            if ($item instanceof self) {
2951
                $result[$key] = $item->prependToEachValue($suffix);
2952 9
            } elseif (\is_array($item)) {
2953
                $result[$key] = self::create(
2954
                    $item,
2955
                    $this->iteratorClass,
2956
                    false
2957
                )->prependToEachValue($suffix)
2958
                    ->toArray();
2959 9
            } elseif (\is_object($item)) {
2960 1
                $result[$key] = $item;
2961
            } else {
2962 9
                $result[$key] = $item . $suffix;
2963
            }
2964
        }
2965
2966 10
        return self::create(
2967 10
            $result,
2968 10
            $this->iteratorClass,
2969 10
            false
2970
        );
2971
    }
2972
2973
    /**
2974
     * Push one or more values onto the end of array at once.
2975
     *
2976
     * @return static
2977
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
2978
     */
2979 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...
2980
    {
2981 4
        $this->generatorToArray();
2982
2983 4
        if (\func_num_args()) {
2984 4
            $args = \func_get_args();
2985 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...
2986
        }
2987
2988 4
        return $this;
2989
    }
2990
2991
    /**
2992
     * Get a random value from the current array.
2993
     *
2994
     * @param int|null $number <p>How many values you will take?</p>
2995
     *
2996
     * @return static
2997
     *                <p>(Immutable)</p>
2998
     */
2999 18
    public function randomImmutable(int $number = null): self
3000
    {
3001 18
        $this->generatorToArray();
3002
3003 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...
3004 1
            return static::create(
3005 1
                [],
3006 1
                $this->iteratorClass,
3007 1
                false
3008
            );
3009
        }
3010
3011 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...
3012
            /** @noinspection NonSecureArrayRandUsageInspection */
3013 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3014
3015 13
            return static::create(
3016 13
                $arrayRandValue,
3017 13
                $this->iteratorClass,
3018 13
                false
3019
            );
3020
        }
3021
3022 5
        $arrayTmp = $this->array;
3023
        /** @noinspection NonSecureShuffleUsageInspection */
3024 5
        \shuffle($arrayTmp);
3025
3026 5
        return static::create(
3027 5
            $arrayTmp,
3028 5
            $this->iteratorClass,
3029 5
            false
3030 5
        )->firstsImmutable($number);
3031
    }
3032
3033
    /**
3034
     * Pick a random key/index from the keys of this array.
3035
     *
3036
     * @throws \RangeException If array is empty
3037
     *
3038
     * @return mixed
3039
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3040
     */
3041 4
    public function randomKey()
3042
    {
3043 4
        $result = $this->randomKeys(1);
3044
3045 4
        if (!isset($result[0])) {
3046
            $result[0] = null;
3047
        }
3048
3049 4
        return $result[0];
3050
    }
3051
3052
    /**
3053
     * Pick a given number of random keys/indexes out of this array.
3054
     *
3055
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3056
     *
3057
     * @throws \RangeException If array is empty
3058
     *
3059
     * @return static
3060
     *                <p>(Immutable)</p>
3061
     */
3062 13
    public function randomKeys(int $number): self
3063
    {
3064 13
        $this->generatorToArray();
3065
3066 13
        $count = \count($this->array, \COUNT_NORMAL);
3067
3068 13
        if ($number === 0 || $number > $count) {
3069 2
            throw new \RangeException(
3070 2
                \sprintf(
3071 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3072 2
                    $number,
3073 2
                    $count
3074
                )
3075
            );
3076
        }
3077
3078 11
        $result = (array) \array_rand($this->array, $number);
3079
3080 11
        return static::create(
3081 11
            $result,
3082 11
            $this->iteratorClass,
3083 11
            false
3084
        );
3085
    }
3086
3087
    /**
3088
     * Get a random value from the current array.
3089
     *
3090
     * @param int|null $number <p>How many values you will take?</p>
3091
     *
3092
     * @return static
3093
     *                <p>(Mutable)</p>
3094
     */
3095 17
    public function randomMutable(int $number = null): self
3096
    {
3097 17
        $this->generatorToArray();
3098
3099 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...
3100
            return static::create(
3101
                [],
3102
                $this->iteratorClass,
3103
                false
3104
            );
3105
        }
3106
3107 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...
3108
            /** @noinspection NonSecureArrayRandUsageInspection */
3109 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3110 7
            $this->array = $arrayRandValue;
3111
3112 7
            return $this;
3113
        }
3114
3115
        /** @noinspection NonSecureShuffleUsageInspection */
3116 11
        \shuffle($this->array);
3117
3118 11
        return $this->firstsMutable($number);
3119
    }
3120
3121
    /**
3122
     * Pick a random value from the values of this array.
3123
     *
3124
     * @return mixed
3125
     *               <p>Get a random value or null if there wasn't a value.</p>
3126
     */
3127 4
    public function randomValue()
3128
    {
3129 4
        $result = $this->randomImmutable();
3130
3131 4
        if (!isset($result[0])) {
3132
            $result[0] = null;
3133
        }
3134
3135 4
        return $result[0];
3136
    }
3137
3138
    /**
3139
     * Pick a given number of random values out of this array.
3140
     *
3141
     * @param int $number
3142
     *
3143
     * @return static
3144
     *                <p>(Mutable)</p>
3145
     */
3146 7
    public function randomValues(int $number): self
3147
    {
3148 7
        return $this->randomMutable($number);
3149
    }
3150
3151
    /**
3152
     * Get a random value from an array, with the ability to skew the results.
3153
     *
3154
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3155
     *
3156
     * @param array    $array
3157
     * @param int|null $number <p>How many values you will take?</p>
3158
     *
3159
     * @return static
3160
     *                <p>(Immutable)</p>
3161
     */
3162 9
    public function randomWeighted(array $array, int $number = null): self
3163
    {
3164
        // init
3165 9
        $options = [];
3166
3167 9
        foreach ($array as $option => $weight) {
3168 9
            if ($this->searchIndex($option) !== false) {
3169 9
                for ($i = 0; $i < $weight; ++$i) {
3170 1
                    $options[] = $option;
3171
                }
3172
            }
3173
        }
3174
3175 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3176
    }
3177
3178
    /**
3179
     * Reduce the current array via callable e.g. anonymous-function.
3180
     *
3181
     * @param callable $callable
3182
     * @param array    $init
3183
     *
3184
     * @return static
3185
     *                <p>(Immutable)</p>
3186
     */
3187 16
    public function reduce($callable, array $init = []): self
3188
    {
3189 16
        if ($this->generator) {
3190 1
            $result = $init;
3191
3192 1
            foreach ($this->getGenerator() as $value) {
3193 1
                $result = $callable($result, $value);
3194
            }
3195
3196 1
            return static::create(
3197 1
                $result,
3198 1
                $this->iteratorClass,
3199 1
                false
3200
            );
3201
        }
3202
3203 16
        $result = \array_reduce($this->array, $callable, $init);
3204
3205 16
        if ($result === null) {
3206
            $this->array = [];
3207
        } else {
3208 16
            $this->array = (array) $result;
3209
        }
3210
3211 16
        return static::create(
3212 16
            $this->array,
3213 16
            $this->iteratorClass,
3214 16
            false
3215
        );
3216
    }
3217
3218
    /**
3219
     * Create a numerically re-indexed Arrayy object.
3220
     *
3221
     * @return static
3222
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3223
     */
3224 9
    public function reindex(): self
3225
    {
3226 9
        $this->generatorToArray();
3227
3228 9
        $this->array = \array_values($this->array);
3229
3230 9
        return $this;
3231
    }
3232
3233
    /**
3234
     * Return all items that fail the truth test.
3235
     *
3236
     * @param \Closure $closure
3237
     *
3238
     * @return static
3239
     *                <p>(Immutable)</p>
3240
     */
3241 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...
3242
    {
3243
        // init
3244 1
        $filtered = [];
3245
3246 1
        foreach ($this->getGenerator() as $key => $value) {
3247 1
            if (!$closure($value, $key)) {
3248 1
                $filtered[$key] = $value;
3249
            }
3250
        }
3251
3252 1
        return static::create(
3253 1
            $filtered,
3254 1
            $this->iteratorClass,
3255 1
            false
3256
        );
3257
    }
3258
3259
    /**
3260
     * Remove a value from the current array (optional using dot-notation).
3261
     *
3262
     * @param mixed $key
3263
     *
3264
     * @return static
3265
     *                <p>(Mutable)</p>
3266
     */
3267 18
    public function remove($key): self
3268
    {
3269
        // recursive call
3270 18
        if (\is_array($key)) {
3271
            foreach ($key as $k) {
3272
                $this->internalRemove($k);
3273
            }
3274
3275
            return static::create(
3276
                $this->getArray(),
3277
                $this->iteratorClass,
3278
                false
3279
            );
3280
        }
3281
3282 18
        $this->internalRemove($key);
3283
3284 18
        return static::create(
3285 18
            $this->getArray(),
3286 18
            $this->iteratorClass,
3287 18
            false
3288
        );
3289
    }
3290
3291
    /**
3292
     * Remove the first value from the current array.
3293
     *
3294
     * @return static
3295
     *                <p>(Immutable)</p>
3296
     */
3297 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...
3298
    {
3299 7
        $tmpArray = $this->getArray();
3300
3301 7
        \array_shift($tmpArray);
3302
3303 7
        return static::create(
3304 7
            $tmpArray,
3305 7
            $this->iteratorClass,
3306 7
            false
3307
        );
3308
    }
3309
3310
    /**
3311
     * Remove the last value from the current array.
3312
     *
3313
     * @return static
3314
     *                <p>(Immutable)</p>
3315
     */
3316 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...
3317
    {
3318 7
        $tmpArray = $this->getArray();
3319
3320 7
        \array_pop($tmpArray);
3321
3322 7
        return static::create(
3323 7
            $tmpArray,
3324 7
            $this->iteratorClass,
3325 7
            false
3326
        );
3327
    }
3328
3329
    /**
3330
     * Removes a particular value from an array (numeric or associative).
3331
     *
3332
     * @param mixed $value
3333
     *
3334
     * @return static
3335
     *                <p>(Immutable)</p>
3336
     */
3337 7
    public function removeValue($value): self
3338
    {
3339 7
        $this->generatorToArray();
3340
3341
        // init
3342 7
        $isNumericArray = true;
3343
3344 7
        foreach ($this->getGenerator() as $key => $item) {
3345 6
            if ($item === $value) {
3346 6
                if (!\is_int($key)) {
3347
                    $isNumericArray = false;
3348
                }
3349 6
                unset($this->array[$key]);
3350
            }
3351
        }
3352
3353 7
        if ($isNumericArray) {
3354 7
            $this->array = \array_values($this->array);
3355
        }
3356
3357 7
        return static::create(
3358 7
            $this->array,
3359 7
            $this->iteratorClass,
3360 7
            false
3361
        );
3362
    }
3363
3364
    /**
3365
     * Generate array of repeated arrays.
3366
     *
3367
     * @param int $times <p>How many times has to be repeated.</p>
3368
     *
3369
     * @return static
3370
     *                <p>(Immutable)</p>
3371
     */
3372 1
    public function repeat($times): self
3373
    {
3374 1
        if ($times === 0) {
3375 1
            return new static();
3376
        }
3377
3378 1
        return static::create(
3379 1
            \array_fill(0, (int) $times, $this->getArray()),
3380 1
            $this->iteratorClass,
3381 1
            false
3382
        );
3383
    }
3384
3385
    /**
3386
     * Replace a key with a new key/value pair.
3387
     *
3388
     * @param mixed $replace
3389
     * @param mixed $key
3390
     * @param mixed $value
3391
     *
3392
     * @return static
3393
     *                <p>(Immutable)</p>
3394
     */
3395 2
    public function replace($replace, $key, $value): self
3396
    {
3397 2
        $that = clone $this;
3398
3399 2
        return $that->remove($replace)
3400 2
            ->set($key, $value);
3401
    }
3402
3403
    /**
3404
     * Create an array using the current array as values and the other array as keys.
3405
     *
3406
     * @param array $keys <p>An array of keys.</p>
3407
     *
3408
     * @return static
3409
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3410
     */
3411 2
    public function replaceAllKeys(array $keys): self
3412
    {
3413 2
        return static::create(
3414 2
            \array_combine($keys, $this->getArray()),
3415 2
            $this->iteratorClass,
3416 2
            false
3417
        );
3418
    }
3419
3420
    /**
3421
     * Create an array using the current array as keys and the other array as values.
3422
     *
3423
     * @param array $array <p>An array o values.</p>
3424
     *
3425
     * @return static
3426
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3427
     */
3428 2
    public function replaceAllValues(array $array): self
3429
    {
3430 2
        return static::create(
3431 2
            \array_combine($this->array, $array),
3432 2
            $this->iteratorClass,
3433 2
            false
3434
        );
3435
    }
3436
3437
    /**
3438
     * Replace the keys in an array with another set.
3439
     *
3440
     * @param array $keys <p>An array of keys matching the array's size</p>
3441
     *
3442
     * @return static
3443
     *                <p>(Immutable)</p>
3444
     */
3445 1
    public function replaceKeys(array $keys): self
3446
    {
3447 1
        $values = \array_values($this->getArray());
3448 1
        $result = \array_combine($keys, $values);
3449
3450 1
        return static::create(
3451 1
            $result,
3452 1
            $this->iteratorClass,
3453 1
            false
3454
        );
3455
    }
3456
3457
    /**
3458
     * Replace the first matched value in an array.
3459
     *
3460
     * @param mixed $search      <p>The value to replace.</p>
3461
     * @param mixed $replacement <p>The value to replace.</p>
3462
     *
3463
     * @return static
3464
     *                <p>(Immutable)</p>
3465
     */
3466 3
    public function replaceOneValue($search, $replacement = ''): self
3467
    {
3468 3
        $array = $this->getArray();
3469 3
        $key = \array_search($search, $array, true);
3470
3471 3
        if ($key !== false) {
3472 3
            $array[$key] = $replacement;
3473
        }
3474
3475 3
        return static::create(
3476 3
            $array,
3477 3
            $this->iteratorClass,
3478 3
            false
3479
        );
3480
    }
3481
3482
    /**
3483
     * Replace values in the current array.
3484
     *
3485
     * @param mixed $search      <p>The value to replace.</p>
3486
     * @param mixed $replacement <p>What to replace it with.</p>
3487
     *
3488
     * @return static
3489
     *                <p>(Immutable)</p>
3490
     */
3491 1
    public function replaceValues($search, $replacement = ''): self
3492
    {
3493 1
        $array = $this->each(
3494
            static function ($value) use ($search, $replacement) {
3495 1
                return \str_replace($search, $replacement, $value);
3496 1
            }
3497
        );
3498
3499 1
        return $array;
3500
    }
3501
3502
    /**
3503
     * Get the last elements from index $from until the end of this array.
3504
     *
3505
     * @param int $from
3506
     *
3507
     * @return static
3508
     *                <p>(Immutable)</p>
3509
     */
3510 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...
3511
    {
3512 15
        $tmpArray = $this->getArray();
3513
3514 15
        return static::create(
3515 15
            \array_splice($tmpArray, $from),
3516 15
            $this->iteratorClass,
3517 15
            false
3518
        );
3519
    }
3520
3521
    /**
3522
     * Return the array in the reverse order.
3523
     *
3524
     * @return static
3525
     *                <p>(Mutable) Return this Arrayy object.</p>
3526
     */
3527 8
    public function reverse(): self
3528
    {
3529 8
        $this->generatorToArray();
3530
3531 8
        $this->array = \array_reverse($this->array);
3532
3533 8
        return $this;
3534
    }
3535
3536
    /**
3537
     * Sort an array in reverse order.
3538
     *
3539
     * @param int $sort_flags [optional] <p>
3540
     *                        You may modify the behavior of the sort using the optional
3541
     *                        parameter sort_flags, for details
3542
     *                        see sort.
3543
     *                        </p>
3544
     *
3545
     * @return static
3546
     *                <p>(Mutable) Return this Arrayy object.</p>
3547
     */
3548 4
    public function rsort(int $sort_flags = 0): self
3549
    {
3550 4
        $this->generatorToArray();
3551
3552 4
        \rsort($this->array, $sort_flags);
3553
3554 4
        return $this;
3555
    }
3556
3557
    /**
3558
     * Search for the first index of the current array via $value.
3559
     *
3560
     * @param mixed $value
3561
     *
3562
     * @return false|float|int|string
3563
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3564
     */
3565 20
    public function searchIndex($value)
3566
    {
3567 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3568 19
            if ($value === $valueFromArray) {
3569 19
                return $keyFromArray;
3570
            }
3571
        }
3572
3573 11
        return false;
3574
    }
3575
3576
    /**
3577
     * Search for the value of the current array via $index.
3578
     *
3579
     * @param mixed $index
3580
     *
3581
     * @return static
3582
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3583
     */
3584 9
    public function searchValue($index): self
3585
    {
3586 9
        $this->generatorToArray();
3587
3588
        // init
3589 9
        $return = [];
3590
3591 9
        if ($this->array === []) {
3592
            return static::create(
3593
                [],
3594
                $this->iteratorClass,
3595
                false
3596
            );
3597
        }
3598
3599
        // php cast "bool"-index into "int"-index
3600 9
        if ((bool) $index === $index) {
3601 1
            $index = (int) $index;
3602
        }
3603
3604 9
        if (\array_key_exists($index, $this->array) === true) {
3605 7
            $return = [$this->array[$index]];
3606
        }
3607
3608 9
        return static::create(
3609 9
            $return,
3610 9
            $this->iteratorClass,
3611 9
            false
3612
        );
3613
    }
3614
3615
    /**
3616
     * Set a value for the current array (optional using dot-notation).
3617
     *
3618
     * @param string $key   <p>The key to set.</p>
3619
     * @param mixed  $value <p>Its value.</p>
3620
     *
3621
     * @return static
3622
     *                <p>(Mutable)</p>
3623
     */
3624 18
    public function set($key, $value): self
3625
    {
3626 18
        $this->generatorToArray();
3627
3628 18
        $this->internalSet($key, $value);
3629
3630 18
        return $this;
3631
    }
3632
3633
    /**
3634
     * Get a value from a array and set it if it was not.
3635
     *
3636
     * WARNING: this method only set the value, if the $key is not already set
3637
     *
3638
     * @param mixed $key      <p>The key</p>
3639
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3640
     *
3641
     * @return mixed
3642
     *               <p>(Mutable)</p>
3643
     */
3644 11
    public function setAndGet($key, $fallback = null)
3645
    {
3646 11
        $this->generatorToArray();
3647
3648
        // If the key doesn't exist, set it.
3649 11
        if (!$this->has($key)) {
3650 4
            $this->array = $this->set($key, $fallback)->getArray();
3651
        }
3652
3653 11
        return $this->get($key);
3654
    }
3655
3656
    /**
3657
     * Shifts a specified value off the beginning of array.
3658
     *
3659
     * @return mixed
3660
     *               <p>(Mutable) A shifted element from the current array.</p>
3661
     */
3662 4
    public function shift()
3663
    {
3664 4
        $this->generatorToArray();
3665
3666 4
        return \array_shift($this->array);
3667
    }
3668
3669
    /**
3670
     * Shuffle the current array.
3671
     *
3672
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3673
     * @param array $array  [optional]
3674
     *
3675
     * @return static
3676
     *                <p>(Immutable)</p>
3677
     */
3678 1
    public function shuffle(bool $secure = false, array $array = null): self
3679
    {
3680 1
        if ($array === null) {
3681 1
            $array = $this->getArray();
3682
        }
3683
3684 1
        if ($secure !== true) {
3685
            /** @noinspection NonSecureShuffleUsageInspection */
3686 1
            \shuffle($array);
3687
        } else {
3688 1
            $size = \count($array, \COUNT_NORMAL);
3689 1
            $keys = \array_keys($array);
3690 1
            for ($i = $size - 1; $i > 0; --$i) {
3691
                try {
3692 1
                    $r = \random_int(0, $i);
3693
                } catch (\Exception $e) {
3694
                    /** @noinspection RandomApiMigrationInspection */
3695
                    $r = \mt_rand(0, $i);
3696
                }
3697 1
                if ($r !== $i) {
3698
                    $temp = $array[$keys[$r]];
3699
                    $array[$keys[$r]] = $array[$keys[$i]];
3700
                    $array[$keys[$i]] = $temp;
3701
                }
3702
            }
3703
3704
            // reset indices
3705 1
            $array = \array_values($array);
3706
        }
3707
3708 1
        foreach ($array as $key => $value) {
3709
            // check if recursive is needed
3710 1
            if (\is_array($value) === true) {
3711 1
                $array[$key] = $this->shuffle($secure, $value);
3712
            }
3713
        }
3714
3715 1
        return static::create(
3716 1
            $array,
3717 1
            $this->iteratorClass,
3718 1
            false
3719
        );
3720
    }
3721
3722
    /**
3723
     * Checks whether array has exactly $size items.
3724
     *
3725
     * @param int $size
3726
     *
3727
     * @return bool
3728
     */
3729 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...
3730
    {
3731
        // init
3732 1
        $itemsTempCount = 0;
3733
3734 1
        foreach ($this->getGenerator() as $key => $value) {
3735 1
            ++$itemsTempCount;
3736 1
            if ($itemsTempCount > $size) {
3737 1
                return false;
3738
            }
3739
        }
3740
3741 1
        return $itemsTempCount === $size;
3742
    }
3743
3744
    /**
3745
     * Checks whether array has less than $size items.
3746
     *
3747
     * @param int $size
3748
     *
3749
     * @return bool
3750
     */
3751 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...
3752
    {
3753
        // init
3754 1
        $itemsTempCount = 0;
3755
3756 1
        foreach ($this->getGenerator() as $key => $value) {
3757 1
            ++$itemsTempCount;
3758 1
            if ($itemsTempCount > $size) {
3759 1
                return false;
3760
            }
3761
        }
3762
3763 1
        return $itemsTempCount < $size;
3764
    }
3765
3766
    /**
3767
     * Checks whether array has more than $size items.
3768
     *
3769
     * @param int $size
3770
     *
3771
     * @return bool
3772
     */
3773 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...
3774
    {
3775
        // init
3776 1
        $itemsTempCount = 0;
3777
3778 1
        foreach ($this->getGenerator() as $key => $value) {
3779 1
            ++$itemsTempCount;
3780 1
            if ($itemsTempCount > $size) {
3781 1
                return true;
3782
            }
3783
        }
3784
3785 1
        return $itemsTempCount > $size;
3786
    }
3787
3788
    /**
3789
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
3790
     * smaller than $fromSize.
3791
     *
3792
     * @param int $fromSize
3793
     * @param int $toSize
3794
     *
3795
     * @return bool
3796
     */
3797 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
3798
    {
3799 1
        if ($fromSize > $toSize) {
3800 1
            $tmp = $toSize;
3801 1
            $toSize = $fromSize;
3802 1
            $fromSize = $tmp;
3803
        }
3804
3805
        // init
3806 1
        $itemsTempCount = 0;
3807
3808 1
        foreach ($this->getGenerator() as $key => $value) {
3809 1
            ++$itemsTempCount;
3810 1
            if ($itemsTempCount > $toSize) {
3811 1
                return false;
3812
            }
3813
        }
3814
3815 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
3816
    }
3817
3818
    /**
3819
     * Count the values from the current array.
3820
     *
3821
     * alias: for "Arrayy->count()"
3822
     *
3823
     * @param int $mode
3824
     *
3825
     * @return int
3826
     */
3827 20
    public function size(int $mode = \COUNT_NORMAL): int
3828
    {
3829 20
        return $this->count($mode);
3830
    }
3831
3832
    /**
3833
     * Counts all elements in an array, or something in an object.
3834
     *
3835
     * <p>
3836
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
3837
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
3838
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
3839
     * implemented and used in PHP.
3840
     * </p>
3841
     *
3842
     * @return int
3843
     *             <p>
3844
     *             The number of elements in var, which is
3845
     *             typically an array, since anything else will have one
3846
     *             element.
3847
     *             </p>
3848
     *             <p>
3849
     *             If var is not an array or an object with
3850
     *             implemented Countable interface,
3851
     *             1 will be returned.
3852
     *             There is one exception, if var is &null;,
3853
     *             0 will be returned.
3854
     *             </p>
3855
     *             <p>
3856
     *             Caution: count may return 0 for a variable that isn't set,
3857
     *             but it may also return 0 for a variable that has been initialized with an
3858
     *             empty array. Use isset to test if a variable is set.
3859
     *             </p>
3860
     */
3861 10
    public function sizeRecursive(): int
3862
    {
3863 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
3864
    }
3865
3866
    /**
3867
     * Extract a slice of the array.
3868
     *
3869
     * @param int      $offset       <p>Slice begin index.</p>
3870
     * @param int|null $length       <p>Length of the slice.</p>
3871
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
3872
     *
3873
     * @return static
3874
     *                <p>A slice of the original array with length $length.</p>
3875
     */
3876 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
3877
    {
3878 4
        return static::create(
3879 4
            \array_slice(
3880 4
                $this->getArray(),
3881 4
                $offset,
3882 4
                $length,
3883 4
                $preserveKeys
3884
            ),
3885 4
            $this->iteratorClass,
3886 4
            false
3887
        );
3888
    }
3889
3890
    /**
3891
     * Sort the current array and optional you can keep the keys.
3892
     *
3893
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3894
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
3895
     *                              <strong>SORT_NATURAL</strong></p>
3896
     * @param bool       $keepKeys
3897
     *
3898
     * @return static
3899
     *                <p>(Mutable) Return this Arrayy object.</p>
3900
     */
3901 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
3902
    {
3903 20
        $this->generatorToArray();
3904
3905 20
        return $this->sorting(
3906 20
            $this->array,
3907 20
            $direction,
3908 20
            $strategy,
3909 20
            $keepKeys
3910
        );
3911
    }
3912
3913
    /**
3914
     * Sort the current array by key.
3915
     *
3916
     * @see http://php.net/manual/en/function.ksort.php
3917
     * @see http://php.net/manual/en/function.krsort.php
3918
     *
3919
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3920
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3921
     *                              <strong>SORT_NATURAL</strong></p>
3922
     *
3923
     * @return static
3924
     *                <p>(Mutable) Return this Arrayy object.</p>
3925
     */
3926 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3927
    {
3928 18
        $this->generatorToArray();
3929
3930 18
        $this->sorterKeys($this->array, $direction, $strategy);
3931
3932 18
        return $this;
3933
    }
3934
3935
    /**
3936
     * Sort the current array by value.
3937
     *
3938
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3939
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3940
     *                              <strong>SORT_NATURAL</strong></p>
3941
     *
3942
     * @return static
3943
     *                <p>(Mutable)</p>
3944
     */
3945 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3946
    {
3947 1
        return $this->sort($direction, $strategy, true);
3948
    }
3949
3950
    /**
3951
     * Sort the current array by value.
3952
     *
3953
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3954
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3955
     *                              <strong>SORT_NATURAL</strong></p>
3956
     *
3957
     * @return static
3958
     *                <p>(Mutable)</p>
3959
     */
3960 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3961
    {
3962 1
        return $this->sort($direction, $strategy, false);
3963
    }
3964
3965
    /**
3966
     * Sort a array by value, by a closure or by a property.
3967
     *
3968
     * - If the sorter is null, the array is sorted naturally.
3969
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
3970
     *
3971
     * @param callable|string|null $sorter
3972
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3973
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3974
     *                                        <strong>SORT_NATURAL</strong></p>
3975
     *
3976
     * @return static
3977
     *                <p>(Immutable)</p>
3978
     */
3979 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3980
    {
3981 1
        $array = $this->getArray();
3982 1
        $direction = $this->getDirection($direction);
3983
3984
        // Transform all values into their results.
3985 1
        if ($sorter) {
3986 1
            $arrayy = static::create(
3987 1
                $array,
3988 1
                $this->iteratorClass,
3989 1
                false
3990
            );
3991
3992 1
            $results = $arrayy->each(
3993
                function ($value) use ($sorter) {
3994 1
                    if (\is_callable($sorter)) {
3995 1
                        return $sorter($value);
3996
                    }
3997
3998 1
                    return $this->get($sorter, null, $this->getArray());
3999 1
                }
4000
            );
4001
4002 1
            $results = $results->getArray();
4003
        } else {
4004 1
            $results = $array;
4005
        }
4006
4007
        // Sort by the results and replace by original values
4008 1
        \array_multisort($results, $direction, $strategy, $array);
4009
4010 1
        return static::create(
4011 1
            $array,
4012 1
            $this->iteratorClass,
4013 1
            false
4014
        );
4015
    }
4016
4017
    /**
4018
     * Split an array in the given amount of pieces.
4019
     *
4020
     * @param int  $numberOfPieces
4021
     * @param bool $keepKeys
4022
     *
4023
     * @return static
4024
     *                <p>(Immutable)</p>
4025
     */
4026 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4027
    {
4028 1
        $this->generatorToArray();
4029
4030 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4031
4032 1
        if ($arrayCount === 0) {
4033 1
            $result = [];
4034
        } else {
4035 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4036 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4037
        }
4038
4039 1
        return static::create(
4040 1
            $result,
4041 1
            $this->iteratorClass,
4042 1
            false
4043
        );
4044
    }
4045
4046
    /**
4047
     * Stripe all empty items.
4048
     *
4049
     * @return static
4050
     *                <p>(Immutable)</p>
4051
     */
4052 1
    public function stripEmpty(): self
4053
    {
4054 1
        return $this->filter(
4055
            static function ($item) {
4056 1
                if ($item === null) {
4057 1
                    return false;
4058
                }
4059
4060 1
                return (bool) \trim((string) $item);
4061 1
            }
4062
        );
4063
    }
4064
4065
    /**
4066
     * Swap two values between positions by key.
4067
     *
4068
     * @param int|string $swapA <p>a key in the array</p>
4069
     * @param int|string $swapB <p>a key in the array</p>
4070
     *
4071
     * @return static
4072
     *                <p>(Immutable)</p>
4073
     */
4074 1
    public function swap($swapA, $swapB): self
4075
    {
4076 1
        $array = $this->getArray();
4077
4078 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4079
4080 1
        return static::create(
4081 1
            $array,
4082 1
            $this->iteratorClass,
4083 1
            false
4084
        );
4085
    }
4086
4087
    /**
4088
     * alias: for "Arrayy->getArray()"
4089
     *
4090
     * @see Arrayy::getArray()
4091
     */
4092 198
    public function toArray()
4093
    {
4094 198
        return $this->getArray();
4095
    }
4096
4097
    /**
4098
     * Convert the current array to JSON.
4099
     *
4100
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4101
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4102
     *
4103
     * @return string
4104
     */
4105 6
    public function toJson(int $options = 0, int $depth = 512): string
4106
    {
4107
        /** @noinspection PhpComposerExtensionStubsInspection */
4108 6
        $return = \json_encode($this->getArray(), $options, $depth);
4109 6
        if ($return === false) {
4110
            return '';
4111
        }
4112
4113 6
        return $return;
4114
    }
4115
4116
    /**
4117
     * Implodes array to a string with specified separator.
4118
     *
4119
     * @param string $separator [optional] <p>The element's separator.</p>
4120
     *
4121
     * @return string
4122
     *                <p>The string representation of array, separated by ",".</p>
4123
     */
4124 19
    public function toString(string $separator = ','): string
4125
    {
4126 19
        return $this->implode($separator);
4127
    }
4128
4129
    /**
4130
     * Return a duplicate free copy of the current array.
4131
     *
4132
     * @return static
4133
     *                <p>(Mutable)</p>
4134
     */
4135 12
    public function unique(): self
4136
    {
4137
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4138
4139 12
        $this->array = $this->reduce(
4140
            static function ($resultArray, $value) {
4141 11
                if (!\in_array($value, $resultArray, true)) {
4142 11
                    $resultArray[] = $value;
4143
                }
4144
4145 11
                return $resultArray;
4146 12
            },
4147 12
            []
4148 12
        )->getArray();
4149 12
        $this->generator = null;
4150
4151 12
        return $this;
4152
    }
4153
4154
    /**
4155
     * Return a duplicate free copy of the current array. (with the old keys)
4156
     *
4157
     * @return static
4158
     *                <p>(Mutable)</p>
4159
     */
4160 11
    public function uniqueKeepIndex(): self
4161
    {
4162
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4163
4164
        // init
4165 11
        $array = $this->getArray();
4166
4167 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...
4168 11
            \array_keys($array),
4169
            static function ($resultArray, $key) use ($array) {
4170 10
                if (!\in_array($array[$key], $resultArray, true)) {
4171 10
                    $resultArray[$key] = $array[$key];
4172
                }
4173
4174 10
                return $resultArray;
4175 11
            },
4176 11
            []
4177
        );
4178 11
        $this->generator = null;
4179
4180 11
        if ($this->array === null) {
4181
            $this->array = [];
4182
        } else {
4183 11
            $this->array = (array) $this->array;
4184
        }
4185
4186 11
        return $this;
4187
    }
4188
4189
    /**
4190
     * @param bool $unique
4191
     *
4192
     * @return static
4193
     *                <p>(Immutable)</p>
4194
     */
4195 14
    public function reduce_dimension(bool $unique = true): self
4196
    {
4197
        // init
4198 14
        $result = [[]];
4199
4200 14
        foreach ($this->getGenerator() as $val) {
4201 12
            if (\is_array($val)) {
4202 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
4203
            } else {
4204 12
                $result[] = [$val];
4205
            }
4206
        }
4207 14
        $result = \array_merge(...$result);
4208
4209 14
        $resultArrayy = new self($result);
4210
4211 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4212
    }
4213
4214
    /**
4215
     * alias: for "Arrayy->unique()"
4216
     *
4217
     * @return static
4218
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4219
     *
4220
     * @see Arrayy::unique()
4221
     */
4222 10
    public function uniqueNewIndex(): self
4223
    {
4224 10
        return $this->unique();
4225
    }
4226
4227
    /**
4228
     * Prepends one or more values to the beginning of array at once.
4229
     *
4230
     * @return static
4231
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4232
     */
4233 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...
4234
    {
4235 4
        $this->generatorToArray();
4236
4237 4
        if (\func_num_args()) {
4238 4
            $args = \func_get_args();
4239 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...
4240
        }
4241
4242 4
        return $this;
4243
    }
4244
4245
    /**
4246
     * Get all values from a array.
4247
     *
4248
     * @return static
4249
     *                <p>(Immutable)</p>
4250
     */
4251 2
    public function values(): self
4252
    {
4253 2
        return static::create(
4254
            function () {
4255
                /** @noinspection YieldFromCanBeUsedInspection */
4256 2
                foreach ($this->getGenerator() as $value) {
4257 2
                    yield $value;
4258
                }
4259 2
            },
4260 2
            $this->iteratorClass,
4261 2
            false
4262
        );
4263
    }
4264
4265
    /**
4266
     * Apply the given function to every element in the array, discarding the results.
4267
     *
4268
     * @param callable $callable
4269
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4270
     *
4271
     * @return static
4272
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4273
     */
4274 11
    public function walk($callable, bool $recursive = false): self
4275
    {
4276 11
        $this->generatorToArray();
4277
4278 11
        if ($recursive === true) {
4279 6
            \array_walk_recursive($this->array, $callable);
4280
        } else {
4281 5
            \array_walk($this->array, $callable);
4282
        }
4283
4284 11
        return $this;
4285
    }
4286
4287
    /**
4288
     * Convert an array into a object.
4289
     *
4290
     * @param array $array PHP array
4291
     *
4292
     * @return \stdClass
4293
     */
4294 4
    protected static function arrayToObject(array $array = []): \stdClass
4295
    {
4296
        // init
4297 4
        $object = new \stdClass();
4298
4299 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4300 1
            return $object;
4301
        }
4302
4303 3
        foreach ($array as $name => $value) {
4304 3
            if (\is_array($value)) {
4305 1
                $object->{$name} = self::arrayToObject($value);
4306
            } else {
4307 3
                $object->{$name} = $value;
4308
            }
4309
        }
4310
4311 3
        return $object;
4312
    }
4313
4314
    /**
4315
     * @param array|\Generator|null $input        <p>
4316
     *                                            An array containing keys to return.
4317
     *                                            </p>
4318
     * @param mixed                 $search_value [optional] <p>
4319
     *                                            If specified, then only keys containing these values are returned.
4320
     *                                            </p>
4321
     * @param bool                  $strict       [optional] <p>
4322
     *                                            Determines if strict comparison (===) should be used during the
4323
     *                                            search.
4324
     *                                            </p>
4325
     *
4326
     * @return array
4327
     *               <p>an array of all the keys in input</p>
4328
     */
4329 10
    protected function array_keys_recursive(
4330
        $input = null,
4331
        $search_value = null,
4332
        bool $strict = true
4333
    ): array {
4334
        // init
4335 10
        $keys = [];
4336 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
4337
4338 10
        if ($input === null) {
4339
            $input = $this->getGenerator();
4340
        }
4341
4342 10
        foreach ($input as $key => $value) {
4343
            if (
4344 10
                $search_value === null
4345
                ||
4346
                (
4347
                    \is_array($search_value) === true
4348
                    &&
4349 10
                    \in_array($key, $search_value, $strict)
4350
                )
4351
            ) {
4352 10
                $keys[] = $key;
4353
            }
4354
4355
            // check if recursive is needed
4356 10
            if (\is_array($value) === true) {
4357 10
                $keysTmp[] = $this->array_keys_recursive($value);
4358
            }
4359
        }
4360
4361 10
        return \array_merge($keys, ...$keysTmp);
4362
    }
4363
4364
    /**
4365
     * @param mixed      $path
4366
     * @param callable   $callable
4367
     * @param array|null $currentOffset
4368
     */
4369 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4370
    {
4371 4
        $this->generatorToArray();
4372
4373 4
        if ($currentOffset === null) {
4374 4
            $currentOffset = &$this->array;
4375
        }
4376
4377 4
        $explodedPath = \explode($this->pathSeparator, $path);
4378 4
        if ($explodedPath === false) {
4379
            return;
4380
        }
4381
4382 4
        $nextPath = \array_shift($explodedPath);
4383
4384 4
        if (!isset($currentOffset[$nextPath])) {
4385
            return;
4386
        }
4387
4388 4
        if (!empty($explodedPath)) {
4389 1
            $this->callAtPath(
4390 1
                \implode($this->pathSeparator, $explodedPath),
4391 1
                $callable,
4392 1
                $currentOffset[$nextPath]
4393
            );
4394
        } else {
4395 4
            $callable($currentOffset[$nextPath]);
4396
        }
4397 4
    }
4398
4399
    /**
4400
     * create a fallback for array
4401
     *
4402
     * 1. use the current array, if it's a array
4403
     * 2. fallback to empty array, if there is nothing
4404
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4405
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4406
     * 5. call "__toArray()" on object, if the method exists
4407
     * 6. cast a string or object with "__toString()" into an array
4408
     * 7. throw a "InvalidArgumentException"-Exception
4409
     *
4410
     * @param mixed $data
4411
     *
4412
     * @throws \InvalidArgumentException
4413
     *
4414
     * @return array
4415
     */
4416 940
    protected function fallbackForArray(&$data): array
4417
    {
4418 940
        if (\is_array($data)) {
4419 937
            return $data;
4420
        }
4421
4422 46
        if (!$data) {
4423 6
            return [];
4424
        }
4425
4426 45
        $isObject = \is_object($data);
4427
4428 45
        if ($isObject && $data instanceof self) {
4429 2
            return $data->getArray();
4430
        }
4431
4432 44
        if ($isObject && $data instanceof \ArrayObject) {
4433
            return $data->getArrayCopy();
4434
        }
4435
4436 44
        if ($isObject && $data instanceof \Generator) {
4437
            return static::createFromGeneratorImmutable($data)->getArray();
4438
        }
4439
4440 44
        if ($isObject && $data instanceof \Traversable) {
4441
            return static::createFromObject($data)->getArray();
4442
        }
4443
4444 44
        if (\is_callable($data)) {
4445 37
            $this->generator = new ArrayyRewindableGenerator($data);
4446
4447 37
            return [];
4448
        }
4449
4450 9
        if ($isObject && \method_exists($data, '__toArray')) {
4451
            return (array) $data->__toArray();
4452
        }
4453
4454
        if (
4455 9
            \is_string($data)
4456
            ||
4457 9
            ($isObject && \method_exists($data, '__toString'))
4458
        ) {
4459 7
            return [(string) $data];
4460
        }
4461
4462 2
        throw new \InvalidArgumentException(
4463 2
            'Passed value should be a array'
4464
        );
4465
    }
4466
4467
    /**
4468
     * Get correct PHP constant for direction.
4469
     *
4470
     * @param int|string $direction
4471
     *
4472
     * @return int
4473
     */
4474 39
    protected function getDirection($direction): int
4475
    {
4476 39
        if (\is_string($direction)) {
4477 10
            $direction = \strtolower($direction);
4478
4479 10
            if ($direction === 'desc') {
4480 2
                $direction = \SORT_DESC;
4481
            } else {
4482 8
                $direction = \SORT_ASC;
4483
            }
4484
        }
4485
4486
        if (
4487 39
            $direction !== \SORT_DESC
4488
            &&
4489 39
            $direction !== \SORT_ASC
4490
        ) {
4491
            $direction = \SORT_ASC;
4492
        }
4493
4494 39
        return $direction;
4495
    }
4496
4497
    /**
4498
     * @param mixed               $glue
4499
     * @param array|static|string $pieces
4500
     * @param bool                $useKeys
4501
     *
4502
     * @return string
4503
     */
4504 35
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4505
    {
4506 35
        if ($pieces instanceof self) {
4507 1
            $pieces = $pieces->getArray();
4508
        }
4509
4510 35
        if (\is_array($pieces)) {
4511 35
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4512 35
            $pieces_count_not_zero = $pieces_count > 0;
4513
4514 35
            return \implode(
4515 35
                $glue,
4516 35
                \array_map(
4517 35
                    [$this, 'implode_recursive'],
4518 35
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4519 35
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4520
                )
4521
            );
4522
        }
4523
4524 35
        return (string) $pieces;
4525
    }
4526
4527
    /**
4528
     * @param mixed                 $needle   <p>
4529
     *                                        The searched value.
4530
     *                                        </p>
4531
     *                                        <p>
4532
     *                                        If needle is a string, the comparison is done
4533
     *                                        in a case-sensitive manner.
4534
     *                                        </p>
4535
     * @param array|\Generator|null $haystack <p>
4536
     *                                        The array.
4537
     *                                        </p>
4538
     * @param bool                  $strict   [optional] <p>
4539
     *                                        If the third parameter strict is set to true
4540
     *                                        then the in_array function will also check the
4541
     *                                        types of the
4542
     *                                        needle in the haystack.
4543
     *                                        </p>
4544
     *
4545
     * @return bool
4546
     *              <p>true if needle is found in the array, false otherwise</p>
4547
     */
4548 19
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4549
    {
4550 19
        if ($haystack === null) {
4551
            $haystack = $this->getGenerator();
4552
        }
4553
4554 19
        foreach ($haystack as $item) {
4555 15
            if (\is_array($item) === true) {
4556 4
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4557
            } else {
4558
                /** @noinspection NestedPositiveIfStatementsInspection */
4559 15
                if ($strict === true) {
4560 15
                    $returnTmp = $item === $needle;
4561
                } else {
4562
                    $returnTmp = $item == $needle;
4563
                }
4564
            }
4565
4566 15
            if ($returnTmp === true) {
4567 15
                return true;
4568
            }
4569
        }
4570
4571 8
        return false;
4572
    }
4573
4574
    /**
4575
     * @param mixed $value
4576
     */
4577
    protected function internalGetArray(&$value)
4578
    {
4579
        if ($value instanceof self) {
4580
            $valueTmp = $value->getArray();
4581
            if (\count($valueTmp, \COUNT_NORMAL) === 0) {
4582
                $value = [];
4583
            } else {
4584
                /** @noinspection PhpUnusedLocalVariableInspection */
4585
                $value = &$valueTmp;
4586
            }
4587
        }
4588
4589
        /** @noinspection PhpComposerExtensionStubsInspection */
4590
        /** @noinspection NotOptimalIfConditionsInspection */
4591
        if (
4592
            \class_exists('JsonSerializable')
4593
            &&
4594
            $value 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...
4595
        ) {
4596
4597
            /** @noinspection PhpUnusedLocalVariableInspection */
4598
            $value = &$value->jsonSerialize();
4599
        }
4600
    }
4601
4602
    /**
4603
     * Internal mechanics of remove method.
4604
     *
4605
     * @param mixed $key
4606
     *
4607
     * @return bool
4608
     */
4609 18
    protected function internalRemove($key): bool
4610
    {
4611 18
        $this->generatorToArray();
4612
4613
        if (
4614 18
            $this->pathSeparator
4615
            &&
4616 18
            \is_string($key)
4617
            &&
4618 18
            \strpos($key, $this->pathSeparator) !== false
4619
        ) {
4620
            $path = \explode($this->pathSeparator, (string) $key);
4621
4622
            if ($path !== false) {
4623
                // crawl though the keys
4624
                while (\count($path, \COUNT_NORMAL) > 1) {
4625
                    $key = \array_shift($path);
4626
4627
                    if (!$this->has($key)) {
4628
                        return false;
4629
                    }
4630
4631
                    $this->array = &$this->array[$key];
4632
                }
4633
4634
                $key = \array_shift($path);
4635
            }
4636
        }
4637
4638 18
        unset($this->array[$key]);
4639
4640 18
        return true;
4641
    }
4642
4643
    /**
4644
     * Internal mechanic of set method.
4645
     *
4646
     * @param int|string|null $key
4647
     * @param mixed           $value
4648
     * @param bool            $checkProperties
4649
     *
4650
     * @return bool
4651
     */
4652 823
    protected function internalSet($key, $value, $checkProperties = true): bool
4653
    {
4654
        if (
4655 823
            $checkProperties === true
4656
            &&
4657 823
            $this->properties !== []
4658
        ) {
4659 13
            if (isset($this->properties[$key]) === false) {
4660
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4661
            }
4662
4663 13
            $this->properties[$key]->checkType($value);
4664
        }
4665
4666 822
        if ($key === null) {
4667
            return false;
4668
        }
4669
4670 822
        $this->generatorToArray();
4671
4672 822
        $array = &$this->array;
4673
4674
        if (
4675 822
            $this->pathSeparator
4676
            &&
4677 822
            \is_string($key)
4678
            &&
4679 822
            \strpos($key, $this->pathSeparator) !== false
4680
        ) {
4681 3
            $path = \explode($this->pathSeparator, (string) $key);
4682
4683 3
            if ($path !== false) {
4684
                // crawl through the keys
4685 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4686 3
                    $key = \array_shift($path);
4687
4688 3
                    $array = &$array[$key];
4689
                }
4690
4691 3
                $key = \array_shift($path);
4692
            }
4693
        }
4694
4695 822
        $array[$key] = $value;
4696
4697 822
        return true;
4698
    }
4699
4700
    /**
4701
     * Convert a object into an array.
4702
     *
4703
     * @param object $object
4704
     *
4705
     * @return mixed
4706
     */
4707 5
    protected static function objectToArray($object)
4708
    {
4709 5
        if (!\is_object($object)) {
4710 4
            return $object;
4711
        }
4712
4713 5
        if (\is_object($object)) {
4714 5
            $object = \get_object_vars($object);
4715
        }
4716
4717 5
        return \array_map(['static', 'objectToArray'], $object);
4718
    }
4719
4720
    /**
4721
     * sorting keys
4722
     *
4723
     * @param array      $elements
4724
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4725
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4726
     *                              <strong>SORT_NATURAL</strong></p>
4727
     *
4728
     * @return static
4729
     *                <p>(Mutable) Return this Arrayy object.</p>
4730
     */
4731 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4732
    {
4733 18
        $direction = $this->getDirection($direction);
4734
4735
        switch ($direction) {
4736 18
            case 'desc':
4737 18
            case \SORT_DESC:
4738 6
                \krsort($elements, $strategy);
4739
4740 6
                break;
4741 13
            case 'asc':
4742 13
            case \SORT_ASC:
4743
            default:
4744 13
                \ksort($elements, $strategy);
4745
        }
4746
4747 18
        return $this;
4748
    }
4749
4750
    /**
4751
     * @param array      $elements  <p>Warning: used as reference</p>
4752
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4753
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4754
     *                              <strong>SORT_NATURAL</strong></p>
4755
     * @param bool       $keepKeys
4756
     *
4757
     * @return static
4758
     *                <p>(Mutable) Return this Arrayy object.</p>
4759
     */
4760 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4761
    {
4762 20
        $direction = $this->getDirection($direction);
4763
4764 20
        if (!$strategy) {
4765 20
            $strategy = \SORT_REGULAR;
4766
        }
4767
4768
        switch ($direction) {
4769 20
            case 'desc':
4770 20
            case \SORT_DESC:
4771 9
                if ($keepKeys) {
4772 5
                    \arsort($elements, $strategy);
4773
                } else {
4774 4
                    \rsort($elements, $strategy);
4775
                }
4776
4777 9
                break;
4778 11
            case 'asc':
4779 11
            case \SORT_ASC:
4780
            default:
4781 11
                if ($keepKeys) {
4782 4
                    \asort($elements, $strategy);
4783
                } else {
4784 7
                    \sort($elements, $strategy);
4785
                }
4786
        }
4787
4788 20
        return $this;
4789
    }
4790
4791
    /**
4792
     * @return bool
4793
     */
4794 858
    private function generatorToArray(): bool
4795
    {
4796 858
        if ($this->generator) {
4797 1
            $this->array = $this->getArray();
4798 1
            $this->generator = null;
4799
4800 1
            return true;
4801
        }
4802
4803 858
        return false;
4804
    }
4805
4806
    /**
4807
     * @return Property[]
4808
     */
4809 15
    private function getPropertiesFromPhpDoc(): array
4810
    {
4811 15
        static $PROPERTY_CACHE = [];
4812 15
        $cacheKey = 'Class::' . static::class;
4813
4814 15
        if (isset($PROPERTY_CACHE[$cacheKey])) {
4815 14
            return $PROPERTY_CACHE[$cacheKey];
4816
        }
4817
4818
        // init
4819 2
        $properties = [];
4820
4821 2
        $reflector = new \ReflectionClass($this);
4822 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
4823 2
        $docComment = $reflector->getDocComment();
4824 2
        if ($docComment) {
4825 2
            $docblock = $factory->create($docComment);
4826 2
            foreach ($docblock->getTagsByName('property') as $tag) {
4827
                /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
4828 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
4829
            }
4830
        }
4831
4832 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
4833
    }
4834
}
4835