Completed
Pull Request — master (#29)
by Lars
04:30
created

Arrayy::createFromString()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4.0092

Importance

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