Completed
Push — master ( 1d4eca...2da837 )
by Lars
01:50
created

Arrayy::moveElement()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 6.0029

Importance

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