Completed
Push — master ( 65928f...2d3df6 )
by Lars
02:13
created

Arrayy::containsKeys()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 24
ccs 14
cts 14
cp 1
crap 2
rs 9.536
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, false);
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 3
                        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 2
                    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 8
            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 22
    public function contains($value, bool $recursive = false, bool $strict = true): bool
915
    {
916 22
        if ($recursive === true) {
917 18
            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
            /** @noinspection PhpComposerExtensionStubsInspection */
948 26
            return $this->in_array_recursive(
949 26
                \mb_strtoupper((string) $value),
950 26
                $this->walk(
951 26
                    static function (&$val) {
952
                        /** @noinspection PhpComposerExtensionStubsInspection */
953 22
                        $val = \mb_strtoupper((string) $val);
954 26
                    },
955 26
                    true
956 26
                )->getArray(),
957 26
                true
958
            );
959
        }
960
961
        /** @noinspection PhpComposerExtensionStubsInspection */
962 13
        return \in_array(
963 13
            \mb_strtoupper((string) $value),
964 13
            $this->walk(
965 13
                static function (&$val) {
966
                    /** @noinspection PhpComposerExtensionStubsInspection */
967 11
                    $val = \mb_strtoupper((string) $val);
968 13
                },
969 13
                false
970 13
            )->getArray(),
971 13
            true
972
        );
973
    }
974
975
    /**
976
     * Check if the given key/index exists in the array.
977
     *
978
     * @param int|string $key <p>key/index to search for</p>
979
     *
980
     * @return bool
981
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
982
     */
983 4
    public function containsKey($key): bool
984
    {
985 4
        return $this->offsetExists($key);
986
    }
987
988
    /**
989
     * Check if all given needles are present in the array as key/index.
990
     *
991
     * @param array $needles   <p>The keys you are searching for.</p>
992
     * @param bool  $recursive
993
     *
994
     * @return bool
995
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
996
     */
997 2
    public function containsKeys(array $needles, $recursive = false): bool
998
    {
999 2
        if ($recursive === true) {
1000 2
            return \count(
1001 2
                       \array_intersect($needles, $this->keys(true)->getArray()),
1002 2
                       \COUNT_RECURSIVE
1003
                   )
1004
                   ===
1005 2
                   \count(
1006 2
                       $needles,
1007 2
                       \COUNT_RECURSIVE
1008
                   );
1009
        }
1010
1011 1
        return \count(
1012 1
                   \array_intersect($needles, $this->keys()->getArray()),
1013 1
                   \COUNT_NORMAL
1014
               )
1015
               ===
1016 1
               \count(
1017 1
                   $needles,
1018 1
                   \COUNT_NORMAL
1019
               );
1020
    }
1021
1022
    /**
1023
     * Check if all given needles are present in the array as key/index.
1024
     *
1025
     * @param array $needles <p>The keys you are searching for.</p>
1026
     *
1027
     * @return bool
1028
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1029
     */
1030 1
    public function containsKeysRecursive(array $needles): bool
1031
    {
1032 1
        return $this->containsKeys($needles, true);
1033
    }
1034
1035
    /**
1036
     * alias: for "Arrayy->contains()"
1037
     *
1038
     * @param float|int|string $value
1039
     *
1040
     * @return bool
1041
     *
1042
     * @see Arrayy::contains()
1043
     */
1044 9
    public function containsValue($value): bool
1045
    {
1046 9
        return $this->contains($value);
1047
    }
1048
1049
    /**
1050
     * alias: for "Arrayy->contains($value, true)"
1051
     *
1052
     * @param float|int|string $value
1053
     *
1054
     * @return bool
1055
     *
1056
     * @see Arrayy::contains()
1057
     */
1058 18
    public function containsValueRecursive($value): bool
1059
    {
1060 18
        return $this->contains($value, true);
1061
    }
1062
1063
    /**
1064
     * Check if all given needles are present in the array.
1065
     *
1066
     * @param array $needles
1067
     *
1068
     * @return bool
1069
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1070
     */
1071 1
    public function containsValues(array $needles): bool
1072
    {
1073 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1074
               ===
1075 1
               \count($needles, \COUNT_NORMAL);
1076
    }
1077
1078
    /**
1079
     * Counts all the values of an array
1080
     *
1081
     * @see http://php.net/manual/en/function.array-count-values.php
1082
     *
1083
     * @return static
1084
     *                <p>
1085
     *                (Immutable)
1086
     *                An associative Arrayy-object of values from input as
1087
     *                keys and their count as value.
1088
     *                </p>
1089
     */
1090 1
    public function countValues(): self
1091
    {
1092 1
        return new static(\array_count_values($this->getArray()));
1093
    }
1094
1095
    /**
1096
     * Creates an Arrayy object.
1097
     *
1098
     * @param mixed  $array
1099
     * @param string $iteratorClass
1100
     * @param bool   $checkForMissingPropertiesInConstructor
1101
     *
1102
     * @return static
1103
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1104
     */
1105 556
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1106
    {
1107 556
        return new static(
1108 556
            $array,
1109 556
            $iteratorClass,
1110 556
            $checkForMissingPropertiesInConstructor
1111
        );
1112
    }
1113
1114
    /**
1115
     * WARNING: Creates an Arrayy object by reference.
1116
     *
1117
     * @param array $array
1118
     *
1119
     * @return static
1120
     *                <p>(Mutable) Return this Arrayy object.</p>
1121
     */
1122 1
    public function createByReference(array &$array = []): self
1123
    {
1124 1
        $array = $this->fallbackForArray($array);
1125
1126 1
        $this->array = &$array;
1127
1128 1
        return $this;
1129
    }
1130
1131
    /**
1132
     * Create an new instance from a callable function which will return an Generator.
1133
     *
1134
     * @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...
1135
     *
1136
     * @return static
1137
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1138
     */
1139 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1140
    {
1141 5
        $arrayy = new static($generatorFunction);
1142
1143 5
        return $arrayy;
1144
    }
1145
1146
    /**
1147
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1148
     *
1149
     * @param \Traversable $traversable
1150
     *
1151
     * @return static
1152
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1153
     */
1154
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1155
    {
1156
        return new static(\iterator_to_array($traversable, true));
1157
    }
1158
1159
    /**
1160
     * Create an new instance filled with a copy of values from a "Generator"-object.
1161
     *
1162
     * @param \Generator $generator
1163
     *
1164
     * @return static
1165
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1166
     */
1167 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1168
    {
1169 4
        return new static(\iterator_to_array($generator, true));
1170
    }
1171
1172
    /**
1173
     * Create an new Arrayy object via JSON.
1174
     *
1175
     * @param string $json
1176
     *
1177
     * @return static
1178
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1179
     */
1180 5
    public static function createFromJson(string $json): self
1181
    {
1182
        /** @noinspection PhpComposerExtensionStubsInspection */
1183 5
        return static::create(\json_decode($json, true));
1184
    }
1185
1186
    /**
1187
     * Create an new instance filled with values from an object that is iterable.
1188
     *
1189
     * @param \Traversable $object <p>iterable object</p>
1190
     *
1191
     * @return static
1192
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1193
     */
1194 4
    public static function createFromObject(\Traversable $object): self
1195
    {
1196
        // init
1197 4
        $array = new static();
1198
1199 4
        if ($object instanceof self) {
1200 4
            $objectArray = $object->getGenerator();
1201
        } else {
1202
            $objectArray = $object;
1203
        }
1204
1205 4
        foreach ($objectArray as $key => $value) {
1206 3
            $array[$key] = $value;
1207
        }
1208
1209 4
        return $array;
1210
    }
1211
1212
    /**
1213
     * Create an new instance filled with values from an object.
1214
     *
1215
     * @param object $object
1216
     *
1217
     * @return static
1218
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1219
     */
1220 5
    public static function createFromObjectVars($object): self
1221
    {
1222 5
        return new static(self::objectToArray($object));
1223
    }
1224
1225
    /**
1226
     * Create an new Arrayy object via string.
1227
     *
1228
     * @param string      $str       <p>The input string.</p>
1229
     * @param string|null $delimiter <p>The boundary string.</p>
1230
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1231
     *                               used.</p>
1232
     *
1233
     * @return static
1234
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1235
     */
1236 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1237
    {
1238 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...
1239 1
            \preg_match_all($regEx, $str, $array);
1240
1241 1
            if (!empty($array)) {
1242 1
                $array = $array[0];
1243
            }
1244
        } else {
1245
            /** @noinspection NestedPositiveIfStatementsInspection */
1246 9
            if ($delimiter !== null) {
1247 7
                $array = \explode($delimiter, $str);
1248
            } else {
1249 2
                $array = [$str];
1250
            }
1251
        }
1252
1253
        // trim all string in the array
1254 10
        \array_walk(
1255
            $array,
1256 10
            static function (&$val) {
1257 10
                if (\is_string($val)) {
1258 10
                    $val = \trim($val);
1259
                }
1260 10
            }
1261
        );
1262
1263 10
        return static::create($array);
1264
    }
1265
1266
    /**
1267
     * Create an new instance containing a range of elements.
1268
     *
1269
     * @param mixed $low  <p>First value of the sequence.</p>
1270
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1271
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1272
     *
1273
     * @return static
1274
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1275
     */
1276 2
    public static function createWithRange($low, $high, int $step = 1): self
1277
    {
1278 2
        return static::create(\range($low, $high, $step));
1279
    }
1280
1281
    /**
1282
     * Custom sort by index via "uksort".
1283
     *
1284
     * @see http://php.net/manual/en/function.uksort.php
1285
     *
1286
     * @param callable $function
1287
     *
1288
     * @throws \InvalidArgumentException
1289
     *
1290
     * @return static
1291
     *                <p>(Mutable) Return this Arrayy object.</p>
1292
     */
1293 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...
1294
    {
1295 5
        if (!\is_callable($function)) {
1296
            throw new \InvalidArgumentException(
1297
                'Passed function must be callable'
1298
            );
1299
        }
1300
1301 5
        $this->generatorToArray();
1302
1303 5
        \uksort($this->array, $function);
1304
1305 5
        return $this;
1306
    }
1307
1308
    /**
1309
     * Custom sort by value via "usort".
1310
     *
1311
     * @see http://php.net/manual/en/function.usort.php
1312
     *
1313
     * @param callable $function
1314
     *
1315
     * @throws \InvalidArgumentException
1316
     *
1317
     * @return static
1318
     *                <p>(Mutable) Return this Arrayy object.</p>
1319
     */
1320 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...
1321
    {
1322 5
        if (!\is_callable($function)) {
1323
            throw new \InvalidArgumentException(
1324
                'Passed function must be callable'
1325
            );
1326
        }
1327
1328 5
        $this->generatorToArray();
1329
1330 5
        \usort($this->array, $function);
1331
1332 5
        return $this;
1333
    }
1334
1335
    /**
1336
     * Return values that are only in the current array.
1337
     *
1338
     * @param array $array
1339
     *
1340
     * @return static
1341
     *                <p>(Immutable)</p>
1342
     */
1343 12
    public function diff(array $array = []): self
1344
    {
1345 12
        return static::create(
1346 12
            \array_diff($this->getArray(), $array),
1347 12
            $this->iteratorClass,
1348 12
            false
1349
        );
1350
    }
1351
1352
    /**
1353
     * Return values that are only in the current multi-dimensional array.
1354
     *
1355
     * @param array      $array
1356
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1357
     *
1358
     * @return static
1359
     *                <p>(Immutable)</p>
1360
     */
1361 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1362
    {
1363
        // init
1364 1
        $result = [];
1365
1366
        if (
1367 1
            $helperVariableForRecursion !== null
1368
            &&
1369 1
            \is_array($helperVariableForRecursion)
1370
        ) {
1371
            $arrayForTheLoop = $helperVariableForRecursion;
1372
        } else {
1373 1
            $arrayForTheLoop = $this->getGenerator();
1374
        }
1375
1376 1
        foreach ($arrayForTheLoop as $key => $value) {
1377 1
            if ($value instanceof self) {
1378
                $value = $value->getArray();
1379
            }
1380
1381 1
            if (\array_key_exists($key, $array)) {
1382 1
                if ($value !== $array[$key]) {
1383 1
                    $result[$key] = $value;
1384
                }
1385
            } else {
1386 1
                $result[$key] = $value;
1387
            }
1388
        }
1389
1390 1
        return static::create(
1391 1
            $result,
1392 1
            $this->iteratorClass,
1393 1
            false
1394
        );
1395
    }
1396
1397
    /**
1398
     * Return values that are only in the new $array.
1399
     *
1400
     * @param array $array
1401
     *
1402
     * @return static
1403
     *                <p>(Immutable)</p>
1404
     */
1405 8
    public function diffReverse(array $array = []): self
1406
    {
1407 8
        return static::create(
1408 8
            \array_diff($array, $this->getArray()),
1409 8
            $this->iteratorClass,
1410 8
            false
1411
        );
1412
    }
1413
1414
    /**
1415
     * Divide an array into two arrays. One with keys and the other with values.
1416
     *
1417
     * @return static
1418
     *                <p>(Immutable)</p>
1419
     */
1420 1
    public function divide(): self
1421
    {
1422 1
        return static::create(
1423
            [
1424 1
                $this->keys(),
1425 1
                $this->values(),
1426
            ],
1427 1
            $this->iteratorClass,
1428 1
            false
1429
        );
1430
    }
1431
1432
    /**
1433
     * Iterate over the current array and modify the array's value.
1434
     *
1435
     * @param \Closure $closure
1436
     *
1437
     * @return static
1438
     *                <p>(Immutable)</p>
1439
     */
1440 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...
1441
    {
1442
        // init
1443 4
        $array = [];
1444
1445 4
        foreach ($this->getGenerator() as $key => $value) {
1446 4
            $array[$key] = $closure($value, $key);
1447
        }
1448
1449 4
        return static::create(
1450 4
            $array,
1451 4
            $this->iteratorClass,
1452 4
            false
1453
        );
1454
    }
1455
1456
    /**
1457
     * Check if a value is in the current array using a closure.
1458
     *
1459
     * @param \Closure $closure
1460
     *
1461
     * @return bool
1462
     *              <p>Returns true if the given value is found, false otherwise.</p>
1463
     */
1464 4
    public function exists(\Closure $closure): bool
1465
    {
1466
        // init
1467 4
        $isExists = false;
1468
1469 4
        foreach ($this->getGenerator() as $key => $value) {
1470 3
            if ($closure($value, $key)) {
1471 1
                $isExists = true;
1472
1473 3
                break;
1474
            }
1475
        }
1476
1477 4
        return $isExists;
1478
    }
1479
1480
    /**
1481
     * Fill the array until "$num" with "$default" values.
1482
     *
1483
     * @param int   $num
1484
     * @param mixed $default
1485
     *
1486
     * @return static
1487
     *                <p>(Immutable)</p>
1488
     */
1489 8
    public function fillWithDefaults(int $num, $default = null): self
1490
    {
1491 8
        if ($num < 0) {
1492 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1493
        }
1494
1495 7
        $this->generatorToArray();
1496
1497 7
        $tmpArray = $this->array;
1498
1499 7
        $count = \count($tmpArray);
1500
1501 7
        while ($count < $num) {
1502 4
            $tmpArray[] = $default;
1503 4
            ++$count;
1504
        }
1505
1506 7
        return static::create(
1507 7
            $tmpArray,
1508 7
            $this->iteratorClass,
1509 7
            false
1510
        );
1511
    }
1512
1513
    /**
1514
     * Find all items in an array that pass the truth test.
1515
     *
1516
     * @param \Closure|null $closure [optional] <p>
1517
     *                               The callback function to use
1518
     *                               </p>
1519
     *                               <p>
1520
     *                               If no callback is supplied, all entries of
1521
     *                               input equal to false (see
1522
     *                               converting to
1523
     *                               boolean) will be removed.
1524
     *                               </p>
1525
     *                               * @param int $flag [optional] <p>
1526
     *                               Flag determining what arguments are sent to <i>callback</i>:
1527
     *                               </p><ul>
1528
     *                               <li>
1529
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1530
     *                               to <i>callback</i> instead of the value</span>
1531
     *                               </li>
1532
     *                               <li>
1533
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1534
     *                               arguments to <i>callback</i> instead of the value</span>
1535
     *                               </li>
1536
     *                               </ul>
1537
     *
1538
     * @return static
1539
     *                <p>(Immutable)</p>
1540
     */
1541 11
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1542
    {
1543 11
        if (!$closure) {
1544 1
            return $this->clean();
1545
        }
1546
1547 11
        return static::create(
1548 11
            \array_filter($this->getArray(), $closure, $flag),
1549 11
            $this->iteratorClass,
1550 11
            false
1551
        );
1552
    }
1553
1554
    /**
1555
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1556
     * property within that.
1557
     *
1558
     * @param string          $property
1559
     * @param string|string[] $value
1560
     * @param string          $comparisonOp
1561
     *                                      <p>
1562
     *                                      'eq' (equals),<br />
1563
     *                                      'gt' (greater),<br />
1564
     *                                      'gte' || 'ge' (greater or equals),<br />
1565
     *                                      'lt' (less),<br />
1566
     *                                      'lte' || 'le' (less or equals),<br />
1567
     *                                      'ne' (not equals),<br />
1568
     *                                      'contains',<br />
1569
     *                                      'notContains',<br />
1570
     *                                      'newer' (via strtotime),<br />
1571
     *                                      'older' (via strtotime),<br />
1572
     *                                      </p>
1573
     *
1574
     * @return static
1575
     *                <p>(Immutable)</p>
1576
     */
1577 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1578
    {
1579 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...
1580 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1581
        }
1582
1583
        $ops = [
1584 1
            'eq' => static function ($item, $prop, $value) {
1585 1
                return $item[$prop] === $value;
1586 1
            },
1587 1
            'gt' => static function ($item, $prop, $value) {
1588
                return $item[$prop] > $value;
1589 1
            },
1590 1
            'ge' => static function ($item, $prop, $value) {
1591
                return $item[$prop] >= $value;
1592 1
            },
1593 1
            'gte' => static function ($item, $prop, $value) {
1594
                return $item[$prop] >= $value;
1595 1
            },
1596 1
            'lt' => static function ($item, $prop, $value) {
1597 1
                return $item[$prop] < $value;
1598 1
            },
1599 1
            'le' => static function ($item, $prop, $value) {
1600
                return $item[$prop] <= $value;
1601 1
            },
1602 1
            'lte' => static function ($item, $prop, $value) {
1603
                return $item[$prop] <= $value;
1604 1
            },
1605 1
            'ne' => static function ($item, $prop, $value) {
1606
                return $item[$prop] !== $value;
1607 1
            },
1608 1
            'contains' => static function ($item, $prop, $value) {
1609 1
                return \in_array($item[$prop], (array) $value, true);
1610 1
            },
1611 1
            'notContains' => static function ($item, $prop, $value) {
1612
                return !\in_array($item[$prop], (array) $value, true);
1613 1
            },
1614 1
            'newer' => static function ($item, $prop, $value) {
1615
                return \strtotime($item[$prop]) > \strtotime($value);
1616 1
            },
1617 1
            'older' => static function ($item, $prop, $value) {
1618
                return \strtotime($item[$prop]) < \strtotime($value);
1619 1
            },
1620
        ];
1621
1622 1
        $result = \array_values(
1623 1
            \array_filter(
1624 1
                $this->getArray(),
1625 1
                static function ($item) use (
1626 1
                    $property,
1627 1
                    $value,
1628 1
                    $ops,
1629 1
                    $comparisonOp
1630
                ) {
1631 1
                    $item = (array) $item;
1632 1
                    $itemArrayy = new static($item);
1633 1
                    $item[$property] = $itemArrayy->get($property, []);
1634
1635 1
                    return $ops[$comparisonOp]($item, $property, $value);
1636 1
                }
1637
            )
1638
        );
1639
1640 1
        return static::create(
1641 1
            $result,
1642 1
            $this->iteratorClass,
1643 1
            false
1644
        );
1645
    }
1646
1647
    /**
1648
     * Find the first item in an array that passes the truth test,
1649
     *  otherwise return false
1650
     *
1651
     * @param \Closure $closure
1652
     *
1653
     * @return false|mixed
1654
     *                     <p>Return false if we did not find the value.</p>
1655
     */
1656 8
    public function find(\Closure $closure)
1657
    {
1658 8
        foreach ($this->getGenerator() as $key => $value) {
1659 6
            if ($closure($value, $key)) {
1660 6
                return $value;
1661
            }
1662
        }
1663
1664 3
        return false;
1665
    }
1666
1667
    /**
1668
     * find by ...
1669
     *
1670
     * @param string          $property
1671
     * @param string|string[] $value
1672
     * @param string          $comparisonOp
1673
     *
1674
     * @return static
1675
     *                <p>(Immutable)</p>
1676
     */
1677
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1678
    {
1679
        return $this->filterBy($property, $value, $comparisonOp);
1680
    }
1681
1682
    /**
1683
     * Get the first value from the current array.
1684
     *
1685
     * @return mixed
1686
     *               <p>Return null if there wasn't a element.</p>
1687
     */
1688 13
    public function first()
1689
    {
1690 13
        $tmpArray = $this->getArray();
1691
1692 13
        return \array_shift($tmpArray);
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 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 73
    public function get($key, $fallback = null, array $array = null)
1771
    {
1772 73
        if ($array !== null) {
1773 5
            $usedArray = $array;
1774
        } else {
1775 68
            $this->generatorToArray();
1776
1777 68
            $usedArray = $this->array;
1778
        }
1779
1780 73
        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 73
        if ((bool) $key === $key) {
1790 3
            $key = (int) $key;
1791
        }
1792
1793 73
        if (\array_key_exists($key, $usedArray) === true) {
1794 62
            if (\is_array($usedArray[$key])) {
1795 9
                return static::create(
1796 9
                    $usedArray[$key],
1797 9
                    $this->iteratorClass,
1798 9
                    false
1799
                );
1800
            }
1801
1802 55
            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 802
    public function getArray(): array
1869
    {
1870
        // init
1871 802
        $array = [];
1872
1873 802
        foreach ($this->getGenerator() as $key => $value) {
1874 693
            $array[$key] = $value;
1875
        }
1876
1877 802
        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 851
    public function getGenerator(): \Generator
1908
    {
1909 851
        if ($this->generator instanceof ArrayyRewindableGenerator) {
1910 36
            yield from $this->generator;
1911
        }
1912
1913 851
        yield from $this->array;
1914 824
    }
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 1
                    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 = StaticArrayy::repeat(
2215 1
                $arguments,
2216 1
                \count($this->getArray(), \COUNT_NORMAL)
2217 1
            )->getArray();
2218
        }
2219
2220
        // If the callable has arguments, pass them.
2221 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...
2222 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2223
        } else {
2224 1
            $array = $this->map($callable, false);
2225
        }
2226
2227 1
        return static::create(
2228 1
            $array,
2229 1
            $this->iteratorClass,
2230 1
            false
2231
        );
2232
    }
2233
2234
    /**
2235
     * Check whether array is associative or not.
2236
     *
2237
     * @param bool $recursive
2238
     *
2239
     * @return bool
2240
     *              <p>Returns true if associative, false otherwise.</p>
2241
     */
2242 15
    public function isAssoc(bool $recursive = false): bool
2243
    {
2244 15
        if ($this->isEmpty()) {
2245 3
            return false;
2246
        }
2247
2248 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2249 13
            if (!\is_string($key)) {
2250 13
                return false;
2251
            }
2252
        }
2253
2254 3
        return true;
2255
    }
2256
2257
    /**
2258
     * Check whether the array is empty or not.
2259
     *
2260
     * @return bool
2261
     *              <p>Returns true if empty, false otherwise.</p>
2262
     */
2263 38
    public function isEmpty(): bool
2264
    {
2265 38
        if ($this->generator) {
2266
            return $this->getArray() === [];
2267
        }
2268
2269 38
        return $this->array === [];
2270
    }
2271
2272
    /**
2273
     * Check if the current array is equal to the given "$array" or not.
2274
     *
2275
     * @param array $array
2276
     *
2277
     * @return bool
2278
     */
2279
    public function isEqual(array $array): bool
2280
    {
2281
        return $this->getArray() === $array;
2282
    }
2283
2284
    /**
2285
     * Check if the current array is a multi-array.
2286
     *
2287
     * @return bool
2288
     */
2289 14
    public function isMultiArray(): bool
2290
    {
2291
        return !(
2292 14
            \count($this->getArray(), \COUNT_NORMAL)
2293
            ===
2294 14
            \count($this->getArray(), \COUNT_RECURSIVE)
2295
        );
2296
    }
2297
2298
    /**
2299
     * Check whether array is numeric or not.
2300
     *
2301
     * @return bool
2302
     *              <p>Returns true if numeric, false otherwise.</p>
2303
     */
2304 5
    public function isNumeric(): bool
2305
    {
2306 5
        if ($this->isEmpty()) {
2307 2
            return false;
2308
        }
2309
2310 4
        foreach ($this->keys()->getGenerator() as $key) {
2311 4
            if (!\is_int($key)) {
2312 4
                return false;
2313
            }
2314
        }
2315
2316 2
        return true;
2317
    }
2318
2319
    /**
2320
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2321
     *
2322
     * @param bool $recursive
2323
     *
2324
     * @return bool
2325
     */
2326 1
    public function isSequential(bool $recursive = false): bool
2327
    {
2328
2329
        // recursive
2330
2331 1
        if ($recursive === true) {
2332
            return $this->array_keys_recursive($this->getArray())
2333
                   ===
2334
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2335
        }
2336
2337
        // non recursive
2338
2339 1
        return \array_keys($this->getArray())
2340
               ===
2341 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2342
    }
2343
2344
    /**
2345
     * @return array
2346
     */
2347
    public function jsonSerialize(): array
2348
    {
2349
        return $this->getArray();
2350
    }
2351
2352
    /**
2353
     * Get all keys from the current array.
2354
     *
2355
     * @param bool  $recursive    [optional] <p>
2356
     *                            Get all keys, also from all sub-arrays from an multi-dimensional array.
2357
     *                            </p>
2358
     * @param mixed $search_value [optional] <p>
2359
     *                            If specified, then only keys containing these values are returned.
2360
     *                            </p>
2361
     * @param bool  $strict       [optional] <p>
2362
     *                            Determines if strict comparison (===) should be used during the search.
2363
     *                            </p>
2364
     *
2365
     * @return static
2366
     *                <p>(Immutable) An array of all the keys in input.</p>
2367
     */
2368 26
    public function keys(bool $recursive = false, $search_value = null, bool $strict = true): self
2369
    {
2370
2371
        // recursive
2372
2373 26
        if ($recursive === true) {
2374 3
            if ($search_value === null) {
2375 3
                $array = $this->array_keys_recursive($this->getArray());
2376
            } else {
2377
                $array = $this->array_keys_recursive($this->getArray(), $search_value, $strict);
2378
            }
2379
2380 3
            return static::create(
2381 3
                $array,
2382 3
                $this->iteratorClass,
2383 3
                false
2384
            );
2385
        }
2386
2387
        // non recursive
2388
2389 25
        if ($search_value === null) {
2390 25
            $arrayFunction = function () {
2391 25
                foreach ($this->getGenerator() as $key => $value) {
2392 24
                    yield $key;
2393
                }
2394 25
            };
2395
        } else {
2396
            $arrayFunction = function () use ($search_value, $strict) {
2397
                foreach ($this->getGenerator() as $key => $value) {
2398
                    if ($strict) {
2399
                        if ($search_value === $value) {
2400
                            yield $key;
2401
                        }
2402
                    } else {
2403
                        /** @noinspection NestedPositiveIfStatementsInspection */
2404
                        if ($search_value == $value) {
2405
                            yield $key;
2406
                        }
2407
                    }
2408
                }
2409
            };
2410
        }
2411
2412 25
        return static::create(
2413 25
            $arrayFunction,
2414 25
            $this->iteratorClass,
2415 25
            false
2416
        );
2417
    }
2418
2419
    /**
2420
     * Sort an array by key in reverse order.
2421
     *
2422
     * @param int $sort_flags [optional] <p>
2423
     *                        You may modify the behavior of the sort using the optional
2424
     *                        parameter sort_flags, for details
2425
     *                        see sort.
2426
     *                        </p>
2427
     *
2428
     * @return static
2429
     *                <p>(Mutable) Return this Arrayy object.</p>
2430
     */
2431 4
    public function krsort(int $sort_flags = 0): self
2432
    {
2433 4
        $this->generatorToArray();
2434
2435 4
        \krsort($this->array, $sort_flags);
2436
2437 4
        return $this;
2438
    }
2439
2440
    /**
2441
     * Get the last value from the current array.
2442
     *
2443
     * @return mixed
2444
     *               <p>Return null if there wasn't a element.</p>
2445
     */
2446 4
    public function last()
2447
    {
2448 4
        return $this->pop();
2449
    }
2450
2451
    /**
2452
     * Get the last value(s) from the current array.
2453
     *
2454
     * @param int|null $number
2455
     *
2456
     * @return static
2457
     *                <p>(Immutable)</p>
2458
     */
2459 13
    public function lastsImmutable(int $number = null): self
2460
    {
2461 13
        if ($this->isEmpty()) {
2462 1
            return static::create(
2463 1
                [],
2464 1
                $this->iteratorClass,
2465 1
                false
2466
            );
2467
        }
2468
2469 12
        if ($number === null) {
2470 8
            $poppedValue = $this->pop();
2471
2472 8
            if ($poppedValue === null) {
2473 1
                $poppedValue = [$poppedValue];
2474
            } else {
2475 7
                $poppedValue = (array) $poppedValue;
2476
            }
2477
2478 8
            $arrayy = static::create(
2479 8
                $poppedValue,
2480 8
                $this->iteratorClass,
2481 8
                false
2482
            );
2483
        } else {
2484 4
            $number = (int) $number;
2485 4
            $arrayy = $this->rest(-$number);
2486
        }
2487
2488 12
        return $arrayy;
2489
    }
2490
2491
    /**
2492
     * Get the last value(s) from the current array.
2493
     *
2494
     * @param int|null $number
2495
     *
2496
     * @return static
2497
     *                <p>(Mutable)</p>
2498
     */
2499 13
    public function lastsMutable(int $number = null): self
2500
    {
2501 13
        if ($this->isEmpty()) {
2502 1
            return $this;
2503
        }
2504
2505 12
        if ($number === null) {
2506 8
            $poppedValue = $this->pop();
2507
2508 8
            if ($poppedValue === null) {
2509 1
                $poppedValue = [$poppedValue];
2510
            } else {
2511 7
                $poppedValue = (array) $poppedValue;
2512
            }
2513
2514 8
            $this->array = static::create(
2515 8
                $poppedValue,
2516 8
                $this->iteratorClass,
2517 8
                false
2518 8
            )->getArray();
2519
        } else {
2520 4
            $number = (int) $number;
2521 4
            $this->array = $this->rest(-$number)->getArray();
2522
        }
2523
2524 12
        $this->generator = null;
2525
2526 12
        return $this;
2527
    }
2528
2529
    /**
2530
     * Count the values from the current array.
2531
     *
2532
     * alias: for "Arrayy->count()"
2533
     *
2534
     * @param int $mode
2535
     *
2536
     * @return int
2537
     *
2538
     * @see Arrayy::count()
2539
     */
2540 20
    public function length(int $mode = \COUNT_NORMAL): int
2541
    {
2542 20
        return $this->count($mode);
2543
    }
2544
2545
    /**
2546
     * Apply the given function to the every element of the array,
2547
     * collecting the results.
2548
     *
2549
     * @param callable $callable
2550
     * @param bool     $useKeyAsSecondParameter
2551
     * @param mixed    ...$arguments
2552
     *
2553
     * @return static
2554
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2555
     */
2556 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2557
    {
2558 5
        $useArguments = \func_num_args() > 2;
2559
2560 5
        return static::create(
2561 5
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2562 5
                foreach ($this->getGenerator() as $key => $value) {
2563 4
                    if ($useArguments) {
2564 3
                        if ($useKeyAsSecondParameter) {
2565
                            yield $key => $callable($value, $key, ...$arguments);
2566
                        } else {
2567 3
                            yield $key => $callable($value, ...$arguments);
2568
                        }
2569
                    } else {
2570
                        /** @noinspection NestedPositiveIfStatementsInspection */
2571 4
                        if ($useKeyAsSecondParameter) {
2572
                            yield $key => $callable($value, $key);
2573
                        } else {
2574 4
                            yield $key => $callable($value);
2575
                        }
2576
                    }
2577
                }
2578 5
            },
2579 5
            $this->iteratorClass,
2580 5
            false
2581
        );
2582
    }
2583
2584
    /**
2585
     * Check if all items in current array match a truth test.
2586
     *
2587
     * @param \Closure $closure
2588
     *
2589
     * @return bool
2590
     */
2591 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...
2592
    {
2593 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2594 2
            return false;
2595
        }
2596
2597 13
        foreach ($this->getGenerator() as $key => $value) {
2598 13
            $value = $closure($value, $key);
2599
2600 13
            if ($value === false) {
2601 13
                return false;
2602
            }
2603
        }
2604
2605 7
        return true;
2606
    }
2607
2608
    /**
2609
     * Check if any item in the current array matches a truth test.
2610
     *
2611
     * @param \Closure $closure
2612
     *
2613
     * @return bool
2614
     */
2615 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...
2616
    {
2617 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2618 2
            return false;
2619
        }
2620
2621 12
        foreach ($this->getGenerator() as $key => $value) {
2622 12
            $value = $closure($value, $key);
2623
2624 12
            if ($value === true) {
2625 12
                return true;
2626
            }
2627
        }
2628
2629 4
        return false;
2630
    }
2631
2632
    /**
2633
     * Get the max value from an array.
2634
     *
2635
     * @return mixed
2636
     */
2637 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...
2638
    {
2639 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2640 1
            return false;
2641
        }
2642
2643 9
        return \max($this->getArray());
2644
    }
2645
2646
    /**
2647
     * Merge the new $array into the current array.
2648
     *
2649
     * - keep key,value from the current array, also if the index is in the new $array
2650
     *
2651
     * @param array $array
2652
     * @param bool  $recursive
2653
     *
2654
     * @return static
2655
     *                <p>(Immutable)</p>
2656
     */
2657 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...
2658
    {
2659 25
        if ($recursive === true) {
2660 4
            $result = \array_replace_recursive($this->getArray(), $array);
2661
        } else {
2662 21
            $result = \array_replace($this->getArray(), $array);
2663
        }
2664
2665 25
        return static::create(
2666 25
            $result,
2667 25
            $this->iteratorClass,
2668 25
            false
2669
        );
2670
    }
2671
2672
    /**
2673
     * Merge the new $array into the current array.
2674
     *
2675
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2676
     * - create new indexes
2677
     *
2678
     * @param array $array
2679
     * @param bool  $recursive
2680
     *
2681
     * @return static
2682
     *                <p>(Immutable)</p>
2683
     */
2684 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...
2685
    {
2686 16
        if ($recursive === true) {
2687 4
            $result = \array_merge_recursive($this->getArray(), $array);
2688
        } else {
2689 12
            $result = \array_merge($this->getArray(), $array);
2690
        }
2691
2692 16
        return static::create(
2693 16
            $result,
2694 16
            $this->iteratorClass,
2695 16
            false
2696
        );
2697
    }
2698
2699
    /**
2700
     * Merge the the current array into the $array.
2701
     *
2702
     * - use key,value from the new $array, also if the index is in the current array
2703
     *
2704
     * @param array $array
2705
     * @param bool  $recursive
2706
     *
2707
     * @return static
2708
     *                <p>(Immutable)</p>
2709
     */
2710 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...
2711
    {
2712 16
        if ($recursive === true) {
2713 4
            $result = \array_replace_recursive($array, $this->getArray());
2714
        } else {
2715 12
            $result = \array_replace($array, $this->getArray());
2716
        }
2717
2718 16
        return static::create(
2719 16
            $result,
2720 16
            $this->iteratorClass,
2721 16
            false
2722
        );
2723
    }
2724
2725
    /**
2726
     * Merge the current array into the new $array.
2727
     *
2728
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2729
     * - create new indexes
2730
     *
2731
     * @param array $array
2732
     * @param bool  $recursive
2733
     *
2734
     * @return static
2735
     *                <p>(Immutable)</p>
2736
     */
2737 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...
2738
    {
2739 17
        if ($recursive === true) {
2740 4
            $result = \array_merge_recursive($array, $this->getArray());
2741
        } else {
2742 13
            $result = \array_merge($array, $this->getArray());
2743
        }
2744
2745 17
        return static::create(
2746 17
            $result,
2747 17
            $this->iteratorClass,
2748 17
            false
2749
        );
2750
    }
2751
2752
    /**
2753
     * @return ArrayyMeta|static
2754
     */
2755 14
    public static function meta()
2756
    {
2757 14
        return (new ArrayyMeta())->getMetaObject(static::class);
2758
    }
2759
2760
    /**
2761
     * Get the min value from an array.
2762
     *
2763
     * @return mixed
2764
     */
2765 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...
2766
    {
2767 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2768 1
            return false;
2769
        }
2770
2771 9
        return \min($this->getArray());
2772
    }
2773
2774
    /**
2775
     * Move an array element to a new index.
2776
     *
2777
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2778
     *
2779
     * @param int|string $from
2780
     * @param int        $to
2781
     *
2782
     * @return static
2783
     *                <p>(Immutable)</p>
2784
     */
2785 1
    public function moveElement($from, $to): self
2786
    {
2787 1
        $array = $this->getArray();
2788
2789 1
        if (\is_int($from)) {
2790 1
            $tmp = \array_splice($array, $from, 1);
2791 1
            \array_splice($array, (int) $to, 0, $tmp);
2792 1
            $output = $array;
2793 1
        } elseif (\is_string($from)) {
2794 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2795 1
            $itemToMove = $array[$from];
2796 1
            if ($indexToMove !== false) {
2797 1
                \array_splice($array, $indexToMove, 1);
2798
            }
2799 1
            $i = 0;
2800 1
            $output = [];
2801 1
            foreach ($array as $key => $item) {
2802 1
                if ($i === $to) {
2803 1
                    $output[$from] = $itemToMove;
2804
                }
2805 1
                $output[$key] = $item;
2806 1
                ++$i;
2807
            }
2808
        } else {
2809
            $output = [];
2810
        }
2811
2812 1
        return static::create(
2813 1
            $output,
2814 1
            $this->iteratorClass,
2815 1
            false
2816
        );
2817
    }
2818
2819
    /**
2820
     * Get a subset of the items from the given array.
2821
     *
2822
     * @param mixed[] $keys
2823
     *
2824
     * @return static
2825
     *                <p>(Immutable)</p>
2826
     */
2827
    public function only(array $keys): self
2828
    {
2829
        $array = $this->getArray();
2830
2831
        return static::create(
2832
            \array_intersect_key($array, \array_flip($keys)),
2833
            $this->iteratorClass,
2834
            false
2835
        );
2836
    }
2837
2838
    /**
2839
     * Pad array to the specified size with a given value.
2840
     *
2841
     * @param int   $size  <p>Size of the result array.</p>
2842
     * @param mixed $value <p>Empty value by default.</p>
2843
     *
2844
     * @return static
2845
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
2846
     */
2847 4
    public function pad(int $size, $value): self
2848
    {
2849 4
        return static::create(
2850 4
            \array_pad($this->getArray(), $size, $value),
2851 4
            $this->iteratorClass,
2852 4
            false
2853
        );
2854
    }
2855
2856
    /**
2857
     * Pop a specified value off the end of the current array.
2858
     *
2859
     * @return mixed
2860
     *               <p>(Mutable) The popped element from the current array.</p>
2861
     */
2862 16
    public function pop()
2863
    {
2864 16
        $this->generatorToArray();
2865
2866 16
        return \array_pop($this->array);
2867
    }
2868
2869
    /**
2870
     * Prepend a (key) + value to the current array.
2871
     *
2872
     * @param mixed $value
2873
     * @param mixed $key
2874
     *
2875
     * @return static
2876
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
2877
     */
2878 8
    public function prepend($value, $key = null): self
2879
    {
2880 8
        $this->generatorToArray();
2881
2882 8
        if ($key === null) {
2883 8
            \array_unshift($this->array, $value);
2884
        } else {
2885
            /** @noinspection AdditionOperationOnArraysInspection */
2886 1
            $this->array = [$key => $value] + $this->array;
2887
        }
2888
2889 8
        return $this;
2890
    }
2891
2892
    /**
2893
     * Add a suffix to each key.
2894
     *
2895
     * @param mixed $suffix
2896
     *
2897
     * @return static
2898
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
2899
     */
2900 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...
2901
    {
2902
        // init
2903 10
        $result = [];
2904
2905 10
        foreach ($this->getGenerator() as $key => $item) {
2906 9
            if ($item instanceof self) {
2907
                $result[$key] = $item->prependToEachKey($suffix);
2908 9
            } elseif (\is_array($item)) {
2909
                $result[$key] = self::create(
2910
                    $item,
2911
                    $this->iteratorClass,
2912
                    false
2913
                )->prependToEachKey($suffix)
2914
                    ->toArray();
2915
            } else {
2916 9
                $result[$key . $suffix] = $item;
2917
            }
2918
        }
2919
2920 10
        return self::create(
2921 10
            $result,
2922 10
            $this->iteratorClass,
2923 10
            false
2924
        );
2925
    }
2926
2927
    /**
2928
     * Add a suffix to each value.
2929
     *
2930
     * @param mixed $suffix
2931
     *
2932
     * @return static
2933
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
2934
     */
2935 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...
2936
    {
2937
        // init
2938 10
        $result = [];
2939
2940 10
        foreach ($this->getGenerator() as $key => $item) {
2941 9
            if ($item instanceof self) {
2942
                $result[$key] = $item->prependToEachValue($suffix);
2943 9
            } elseif (\is_array($item)) {
2944
                $result[$key] = self::create(
2945
                    $item,
2946
                    $this->iteratorClass,
2947
                    false
2948
                )->prependToEachValue($suffix)
2949
                    ->toArray();
2950 9
            } elseif (\is_object($item)) {
2951 1
                $result[$key] = $item;
2952
            } else {
2953 9
                $result[$key] = $item . $suffix;
2954
            }
2955
        }
2956
2957 10
        return self::create(
2958 10
            $result,
2959 10
            $this->iteratorClass,
2960 10
            false
2961
        );
2962
    }
2963
2964
    /**
2965
     * Push one or more values onto the end of array at once.
2966
     *
2967
     * @return static
2968
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
2969
     */
2970 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...
2971
    {
2972 4
        $this->generatorToArray();
2973
2974 4
        if (\func_num_args()) {
2975 4
            $args = \func_get_args();
2976 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...
2977
        }
2978
2979 4
        return $this;
2980
    }
2981
2982
    /**
2983
     * Get a random value from the current array.
2984
     *
2985
     * @param int|null $number <p>How many values you will take?</p>
2986
     *
2987
     * @return static
2988
     *                <p>(Immutable)</p>
2989
     */
2990 18
    public function randomImmutable(int $number = null): self
2991
    {
2992 18
        $this->generatorToArray();
2993
2994 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...
2995 1
            return static::create(
2996 1
                [],
2997 1
                $this->iteratorClass,
2998 1
                false
2999
            );
3000
        }
3001
3002 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...
3003
            /** @noinspection NonSecureArrayRandUsageInspection */
3004 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3005
3006 13
            return static::create(
3007 13
                $arrayRandValue,
3008 13
                $this->iteratorClass,
3009 13
                false
3010
            );
3011
        }
3012
3013 5
        $arrayTmp = $this->array;
3014
        /** @noinspection NonSecureShuffleUsageInspection */
3015 5
        \shuffle($arrayTmp);
3016
3017 5
        return static::create(
3018 5
            $arrayTmp,
3019 5
            $this->iteratorClass,
3020 5
            false
3021 5
        )->firstsImmutable($number);
3022
    }
3023
3024
    /**
3025
     * Pick a random key/index from the keys of this array.
3026
     *
3027
     * @throws \RangeException If array is empty
3028
     *
3029
     * @return mixed
3030
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3031
     */
3032 4
    public function randomKey()
3033
    {
3034 4
        $result = $this->randomKeys(1);
3035
3036 4
        if (!isset($result[0])) {
3037
            $result[0] = null;
3038
        }
3039
3040 4
        return $result[0];
3041
    }
3042
3043
    /**
3044
     * Pick a given number of random keys/indexes out of this array.
3045
     *
3046
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3047
     *
3048
     * @throws \RangeException If array is empty
3049
     *
3050
     * @return static
3051
     *                <p>(Immutable)</p>
3052
     */
3053 13
    public function randomKeys(int $number): self
3054
    {
3055 13
        $this->generatorToArray();
3056
3057 13
        $count = \count($this->array, \COUNT_NORMAL);
3058
3059 13
        if ($number === 0 || $number > $count) {
3060 2
            throw new \RangeException(
3061 2
                \sprintf(
3062 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3063 2
                    $number,
3064 2
                    $count
3065
                )
3066
            );
3067
        }
3068
3069 11
        $result = (array) \array_rand($this->array, $number);
3070
3071 11
        return static::create(
3072 11
            $result,
3073 11
            $this->iteratorClass,
3074 11
            false
3075
        );
3076
    }
3077
3078
    /**
3079
     * Get a random value from the current array.
3080
     *
3081
     * @param int|null $number <p>How many values you will take?</p>
3082
     *
3083
     * @return static
3084
     *                <p>(Mutable)</p>
3085
     */
3086 17
    public function randomMutable(int $number = null): self
3087
    {
3088 17
        $this->generatorToArray();
3089
3090 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...
3091
            return static::create(
3092
                [],
3093
                $this->iteratorClass,
3094
                false
3095
            );
3096
        }
3097
3098 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...
3099
            /** @noinspection NonSecureArrayRandUsageInspection */
3100 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3101 7
            $this->array = $arrayRandValue;
3102
3103 7
            return $this;
3104
        }
3105
3106
        /** @noinspection NonSecureShuffleUsageInspection */
3107 11
        \shuffle($this->array);
3108
3109 11
        return $this->firstsMutable($number);
3110
    }
3111
3112
    /**
3113
     * Pick a random value from the values of this array.
3114
     *
3115
     * @return mixed
3116
     *               <p>Get a random value or null if there wasn't a value.</p>
3117
     */
3118 4
    public function randomValue()
3119
    {
3120 4
        $result = $this->randomImmutable();
3121
3122 4
        if (!isset($result[0])) {
3123
            $result[0] = null;
3124
        }
3125
3126 4
        return $result[0];
3127
    }
3128
3129
    /**
3130
     * Pick a given number of random values out of this array.
3131
     *
3132
     * @param int $number
3133
     *
3134
     * @return static
3135
     *                <p>(Mutable)</p>
3136
     */
3137 7
    public function randomValues(int $number): self
3138
    {
3139 7
        return $this->randomMutable($number);
3140
    }
3141
3142
    /**
3143
     * Get a random value from an array, with the ability to skew the results.
3144
     *
3145
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3146
     *
3147
     * @param array    $array
3148
     * @param int|null $number <p>How many values you will take?</p>
3149
     *
3150
     * @return static
3151
     *                <p>(Immutable)</p>
3152
     */
3153 9
    public function randomWeighted(array $array, int $number = null): self
3154
    {
3155
        // init
3156 9
        $options = [];
3157
3158 9
        foreach ($array as $option => $weight) {
3159 9
            if ($this->searchIndex($option) !== false) {
3160 9
                for ($i = 0; $i < $weight; ++$i) {
3161 1
                    $options[] = $option;
3162
                }
3163
            }
3164
        }
3165
3166 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3167
    }
3168
3169
    /**
3170
     * Reduce the current array via callable e.g. anonymous-function.
3171
     *
3172
     * @param callable $callable
3173
     * @param array    $init
3174
     *
3175
     * @return static
3176
     *                <p>(Immutable)</p>
3177
     */
3178 16
    public function reduce($callable, array $init = []): self
3179
    {
3180 16
        if ($this->generator) {
3181 1
            $result = $init;
3182
3183 1
            foreach ($this->getGenerator() as $value) {
3184 1
                $result = $callable($result, $value);
3185
            }
3186
3187 1
            return static::create(
3188 1
                $result,
3189 1
                $this->iteratorClass,
3190 1
                false
3191
            );
3192
        }
3193
3194 16
        $result = \array_reduce($this->array, $callable, $init);
3195
3196 16
        if ($result === null) {
3197
            $this->array = [];
3198
        } else {
3199 16
            $this->array = (array) $result;
3200
        }
3201
3202 16
        return static::create(
3203 16
            $this->array,
3204 16
            $this->iteratorClass,
3205 16
            false
3206
        );
3207
    }
3208
3209
    /**
3210
     * Create a numerically re-indexed Arrayy object.
3211
     *
3212
     * @return static
3213
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3214
     */
3215 9
    public function reindex(): self
3216
    {
3217 9
        $this->generatorToArray();
3218
3219 9
        $this->array = \array_values($this->array);
3220
3221 9
        return $this;
3222
    }
3223
3224
    /**
3225
     * Return all items that fail the truth test.
3226
     *
3227
     * @param \Closure $closure
3228
     *
3229
     * @return static
3230
     *                <p>(Immutable)</p>
3231
     */
3232 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...
3233
    {
3234
        // init
3235 1
        $filtered = [];
3236
3237 1
        foreach ($this->getGenerator() as $key => $value) {
3238 1
            if (!$closure($value, $key)) {
3239 1
                $filtered[$key] = $value;
3240
            }
3241
        }
3242
3243 1
        return static::create(
3244 1
            $filtered,
3245 1
            $this->iteratorClass,
3246 1
            false
3247
        );
3248
    }
3249
3250
    /**
3251
     * Remove a value from the current array (optional using dot-notation).
3252
     *
3253
     * @param mixed $key
3254
     *
3255
     * @return static
3256
     *                <p>(Mutable)</p>
3257
     */
3258 18
    public function remove($key): self
3259
    {
3260
        // recursive call
3261 18
        if (\is_array($key)) {
3262
            foreach ($key as $k) {
3263
                $this->internalRemove($k);
3264
            }
3265
3266
            return static::create(
3267
                $this->getArray(),
3268
                $this->iteratorClass,
3269
                false
3270
            );
3271
        }
3272
3273 18
        $this->internalRemove($key);
3274
3275 18
        return static::create(
3276 18
            $this->getArray(),
3277 18
            $this->iteratorClass,
3278 18
            false
3279
        );
3280
    }
3281
3282
    /**
3283
     * Remove the first value from the current array.
3284
     *
3285
     * @return static
3286
     *                <p>(Immutable)</p>
3287
     */
3288 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...
3289
    {
3290 7
        $tmpArray = $this->getArray();
3291
3292 7
        \array_shift($tmpArray);
3293
3294 7
        return static::create(
3295 7
            $tmpArray,
3296 7
            $this->iteratorClass,
3297 7
            false
3298
        );
3299
    }
3300
3301
    /**
3302
     * Remove the last value from the current array.
3303
     *
3304
     * @return static
3305
     *                <p>(Immutable)</p>
3306
     */
3307 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...
3308
    {
3309 7
        $tmpArray = $this->getArray();
3310
3311 7
        \array_pop($tmpArray);
3312
3313 7
        return static::create(
3314 7
            $tmpArray,
3315 7
            $this->iteratorClass,
3316 7
            false
3317
        );
3318
    }
3319
3320
    /**
3321
     * Removes a particular value from an array (numeric or associative).
3322
     *
3323
     * @param mixed $value
3324
     *
3325
     * @return static
3326
     *                <p>(Immutable)</p>
3327
     */
3328 7
    public function removeValue($value): self
3329
    {
3330 7
        $this->generatorToArray();
3331
3332
        // init
3333 7
        $isNumericArray = true;
3334
3335 7
        foreach ($this->getGenerator() as $key => $item) {
3336 6
            if ($item === $value) {
3337 6
                if (!\is_int($key)) {
3338
                    $isNumericArray = false;
3339
                }
3340 6
                unset($this->array[$key]);
3341
            }
3342
        }
3343
3344 7
        if ($isNumericArray) {
3345 7
            $this->array = \array_values($this->array);
3346
        }
3347
3348 7
        return static::create(
3349 7
            $this->array,
3350 7
            $this->iteratorClass,
3351 7
            false
3352
        );
3353
    }
3354
3355
    /**
3356
     * Generate array of repeated arrays.
3357
     *
3358
     * @param int $times <p>How many times has to be repeated.</p>
3359
     *
3360
     * @return static
3361
     *                <p>(Immutable)</p>
3362
     */
3363 1
    public function repeat($times): self
3364
    {
3365 1
        if ($times === 0) {
3366 1
            return new static();
3367
        }
3368
3369 1
        return static::create(
3370 1
            \array_fill(0, (int) $times, $this->getArray()),
3371 1
            $this->iteratorClass,
3372 1
            false
3373
        );
3374
    }
3375
3376
    /**
3377
     * Replace a key with a new key/value pair.
3378
     *
3379
     * @param mixed $replace
3380
     * @param mixed $key
3381
     * @param mixed $value
3382
     *
3383
     * @return static
3384
     *                <p>(Immutable)</p>
3385
     */
3386 2
    public function replace($replace, $key, $value): self
3387
    {
3388 2
        $that = clone $this;
3389
3390 2
        return $that->remove($replace)
3391 2
            ->set($key, $value);
3392
    }
3393
3394
    /**
3395
     * Create an array using the current array as values and the other array as keys.
3396
     *
3397
     * @param array $keys <p>An array of keys.</p>
3398
     *
3399
     * @return static
3400
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3401
     */
3402 2
    public function replaceAllKeys(array $keys): self
3403
    {
3404 2
        return static::create(
3405 2
            \array_combine($keys, $this->getArray()),
3406 2
            $this->iteratorClass,
3407 2
            false
3408
        );
3409
    }
3410
3411
    /**
3412
     * Create an array using the current array as keys and the other array as values.
3413
     *
3414
     * @param array $array <p>An array o values.</p>
3415
     *
3416
     * @return static
3417
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3418
     */
3419 2
    public function replaceAllValues(array $array): self
3420
    {
3421 2
        return static::create(
3422 2
            \array_combine($this->array, $array),
3423 2
            $this->iteratorClass,
3424 2
            false
3425
        );
3426
    }
3427
3428
    /**
3429
     * Replace the keys in an array with another set.
3430
     *
3431
     * @param array $keys <p>An array of keys matching the array's size</p>
3432
     *
3433
     * @return static
3434
     *                <p>(Immutable)</p>
3435
     */
3436 1
    public function replaceKeys(array $keys): self
3437
    {
3438 1
        $values = \array_values($this->getArray());
3439 1
        $result = \array_combine($keys, $values);
3440
3441 1
        return static::create(
3442 1
            $result,
3443 1
            $this->iteratorClass,
3444 1
            false
3445
        );
3446
    }
3447
3448
    /**
3449
     * Replace the first matched value in an array.
3450
     *
3451
     * @param mixed $search      <p>The value to replace.</p>
3452
     * @param mixed $replacement <p>The value to replace.</p>
3453
     *
3454
     * @return static
3455
     *                <p>(Immutable)</p>
3456
     */
3457 3
    public function replaceOneValue($search, $replacement = ''): self
3458
    {
3459 3
        $array = $this->getArray();
3460 3
        $key = \array_search($search, $array, true);
3461
3462 3
        if ($key !== false) {
3463 3
            $array[$key] = $replacement;
3464
        }
3465
3466 3
        return static::create(
3467 3
            $array,
3468 3
            $this->iteratorClass,
3469 3
            false
3470
        );
3471
    }
3472
3473
    /**
3474
     * Replace values in the current array.
3475
     *
3476
     * @param mixed $search      <p>The value to replace.</p>
3477
     * @param mixed $replacement <p>What to replace it with.</p>
3478
     *
3479
     * @return static
3480
     *                <p>(Immutable)</p>
3481
     */
3482 1
    public function replaceValues($search, $replacement = ''): self
3483
    {
3484 1
        $array = $this->each(
3485 1
            static function ($value) use ($search, $replacement) {
3486 1
                return \str_replace($search, $replacement, $value);
3487 1
            }
3488
        );
3489
3490 1
        return $array;
3491
    }
3492
3493
    /**
3494
     * Get the last elements from index $from until the end of this array.
3495
     *
3496
     * @param int $from
3497
     *
3498
     * @return static
3499
     *                <p>(Immutable)</p>
3500
     */
3501 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...
3502
    {
3503 15
        $tmpArray = $this->getArray();
3504
3505 15
        return static::create(
3506 15
            \array_splice($tmpArray, $from),
3507 15
            $this->iteratorClass,
3508 15
            false
3509
        );
3510
    }
3511
3512
    /**
3513
     * Return the array in the reverse order.
3514
     *
3515
     * @return static
3516
     *                <p>(Mutable) Return this Arrayy object.</p>
3517
     */
3518 8
    public function reverse(): self
3519
    {
3520 8
        $this->generatorToArray();
3521
3522 8
        $this->array = \array_reverse($this->array);
3523
3524 8
        return $this;
3525
    }
3526
3527
    /**
3528
     * Sort an array in reverse order.
3529
     *
3530
     * @param int $sort_flags [optional] <p>
3531
     *                        You may modify the behavior of the sort using the optional
3532
     *                        parameter sort_flags, for details
3533
     *                        see sort.
3534
     *                        </p>
3535
     *
3536
     * @return static
3537
     *                <p>(Mutable) Return this Arrayy object.</p>
3538
     */
3539 4
    public function rsort(int $sort_flags = 0): self
3540
    {
3541 4
        $this->generatorToArray();
3542
3543 4
        \rsort($this->array, $sort_flags);
3544
3545 4
        return $this;
3546
    }
3547
3548
    /**
3549
     * Search for the first index of the current array via $value.
3550
     *
3551
     * @param mixed $value
3552
     *
3553
     * @return false|float|int|string
3554
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3555
     */
3556 20
    public function searchIndex($value)
3557
    {
3558 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3559 19
            if ($value === $valueFromArray) {
3560 19
                return $keyFromArray;
3561
            }
3562
        }
3563
3564 11
        return false;
3565
    }
3566
3567
    /**
3568
     * Search for the value of the current array via $index.
3569
     *
3570
     * @param mixed $index
3571
     *
3572
     * @return static
3573
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3574
     */
3575 9
    public function searchValue($index): self
3576
    {
3577 9
        $this->generatorToArray();
3578
3579
        // init
3580 9
        $return = [];
3581
3582 9
        if ($this->array === []) {
3583
            return static::create(
3584
                [],
3585
                $this->iteratorClass,
3586
                false
3587
            );
3588
        }
3589
3590
        // php cast "bool"-index into "int"-index
3591 9
        if ((bool) $index === $index) {
3592 1
            $index = (int) $index;
3593
        }
3594
3595 9
        if (\array_key_exists($index, $this->array) === true) {
3596 7
            $return = [$this->array[$index]];
3597
        }
3598
3599 9
        return static::create(
3600 9
            $return,
3601 9
            $this->iteratorClass,
3602 9
            false
3603
        );
3604
    }
3605
3606
    /**
3607
     * Set a value for the current array (optional using dot-notation).
3608
     *
3609
     * @param string $key   <p>The key to set.</p>
3610
     * @param mixed  $value <p>Its value.</p>
3611
     *
3612
     * @return static
3613
     *                <p>(Mutable)</p>
3614
     */
3615 18
    public function set($key, $value): self
3616
    {
3617 18
        $this->generatorToArray();
3618
3619 18
        $this->internalSet($key, $value);
3620
3621 18
        return $this;
3622
    }
3623
3624
    /**
3625
     * Get a value from a array and set it if it was not.
3626
     *
3627
     * WARNING: this method only set the value, if the $key is not already set
3628
     *
3629
     * @param mixed $key      <p>The key</p>
3630
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3631
     *
3632
     * @return mixed
3633
     *               <p>(Mutable)</p>
3634
     */
3635 11
    public function setAndGet($key, $fallback = null)
3636
    {
3637 11
        $this->generatorToArray();
3638
3639
        // If the key doesn't exist, set it.
3640 11
        if (!$this->has($key)) {
3641 4
            $this->array = $this->set($key, $fallback)->getArray();
3642
        }
3643
3644 11
        return $this->get($key);
3645
    }
3646
3647
    /**
3648
     * Shifts a specified value off the beginning of array.
3649
     *
3650
     * @return mixed
3651
     *               <p>(Mutable) A shifted element from the current array.</p>
3652
     */
3653 4
    public function shift()
3654
    {
3655 4
        $this->generatorToArray();
3656
3657 4
        return \array_shift($this->array);
3658
    }
3659
3660
    /**
3661
     * Shuffle the current array.
3662
     *
3663
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3664
     * @param array $array  [optional]
3665
     *
3666
     * @return static
3667
     *                <p>(Immutable)</p>
3668
     */
3669 1
    public function shuffle(bool $secure = false, array $array = null): self
3670
    {
3671 1
        if ($array === null) {
3672 1
            $array = $this->getArray();
3673
        }
3674
3675 1
        if ($secure !== true) {
3676
            /** @noinspection NonSecureShuffleUsageInspection */
3677 1
            \shuffle($array);
3678
        } else {
3679 1
            $size = \count($array, \COUNT_NORMAL);
3680 1
            $keys = \array_keys($array);
3681 1
            for ($i = $size - 1; $i > 0; --$i) {
3682
                try {
3683 1
                    $r = \random_int(0, $i);
3684
                } catch (\Exception $e) {
3685
                    /** @noinspection RandomApiMigrationInspection */
3686
                    $r = \mt_rand(0, $i);
3687
                }
3688 1
                if ($r !== $i) {
3689 1
                    $temp = $array[$keys[$r]];
3690 1
                    $array[$keys[$r]] = $array[$keys[$i]];
3691 1
                    $array[$keys[$i]] = $temp;
3692
                }
3693
            }
3694
3695
            // reset indices
3696 1
            $array = \array_values($array);
3697
        }
3698
3699 1
        foreach ($array as $key => $value) {
3700
            // check if recursive is needed
3701 1
            if (\is_array($value) === true) {
3702 1
                $array[$key] = $this->shuffle($secure, $value);
3703
            }
3704
        }
3705
3706 1
        return static::create(
3707 1
            $array,
3708 1
            $this->iteratorClass,
3709 1
            false
3710
        );
3711
    }
3712
3713
    /**
3714
     * Checks whether array has exactly $size items.
3715
     *
3716
     * @param int $size
3717
     *
3718
     * @return bool
3719
     */
3720 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...
3721
    {
3722
        // init
3723 1
        $itemsTempCount = 0;
3724
3725 1
        foreach ($this->getGenerator() as $key => $value) {
3726 1
            ++$itemsTempCount;
3727 1
            if ($itemsTempCount > $size) {
3728 1
                return false;
3729
            }
3730
        }
3731
3732 1
        return $itemsTempCount === $size;
3733
    }
3734
3735
    /**
3736
     * Checks whether array has less than $size items.
3737
     *
3738
     * @param int $size
3739
     *
3740
     * @return bool
3741
     */
3742 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...
3743
    {
3744
        // init
3745 1
        $itemsTempCount = 0;
3746
3747 1
        foreach ($this->getGenerator() as $key => $value) {
3748 1
            ++$itemsTempCount;
3749 1
            if ($itemsTempCount > $size) {
3750 1
                return false;
3751
            }
3752
        }
3753
3754 1
        return $itemsTempCount < $size;
3755
    }
3756
3757
    /**
3758
     * Checks whether array has more than $size items.
3759
     *
3760
     * @param int $size
3761
     *
3762
     * @return bool
3763
     */
3764 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...
3765
    {
3766
        // init
3767 1
        $itemsTempCount = 0;
3768
3769 1
        foreach ($this->getGenerator() as $key => $value) {
3770 1
            ++$itemsTempCount;
3771 1
            if ($itemsTempCount > $size) {
3772 1
                return true;
3773
            }
3774
        }
3775
3776 1
        return $itemsTempCount > $size;
3777
    }
3778
3779
    /**
3780
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
3781
     * smaller than $fromSize.
3782
     *
3783
     * @param int $fromSize
3784
     * @param int $toSize
3785
     *
3786
     * @return bool
3787
     */
3788 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
3789
    {
3790 1
        if ($fromSize > $toSize) {
3791 1
            $tmp = $toSize;
3792 1
            $toSize = $fromSize;
3793 1
            $fromSize = $tmp;
3794
        }
3795
3796
        // init
3797 1
        $itemsTempCount = 0;
3798
3799 1
        foreach ($this->getGenerator() as $key => $value) {
3800 1
            ++$itemsTempCount;
3801 1
            if ($itemsTempCount > $toSize) {
3802 1
                return false;
3803
            }
3804
        }
3805
3806 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
3807
    }
3808
3809
    /**
3810
     * Count the values from the current array.
3811
     *
3812
     * alias: for "Arrayy->count()"
3813
     *
3814
     * @param int $mode
3815
     *
3816
     * @return int
3817
     */
3818 20
    public function size(int $mode = \COUNT_NORMAL): int
3819
    {
3820 20
        return $this->count($mode);
3821
    }
3822
3823
    /**
3824
     * Counts all elements in an array, or something in an object.
3825
     *
3826
     * <p>
3827
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
3828
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
3829
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
3830
     * implemented and used in PHP.
3831
     * </p>
3832
     *
3833
     * @return int
3834
     *             <p>
3835
     *             The number of elements in var, which is
3836
     *             typically an array, since anything else will have one
3837
     *             element.
3838
     *             </p>
3839
     *             <p>
3840
     *             If var is not an array or an object with
3841
     *             implemented Countable interface,
3842
     *             1 will be returned.
3843
     *             There is one exception, if var is &null;,
3844
     *             0 will be returned.
3845
     *             </p>
3846
     *             <p>
3847
     *             Caution: count may return 0 for a variable that isn't set,
3848
     *             but it may also return 0 for a variable that has been initialized with an
3849
     *             empty array. Use isset to test if a variable is set.
3850
     *             </p>
3851
     */
3852 10
    public function sizeRecursive(): int
3853
    {
3854 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
3855
    }
3856
3857
    /**
3858
     * Extract a slice of the array.
3859
     *
3860
     * @param int      $offset       <p>Slice begin index.</p>
3861
     * @param int|null $length       <p>Length of the slice.</p>
3862
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
3863
     *
3864
     * @return static
3865
     *                <p>A slice of the original array with length $length.</p>
3866
     */
3867 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
3868
    {
3869 4
        return static::create(
3870 4
            \array_slice(
3871 4
                $this->getArray(),
3872 4
                $offset,
3873 4
                $length,
3874 4
                $preserveKeys
3875
            ),
3876 4
            $this->iteratorClass,
3877 4
            false
3878
        );
3879
    }
3880
3881
    /**
3882
     * Sort the current array and optional you can keep the keys.
3883
     *
3884
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3885
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
3886
     *                              <strong>SORT_NATURAL</strong></p>
3887
     * @param bool       $keepKeys
3888
     *
3889
     * @return static
3890
     *                <p>(Mutable) Return this Arrayy object.</p>
3891
     */
3892 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
3893
    {
3894 20
        $this->generatorToArray();
3895
3896 20
        return $this->sorting(
3897 20
            $this->array,
3898 20
            $direction,
3899 20
            $strategy,
3900 20
            $keepKeys
3901
        );
3902
    }
3903
3904
    /**
3905
     * Sort the current array by key.
3906
     *
3907
     * @see http://php.net/manual/en/function.ksort.php
3908
     * @see http://php.net/manual/en/function.krsort.php
3909
     *
3910
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3911
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3912
     *                              <strong>SORT_NATURAL</strong></p>
3913
     *
3914
     * @return static
3915
     *                <p>(Mutable) Return this Arrayy object.</p>
3916
     */
3917 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3918
    {
3919 18
        $this->generatorToArray();
3920
3921 18
        $this->sorterKeys($this->array, $direction, $strategy);
3922
3923 18
        return $this;
3924
    }
3925
3926
    /**
3927
     * Sort the current array by value.
3928
     *
3929
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3930
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3931
     *                              <strong>SORT_NATURAL</strong></p>
3932
     *
3933
     * @return static
3934
     *                <p>(Mutable)</p>
3935
     */
3936 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3937
    {
3938 1
        return $this->sort($direction, $strategy, true);
3939
    }
3940
3941
    /**
3942
     * Sort the current array by value.
3943
     *
3944
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3945
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3946
     *                              <strong>SORT_NATURAL</strong></p>
3947
     *
3948
     * @return static
3949
     *                <p>(Mutable)</p>
3950
     */
3951 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3952
    {
3953 1
        return $this->sort($direction, $strategy, false);
3954
    }
3955
3956
    /**
3957
     * Sort a array by value, by a closure or by a property.
3958
     *
3959
     * - If the sorter is null, the array is sorted naturally.
3960
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
3961
     *
3962
     * @param callable|string|null $sorter
3963
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3964
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3965
     *                                        <strong>SORT_NATURAL</strong></p>
3966
     *
3967
     * @return static
3968
     *                <p>(Immutable)</p>
3969
     */
3970 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3971
    {
3972 1
        $array = $this->getArray();
3973 1
        $direction = $this->getDirection($direction);
3974
3975
        // Transform all values into their results.
3976 1
        if ($sorter) {
3977 1
            $arrayy = static::create(
3978 1
                $array,
3979 1
                $this->iteratorClass,
3980 1
                false
3981
            );
3982
3983 1
            $results = $arrayy->each(
3984 1
                function ($value) use ($sorter) {
3985 1
                    if (\is_callable($sorter)) {
3986 1
                        return $sorter($value);
3987
                    }
3988
3989 1
                    return $this->get($sorter, null, $this->getArray());
3990 1
                }
3991
            );
3992
3993 1
            $results = $results->getArray();
3994
        } else {
3995 1
            $results = $array;
3996
        }
3997
3998
        // Sort by the results and replace by original values
3999 1
        \array_multisort($results, $direction, $strategy, $array);
4000
4001 1
        return static::create(
4002 1
            $array,
4003 1
            $this->iteratorClass,
4004 1
            false
4005
        );
4006
    }
4007
4008
    /**
4009
     * Split an array in the given amount of pieces.
4010
     *
4011
     * @param int  $numberOfPieces
4012
     * @param bool $keepKeys
4013
     *
4014
     * @return static
4015
     *                <p>(Immutable)</p>
4016
     */
4017 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4018
    {
4019 1
        $this->generatorToArray();
4020
4021 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4022
4023 1
        if ($arrayCount === 0) {
4024 1
            $result = [];
4025
        } else {
4026 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4027 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4028
        }
4029
4030 1
        return static::create(
4031 1
            $result,
4032 1
            $this->iteratorClass,
4033 1
            false
4034
        );
4035
    }
4036
4037
    /**
4038
     * Stripe all empty items.
4039
     *
4040
     * @return static
4041
     *                <p>(Immutable)</p>
4042
     */
4043 1
    public function stripEmpty(): self
4044
    {
4045 1
        return $this->filter(
4046 1
            static function ($item) {
4047 1
                if ($item === null) {
4048 1
                    return false;
4049
                }
4050
4051 1
                return (bool) \trim((string) $item);
4052 1
            }
4053
        );
4054
    }
4055
4056
    /**
4057
     * Swap two values between positions by key.
4058
     *
4059
     * @param int|string $swapA <p>a key in the array</p>
4060
     * @param int|string $swapB <p>a key in the array</p>
4061
     *
4062
     * @return static
4063
     *                <p>(Immutable)</p>
4064
     */
4065 1
    public function swap($swapA, $swapB): self
4066
    {
4067 1
        $array = $this->getArray();
4068
4069 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4070
4071 1
        return static::create(
4072 1
            $array,
4073 1
            $this->iteratorClass,
4074 1
            false
4075
        );
4076
    }
4077
4078
    /**
4079
     * alias: for "Arrayy->getArray()"
4080
     *
4081
     * @see Arrayy::getArray()
4082
     */
4083 198
    public function toArray()
4084
    {
4085 198
        return $this->getArray();
4086
    }
4087
4088
    /**
4089
     * Convert the current array to JSON.
4090
     *
4091
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4092
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4093
     *
4094
     * @return string
4095
     */
4096 6
    public function toJson(int $options = 0, int $depth = 512): string
4097
    {
4098
        /** @noinspection PhpComposerExtensionStubsInspection */
4099 6
        $return = \json_encode($this->getArray(), $options, $depth);
4100 6
        if ($return === false) {
4101
            return '';
4102
        }
4103
4104 6
        return $return;
4105
    }
4106
4107
    /**
4108
     * Implodes array to a string with specified separator.
4109
     *
4110
     * @param string $separator [optional] <p>The element's separator.</p>
4111
     *
4112
     * @return string
4113
     *                <p>The string representation of array, separated by ",".</p>
4114
     */
4115 19
    public function toString(string $separator = ','): string
4116
    {
4117 19
        return $this->implode($separator);
4118
    }
4119
4120
    /**
4121
     * Return a duplicate free copy of the current array.
4122
     *
4123
     * @return static
4124
     *                <p>(Mutable)</p>
4125
     */
4126 12
    public function unique(): self
4127
    {
4128
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4129
4130 12
        $this->array = $this->reduce(
4131 12
            static function ($resultArray, $value) {
4132 11
                if (!\in_array($value, $resultArray, true)) {
4133 11
                    $resultArray[] = $value;
4134
                }
4135
4136 11
                return $resultArray;
4137 12
            },
4138 12
            []
4139 12
        )->getArray();
4140 12
        $this->generator = null;
4141
4142 12
        return $this;
4143
    }
4144
4145
    /**
4146
     * Return a duplicate free copy of the current array. (with the old keys)
4147
     *
4148
     * @return static
4149
     *                <p>(Mutable)</p>
4150
     */
4151 11
    public function uniqueKeepIndex(): self
4152
    {
4153
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4154
4155
        // init
4156 11
        $array = $this->getArray();
4157
4158 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...
4159 11
            \array_keys($array),
4160 11
            static function ($resultArray, $key) use ($array) {
4161 10
                if (!\in_array($array[$key], $resultArray, true)) {
4162 10
                    $resultArray[$key] = $array[$key];
4163
                }
4164
4165 10
                return $resultArray;
4166 11
            },
4167 11
            []
4168
        );
4169 11
        $this->generator = null;
4170
4171 11
        if ($this->array === null) {
4172
            $this->array = [];
4173
        } else {
4174 11
            $this->array = (array) $this->array;
4175
        }
4176
4177 11
        return $this;
4178
    }
4179
4180
    /**
4181
     * @param bool $unique
4182
     *
4183
     * @return static
4184
     *                <p>(Immutable)</p>
4185
     */
4186 14
    public function reduce_dimension(bool $unique = true): self
4187
    {
4188
        // init
4189 14
        $result = [[]];
4190
4191 14
        foreach ($this->getGenerator() as $val) {
4192 12
            if (\is_array($val)) {
4193 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
4194
            } else {
4195 12
                $result[] = [$val];
4196
            }
4197
        }
4198 14
        $result = \array_merge(...$result);
4199
4200 14
        $resultArrayy = new self($result);
4201
4202 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4203
    }
4204
4205
    /**
4206
     * alias: for "Arrayy->unique()"
4207
     *
4208
     * @return static
4209
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4210
     *
4211
     * @see Arrayy::unique()
4212
     */
4213 10
    public function uniqueNewIndex(): self
4214
    {
4215 10
        return $this->unique();
4216
    }
4217
4218
    /**
4219
     * Prepends one or more values to the beginning of array at once.
4220
     *
4221
     * @return static
4222
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4223
     */
4224 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...
4225
    {
4226 4
        $this->generatorToArray();
4227
4228 4
        if (\func_num_args()) {
4229 4
            $args = \func_get_args();
4230 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...
4231
        }
4232
4233 4
        return $this;
4234
    }
4235
4236
    /**
4237
     * Get all values from a array.
4238
     *
4239
     * @return static
4240
     *                <p>(Immutable)</p>
4241
     */
4242 2
    public function values(): self
4243
    {
4244 2
        return static::create(
4245 2
            function () {
4246
                /** @noinspection YieldFromCanBeUsedInspection */
4247 2
                foreach ($this->getGenerator() as $value) {
4248 2
                    yield $value;
4249
                }
4250 2
            },
4251 2
            $this->iteratorClass,
4252 2
            false
4253
        );
4254
    }
4255
4256
    /**
4257
     * Apply the given function to every element in the array, discarding the results.
4258
     *
4259
     * @param callable $callable
4260
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4261
     *
4262
     * @return static
4263
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4264
     */
4265 37
    public function walk($callable, bool $recursive = false): self
4266
    {
4267 37
        $this->generatorToArray();
4268
4269 37
        if ($recursive === true) {
4270 32
            \array_walk_recursive($this->array, $callable);
4271
        } else {
4272 18
            \array_walk($this->array, $callable);
4273
        }
4274
4275 37
        return $this;
4276
    }
4277
4278
    /**
4279
     * Convert an array into a object.
4280
     *
4281
     * @param array $array PHP array
4282
     *
4283
     * @return \stdClass
4284
     */
4285 4
    protected static function arrayToObject(array $array = []): \stdClass
4286
    {
4287
        // init
4288 4
        $object = new \stdClass();
4289
4290 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4291 1
            return $object;
4292
        }
4293
4294 3
        foreach ($array as $name => $value) {
4295 3
            if (\is_array($value)) {
4296 1
                $object->{$name} = self::arrayToObject($value);
4297
            } else {
4298 3
                $object->{$name} = $value;
4299
            }
4300
        }
4301
4302 3
        return $object;
4303
    }
4304
4305
    /**
4306
     * @param array|\Generator|null $input        <p>
4307
     *                                            An array containing keys to return.
4308
     *                                            </p>
4309
     * @param mixed                 $search_value [optional] <p>
4310
     *                                            If specified, then only keys containing these values are returned.
4311
     *                                            </p>
4312
     * @param bool                  $strict       [optional] <p>
4313
     *                                            Determines if strict comparison (===) should be used during the
4314
     *                                            search.
4315
     *                                            </p>
4316
     *
4317
     * @return array
4318
     *               <p>an array of all the keys in input</p>
4319
     */
4320 10
    protected function array_keys_recursive(
4321
        $input = null,
4322
        $search_value = null,
4323
        bool $strict = true
4324
    ): array {
4325
        // init
4326 10
        $keys = [];
4327 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
4328
4329 10
        if ($input === null) {
4330
            $input = $this->getGenerator();
4331
        }
4332
4333 10
        foreach ($input as $key => $value) {
4334
            if (
4335 10
                $search_value === null
4336
                ||
4337
                (
4338
                    \is_array($search_value) === true
4339
                    &&
4340 10
                    \in_array($key, $search_value, $strict)
4341
                )
4342
            ) {
4343 10
                $keys[] = $key;
4344
            }
4345
4346
            // check if recursive is needed
4347 10
            if (\is_array($value) === true) {
4348 10
                $keysTmp[] = $this->array_keys_recursive($value);
4349
            }
4350
        }
4351
4352 10
        return \array_merge($keys, ...$keysTmp);
4353
    }
4354
4355
    /**
4356
     * @param mixed      $path
4357
     * @param callable   $callable
4358
     * @param array|null $currentOffset
4359
     */
4360 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4361
    {
4362 4
        $this->generatorToArray();
4363
4364 4
        if ($currentOffset === null) {
4365 4
            $currentOffset = &$this->array;
4366
        }
4367
4368 4
        $explodedPath = \explode($this->pathSeparator, $path);
4369 4
        if ($explodedPath === false) {
4370
            return;
4371
        }
4372
4373 4
        $nextPath = \array_shift($explodedPath);
4374
4375 4
        if (!isset($currentOffset[$nextPath])) {
4376
            return;
4377
        }
4378
4379 4
        if (!empty($explodedPath)) {
4380 1
            $this->callAtPath(
4381 1
                \implode($this->pathSeparator, $explodedPath),
4382 1
                $callable,
4383 1
                $currentOffset[$nextPath]
4384
            );
4385
        } else {
4386 4
            $callable($currentOffset[$nextPath]);
4387
        }
4388 4
    }
4389
4390
    /**
4391
     * create a fallback for array
4392
     *
4393
     * 1. use the current array, if it's a array
4394
     * 2. fallback to empty array, if there is nothing
4395
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4396
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4397
     * 5. call "__toArray()" on object, if the method exists
4398
     * 6. cast a string or object with "__toString()" into an array
4399
     * 7. throw a "InvalidArgumentException"-Exception
4400
     *
4401
     * @param mixed $data
4402
     *
4403
     * @throws \InvalidArgumentException
4404
     *
4405
     * @return array
4406
     */
4407 940
    protected function fallbackForArray(&$data): array
4408
    {
4409 940
        if (\is_array($data)) {
4410 937
            return $data;
4411
        }
4412
4413 46
        if (!$data) {
4414 6
            return [];
4415
        }
4416
4417 45
        $isObject = \is_object($data);
4418
4419 45
        if ($isObject && $data instanceof self) {
4420 2
            return $data->getArray();
4421
        }
4422
4423 44
        if ($isObject && $data instanceof \ArrayObject) {
4424
            return $data->getArrayCopy();
4425
        }
4426
4427 44
        if ($isObject && $data instanceof \Generator) {
4428
            return static::createFromGeneratorImmutable($data)->getArray();
4429
        }
4430
4431 44
        if ($isObject && $data instanceof \Traversable) {
4432
            return static::createFromObject($data)->getArray();
4433
        }
4434
4435 44
        if (\is_callable($data)) {
4436 37
            $this->generator = new ArrayyRewindableGenerator($data);
4437
4438 37
            return [];
4439
        }
4440
4441 9
        if ($isObject && \method_exists($data, '__toArray')) {
4442
            return (array) $data->__toArray();
4443
        }
4444
4445
        if (
4446 9
            \is_string($data)
4447
            ||
4448 9
            ($isObject && \method_exists($data, '__toString'))
4449
        ) {
4450 7
            return [(string) $data];
4451
        }
4452
4453 2
        throw new \InvalidArgumentException(
4454 2
            'Passed value should be a array'
4455
        );
4456
    }
4457
4458
    /**
4459
     * Get correct PHP constant for direction.
4460
     *
4461
     * @param int|string $direction
4462
     *
4463
     * @return int
4464
     */
4465 39
    protected function getDirection($direction): int
4466
    {
4467 39
        if (\is_string($direction)) {
4468 10
            $direction = \strtolower($direction);
4469
4470 10
            if ($direction === 'desc') {
4471 2
                $direction = \SORT_DESC;
4472
            } else {
4473 8
                $direction = \SORT_ASC;
4474
            }
4475
        }
4476
4477
        if (
4478 39
            $direction !== \SORT_DESC
4479
            &&
4480 39
            $direction !== \SORT_ASC
4481
        ) {
4482
            $direction = \SORT_ASC;
4483
        }
4484
4485 39
        return $direction;
4486
    }
4487
4488
    /**
4489
     * @param mixed               $glue
4490
     * @param array|static|string $pieces
4491
     * @param bool                $useKeys
4492
     *
4493
     * @return string
4494
     */
4495 35
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4496
    {
4497 35
        if ($pieces instanceof self) {
4498 1
            $pieces = $pieces->getArray();
4499
        }
4500
4501 35
        if (\is_array($pieces)) {
4502 35
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4503 35
            $pieces_count_not_zero = $pieces_count > 0;
4504
4505 35
            return \implode(
4506 35
                $glue,
4507 35
                \array_map(
4508 35
                    [$this, 'implode_recursive'],
4509 35
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4510 35
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4511
                )
4512
            );
4513
        }
4514
4515 35
        return (string) $pieces;
4516
    }
4517
4518
    /**
4519
     * @param mixed                 $needle   <p>
4520
     *                                        The searched value.
4521
     *                                        </p>
4522
     *                                        <p>
4523
     *                                        If needle is a string, the comparison is done
4524
     *                                        in a case-sensitive manner.
4525
     *                                        </p>
4526
     * @param array|\Generator|null $haystack <p>
4527
     *                                        The array.
4528
     *                                        </p>
4529
     * @param bool                  $strict   [optional] <p>
4530
     *                                        If the third parameter strict is set to true
4531
     *                                        then the in_array function will also check the
4532
     *                                        types of the
4533
     *                                        needle in the haystack.
4534
     *                                        </p>
4535
     *
4536
     * @return bool
4537
     *              <p>true if needle is found in the array, false otherwise</p>
4538
     */
4539 44
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4540
    {
4541 44
        if ($haystack === null) {
4542
            $haystack = $this->getGenerator();
4543
        }
4544
4545 44
        foreach ($haystack as $item) {
4546 36
            if (\is_array($item) === true) {
4547 8
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4548
            } else {
4549 36
                $returnTmp = ($strict === true ? $item === $needle : $item == $needle);
4550
            }
4551
4552 36
            if ($returnTmp === true) {
4553 36
                return true;
4554
            }
4555
        }
4556
4557 18
        return false;
4558
    }
4559
4560
    /**
4561
     * @param mixed $value
4562
     */
4563
    protected function internalGetArray(&$value)
4564
    {
4565
        if ($value instanceof self) {
4566
            $valueTmp = $value->getArray();
4567
            if (\count($valueTmp, \COUNT_NORMAL) === 0) {
4568
                $value = [];
4569
            } else {
4570
                /** @noinspection PhpUnusedLocalVariableInspection */
4571
                $value = &$valueTmp;
4572
            }
4573
        }
4574
4575
        /** @noinspection PhpComposerExtensionStubsInspection */
4576
        /** @noinspection NotOptimalIfConditionsInspection */
4577
        if (
4578
            \class_exists('JsonSerializable')
4579
            &&
4580
            $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...
4581
        ) {
4582
4583
            /** @noinspection PhpUnusedLocalVariableInspection */
4584
            $value = &$value->jsonSerialize();
4585
        }
4586
    }
4587
4588
    /**
4589
     * Internal mechanics of remove method.
4590
     *
4591
     * @param mixed $key
4592
     *
4593
     * @return bool
4594
     */
4595 18
    protected function internalRemove($key): bool
4596
    {
4597 18
        $this->generatorToArray();
4598
4599
        if (
4600 18
            $this->pathSeparator
4601
            &&
4602 18
            \is_string($key)
4603
            &&
4604 18
            \strpos($key, $this->pathSeparator) !== false
4605
        ) {
4606
            $path = \explode($this->pathSeparator, (string) $key);
4607
4608
            if ($path !== false) {
4609
                // crawl though the keys
4610
                while (\count($path, \COUNT_NORMAL) > 1) {
4611
                    $key = \array_shift($path);
4612
4613
                    if (!$this->has($key)) {
4614
                        return false;
4615
                    }
4616
4617
                    $this->array = &$this->array[$key];
4618
                }
4619
4620
                $key = \array_shift($path);
4621
            }
4622
        }
4623
4624 18
        unset($this->array[$key]);
4625
4626 18
        return true;
4627
    }
4628
4629
    /**
4630
     * Internal mechanic of set method.
4631
     *
4632
     * @param int|string|null $key
4633
     * @param mixed           $value
4634
     * @param bool            $checkProperties
4635
     *
4636
     * @return bool
4637
     */
4638 823
    protected function internalSet($key, $value, $checkProperties = true): bool
4639
    {
4640
        if (
4641 823
            $checkProperties === true
4642
            &&
4643 823
            $this->properties !== []
4644
        ) {
4645 13
            if (isset($this->properties[$key]) === false) {
4646
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4647
            }
4648
4649 13
            $this->properties[$key]->checkType($value);
4650
        }
4651
4652 822
        if ($key === null) {
4653
            return false;
4654
        }
4655
4656 822
        $this->generatorToArray();
4657
4658 822
        $array = &$this->array;
4659
4660
        if (
4661 822
            $this->pathSeparator
4662
            &&
4663 822
            \is_string($key)
4664
            &&
4665 822
            \strpos($key, $this->pathSeparator) !== false
4666
        ) {
4667 3
            $path = \explode($this->pathSeparator, (string) $key);
4668
4669 3
            if ($path !== false) {
4670
                // crawl through the keys
4671 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4672 3
                    $key = \array_shift($path);
4673
4674 3
                    $array = &$array[$key];
4675
                }
4676
4677 3
                $key = \array_shift($path);
4678
            }
4679
        }
4680
4681 822
        $array[$key] = $value;
4682
4683 822
        return true;
4684
    }
4685
4686
    /**
4687
     * Convert a object into an array.
4688
     *
4689
     * @param object $object
4690
     *
4691
     * @return mixed
4692
     */
4693 5
    protected static function objectToArray($object)
4694
    {
4695 5
        if (!\is_object($object)) {
4696 4
            return $object;
4697
        }
4698
4699 5
        if (\is_object($object)) {
4700 5
            $object = \get_object_vars($object);
4701
        }
4702
4703 5
        return \array_map(['static', 'objectToArray'], $object);
4704
    }
4705
4706
    /**
4707
     * sorting keys
4708
     *
4709
     * @param array      $elements
4710
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4711
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4712
     *                              <strong>SORT_NATURAL</strong></p>
4713
     *
4714
     * @return static
4715
     *                <p>(Mutable) Return this Arrayy object.</p>
4716
     */
4717 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4718
    {
4719 18
        $direction = $this->getDirection($direction);
4720
4721
        switch ($direction) {
4722 18
            case 'desc':
4723 18
            case \SORT_DESC:
4724 6
                \krsort($elements, $strategy);
4725
4726 6
                break;
4727 13
            case 'asc':
4728 13
            case \SORT_ASC:
4729
            default:
4730 13
                \ksort($elements, $strategy);
4731
        }
4732
4733 18
        return $this;
4734
    }
4735
4736
    /**
4737
     * @param array      $elements  <p>Warning: used as reference</p>
4738
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4739
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4740
     *                              <strong>SORT_NATURAL</strong></p>
4741
     * @param bool       $keepKeys
4742
     *
4743
     * @return static
4744
     *                <p>(Mutable) Return this Arrayy object.</p>
4745
     */
4746 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4747
    {
4748 20
        $direction = $this->getDirection($direction);
4749
4750 20
        if (!$strategy) {
4751 20
            $strategy = \SORT_REGULAR;
4752
        }
4753
4754
        switch ($direction) {
4755 20
            case 'desc':
4756 20
            case \SORT_DESC:
4757 9
                if ($keepKeys) {
4758 5
                    \arsort($elements, $strategy);
4759
                } else {
4760 4
                    \rsort($elements, $strategy);
4761
                }
4762
4763 9
                break;
4764 11
            case 'asc':
4765 11
            case \SORT_ASC:
4766
            default:
4767 11
                if ($keepKeys) {
4768 4
                    \asort($elements, $strategy);
4769
                } else {
4770 7
                    \sort($elements, $strategy);
4771
                }
4772
        }
4773
4774 20
        return $this;
4775
    }
4776
4777
    /**
4778
     * @return bool
4779
     */
4780 862
    private function generatorToArray(): bool
4781
    {
4782 862
        if ($this->generator) {
4783 1
            $this->array = $this->getArray();
4784 1
            $this->generator = null;
4785
4786 1
            return true;
4787
        }
4788
4789 862
        return false;
4790
    }
4791
4792
    /**
4793
     * @return Property[]
4794
     */
4795 15
    private function getPropertiesFromPhpDoc(): array
4796
    {
4797 15
        static $PROPERTY_CACHE = [];
4798 15
        $cacheKey = 'Class::' . static::class;
4799
4800 15
        if (isset($PROPERTY_CACHE[$cacheKey])) {
4801 14
            return $PROPERTY_CACHE[$cacheKey];
4802
        }
4803
4804
        // init
4805 2
        $properties = [];
4806
4807 2
        $reflector = new \ReflectionClass($this);
4808 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
4809 2
        $docComment = $reflector->getDocComment();
4810 2
        if ($docComment) {
4811 2
            $docblock = $factory->create($docComment);
4812 2
            foreach ($docblock->getTagsByName('property') as $tag) {
4813
                /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
4814 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
4815
            }
4816
        }
4817
4818 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
4819
    }
4820
}
4821