Completed
Push — master ( 9cfa33...b19c09 )
by Lars
02:06
created

Arrayy::matches()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16

Duplication

Lines 16
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 1
dl 16
loc 16
ccs 8
cts 8
cp 1
crap 4
rs 9.7333
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 string
25
     */
26
    protected $iteratorClass = ArrayyIterator::class;
27
28
    /**
29
     * @var string
30
     */
31
    protected $pathSeparator = '.';
32
33
    /**
34
     * @var bool
35
     */
36
    protected $checkPropertyTypes = false;
37
38
    /**
39
     * @var bool
40
     */
41
    protected $checkForMissingPropertiesInConstructor = false;
42
43
    /**
44
     * @var bool
45
     */
46
    protected $checkPropertiesMismatchInConstructor = false;
47
48
    /**
49
     * @var array|Property[]
50
     */
51
    protected $properties = [];
52
53
    /** @noinspection MagicMethodsValidityInspection */
54
    /** @noinspection PhpMissingParentConstructorInspection */
55
56
    /**
57
     * Initializes
58
     *
59
     * @param mixed  $array                                  <p>
60
     *                                                       Should be an array, otherwise it will try to convert
61
     *                                                       it into an array.
62
     *                                                       </p>
63
     * @param string $iteratorClass                          optional <p>
64
     *                                                       You can overwrite the ArrayyIterator, but mostly you don't
65
     *                                                       need this option.
66
     *                                                       </p>
67
     * @param bool   $checkForMissingPropertiesInConstructor optional <p>
68
     *                                                       You need to extend the "Arrayy"-class and you need to set
69
     *                                                       the $checkPropertiesMismatchInConstructor class property to
70
     *                                                       true, otherwise this option didn't not work anyway.
71
     *                                                       </p>
72
     */
73 890
    public function __construct($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true)
74
    {
75 890
        $array = $this->fallbackForArray($array);
76
77
        // used only for serialize + unserialize, all other methods are overwritten
78 888
        parent::__construct([], 0, $iteratorClass);
79
80
        if (
81 888
            $this->checkPropertyTypes === true
82
            ||
83
            (
84 879
                $this->checkForMissingPropertiesInConstructor === true
85
                &&
86 888
                $checkForMissingPropertiesInConstructor === true
87
            )
88
        ) {
89 9
            $this->properties = $this->getPublicProperties();
90
        }
91
92
        if (
93 888
            $this->checkPropertiesMismatchInConstructor === true
94
            &&
95 888
            \count($array) !== 0
96
            &&
97 888
            \count(\array_diff_key($this->properties, $array)) > 0
98
        ) {
99 1
            throw new \InvalidArgumentException('Property mismatch - input: ' . \print_r(\array_keys($array), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
100
        }
101
102 887
        \array_walk(
103
            $array,
104
            function (&$value, &$key) {
105 775
                $this->internalSet($key, $value);
106 887
            }
107
        );
108
109 884
        $this->setIteratorClass($iteratorClass);
110 884
    }
111
112
    /**
113
     * Call object as function.
114
     *
115
     * @param mixed $key
116
     *
117
     * @return mixed
118
     */
119 1
    public function __invoke($key = null)
120
    {
121 1
        if ($key !== null) {
122 1
            return $this->array[$key] ?? false;
123
        }
124
125
        return (array) $this->array;
126
    }
127
128
    /**
129
     * Whether or not an element exists by key.
130
     *
131
     * @param mixed $key
132
     *
133
     * @return bool
134
     *              <p>True is the key/index exists, otherwise false.</p>
135
     */
136
    public function __isset($key)
137
    {
138
        return $this->offsetExists($key);
139
    }
140
141
    /**
142
     * Assigns a value to the specified element.
143
     *
144
     * @param mixed $key
145
     * @param mixed $value
146
     */
147 2
    public function __set($key, $value)
148
    {
149 2
        $this->internalSet($key, $value);
150 2
    }
151
152
    /**
153
     * magic to string
154
     *
155
     * @return string
156
     */
157 15
    public function __toString()
158
    {
159 15
        return $this->toString();
160
    }
161
162
    /**
163
     * Unset element by key.
164
     *
165
     * @param mixed $key
166
     */
167
    public function __unset($key)
168
    {
169
        $this->internalRemove($key);
170
    }
171
172
    /**
173
     * Get a value by key.
174
     *
175
     * @param mixed $key
176
     *
177
     * @return mixed
178
     *               <p>Get a Value from the current array.</p>
179
     */
180 3
    public function &__get($key)
181
    {
182 3
        $return = $this->get($key);
183
184 3
        if (\is_array($return)) {
185
            return static::create($return, $this->iteratorClass, false);
186
        }
187
188 3
        return $return;
189
    }
190
191
    /**
192
     * alias: for "Arrayy->append()"
193
     *
194
     * @see Arrayy::append()
195
     *
196
     * @param mixed $value
197
     *
198
     * @return static
199
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
200
     */
201 1
    public function add($value)
202
    {
203 1
        return $this->append($value);
204
    }
205
206
    /**
207
     * Append a (key) + value to the current array.
208
     *
209
     * @param mixed $value
210
     * @param mixed $key
211
     *
212
     * @return static
213
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
214
     */
215 9
    public function append($value, $key = null)
216
    {
217 9
        if ($key !== null) {
218
            if (
219
                isset($this->array[$key])
220
                &&
221
                \is_array($this->array[$key])
222
            ) {
223
                $this->array[$key][] = $value;
224
            } else {
225
                $this->array[$key] = $value;
226
            }
227
        } else {
228 9
            $this->array[] = $value;
229
        }
230
231 9
        return $this;
232
    }
233
234
    /**
235
     * Sort the entries by value.
236
     *
237
     * @param int $sort_flags [optional] <p>
238
     *                        You may modify the behavior of the sort using the optional
239
     *                        parameter sort_flags, for details
240
     *                        see sort.
241
     *                        </p>
242
     *
243
     * @return static
244
     *                <p>(Mutable) Return this Arrayy object.</p>
245
     */
246 4
    public function asort(int $sort_flags = 0)
247
    {
248 4
        \asort($this->array, $sort_flags);
249
250 4
        return $this;
251
    }
252
253
    /**
254
     * Counts all elements in an array, or something in an object.
255
     *
256
     * <p>
257
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
258
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
259
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
260
     * implemented and used in PHP.
261
     * </p>
262
     *
263
     * @see http://php.net/manual/en/function.count.php
264
     *
265
     * @param int $mode [optional] If the optional mode parameter is set to
266
     *                  COUNT_RECURSIVE (or 1), count
267
     *                  will recursively count the array. This is particularly useful for
268
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
269
     *
270
     * @return int
271
     *             <p>
272
     *             The number of elements in var, which is
273
     *             typically an array, since anything else will have one
274
     *             element.
275
     *             </p>
276
     *             <p>
277
     *             If var is not an array or an object with
278
     *             implemented Countable interface,
279
     *             1 will be returned.
280
     *             There is one exception, if var is &null;,
281
     *             0 will be returned.
282
     *             </p>
283
     *             <p>
284
     *             Caution: count may return 0 for a variable that isn't set,
285
     *             but it may also return 0 for a variable that has been initialized with an
286
     *             empty array. Use isset to test if a variable is set.
287
     *             </p>
288
     */
289 37
    public function count(int $mode = \COUNT_NORMAL): int
290
    {
291 37
        return \count($this->array, $mode);
292
    }
293
294
    /**
295
     * Exchange the array for another one.
296
     *
297
     * @param array|static $data
298
     *
299
     * @return array
300
     */
301 1
    public function exchangeArray($data): array
302
    {
303 1
        $this->array = $this->fallbackForArray($data);
304
305 1
        return $this->array;
306
    }
307
308
    /**
309
     * Creates a copy of the ArrayyObject.
310
     *
311
     * @return array
312
     */
313 1
    public function getArrayCopy(): array
314
    {
315 1
        return $this->array;
316
    }
317
318
    /**
319
     * Returns a new ArrayyIterator, thus implementing the \ArrayIterator interface.
320
     *
321
     * @return \ArrayIterator
322
     *                        <p>An iterator for the values in the array.</p>
323
     */
324 12
    public function getIterator(): \ArrayIterator
325
    {
326 12
        $iterator = $this->getIteratorClass();
327
328 12
        return new $iterator($this->array);
329
    }
330
331
    /**
332
     * Gets the iterator classname for the ArrayObject.
333
     *
334
     * @return string
335
     */
336 12
    public function getIteratorClass(): string
337
    {
338 12
        return $this->iteratorClass;
339
    }
340
341
    /**
342
     * Sort the entries by key
343
     *
344
     * @param int $sort_flags [optional] <p>
345
     *                        You may modify the behavior of the sort using the optional
346
     *                        parameter sort_flags, for details
347
     *                        see sort.
348
     *                        </p>
349
     *
350
     * @return static
351
     *                <p>(Mutable) Return this Arrayy object.</p>
352
     */
353 4
    public function ksort(int $sort_flags = 0)
354
    {
355 4
        \ksort($this->array, $sort_flags);
356
357 4
        return $this;
358
    }
359
360
    /**
361
     * Sort an array using a case insensitive "natural order" algorithm
362
     *
363
     * @return static
364
     *                <p>(Mutable) Return this Arrayy object.</p>
365
     */
366
    public function natcasesort()
367
    {
368
        \natcasesort($this->array);
369
370
        return $this;
371
    }
372
373
    /**
374
     * Sort entries using a "natural order" algorithm
375
     *
376
     * @return static
377
     *                <p>(Mutable) Return this Arrayy object.</p>
378
     */
379 1
    public function natsort()
380
    {
381 1
        \natsort($this->array);
382
383 1
        return $this;
384
    }
385
386
    /**
387
     * Whether or not an offset exists.
388
     *
389
     * @param bool|float|int|string $offset
390
     *
391
     * @return bool
392
     */
393 44
    public function offsetExists($offset): bool
394
    {
395 44
        if ($this->isEmpty()) {
396 4
            return false;
397
        }
398
399
        // php cast "bool"-index into "int"-index
400 40
        if ((bool) $offset === $offset) {
401 1
            $offset = (int) $offset;
402
        }
403
404 40
        $tmpReturn = \array_key_exists($offset, $this->array);
405
406
        if (
407 40
            $tmpReturn === true
408
            ||
409
            (
410 16
                $tmpReturn === false
411
                &&
412 40
                \strpos((string) $offset, $this->pathSeparator) === false
413
            )
414
        ) {
415 38
            return $tmpReturn;
416
        }
417
418 3
        $offsetExists = false;
419
420 3
        if (\strpos((string) $offset, $this->pathSeparator) !== false) {
421 3
            $offsetExists = false;
422 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
423 3
            $lastOffset = \array_pop($explodedPath);
424 3
            $containerPath = \implode($this->pathSeparator, $explodedPath);
425
426 3
            $this->callAtPath(
427 3
                $containerPath,
428
                static function ($container) use ($lastOffset, &$offsetExists) {
429 3
                    $offsetExists = \array_key_exists($lastOffset, $container);
430 3
                }
431
            );
432
        }
433
434 3
        return $offsetExists;
435
    }
436
437
    /**
438
     * Returns the value at specified offset.
439
     *
440
     * @param float|int|string $offset
441
     *
442
     * @return mixed
443
     *               <p>Will return null if the offset did not exists.</p>
444
     */
445 30
    public function offsetGet($offset)
446
    {
447 30
        return $this->offsetExists($offset) ? $this->get($offset) : null;
448
    }
449
450
    /**
451
     * Assigns a value to the specified offset.
452
     *
453
     * @param int|string|null $offset
454
     * @param mixed           $value
455
     */
456 20
    public function offsetSet($offset, $value)
457
    {
458 20
        if ($offset === null) {
459 4
            $this->array[] = $value;
460
        } else {
461 16
            $this->internalSet($offset, $value);
462
        }
463 20
    }
464
465
    /**
466
     * Unset an offset.
467
     *
468
     * @param float|int|string $offset
469
     */
470 7
    public function offsetUnset($offset)
471
    {
472 7
        if ($this->isEmpty()) {
473 1
            return;
474
        }
475
476 6
        if (\array_key_exists($offset, $this->array)) {
477 4
            unset($this->array[$offset]);
478
479 4
            return;
480
        }
481
482 3
        if (\strpos((string) $offset, $this->pathSeparator) !== false) {
483 2
            $path = \explode($this->pathSeparator, (string) $offset);
484 2
            $pathToUnset = \array_pop($path);
485
486 2
            $this->callAtPath(
487 2
                \implode($this->pathSeparator, $path),
488
                static function (&$offset) use ($pathToUnset) {
489 2
                    unset($offset[$pathToUnset]);
490 2
                }
491
            );
492
        }
493 3
    }
494
495
    /** @noinspection SenselessProxyMethodInspection | can not add return type, because of the "Serializable" interface */
496
497
    /**
498
     * Serialize the current "Arrayy"-object.
499
     *
500
     * @return string
501
     */
502 2
    public function serialize()
503
    {
504 2
        return parent::serialize();
505
    }
506
507
    /**
508
     * Sets the iterator classname for the current "Arrayy"-object.
509
     *
510
     * @param string $class
511
     *
512
     * @throws \InvalidArgumentException
513
     */
514 884
    public function setIteratorClass($class)
515
    {
516 884
        if (\class_exists($class)) {
517 884
            $this->iteratorClass = $class;
518
519 884
            return;
520
        }
521
522
        if (\strpos($class, '\\') === 0) {
523
            $class = '\\' . $class;
524
            if (\class_exists($class)) {
525
                $this->iteratorClass = $class;
526
527
                return;
528
            }
529
        }
530
531
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
532
    }
533
534
    /**
535
     * Sort the entries with a user-defined comparison function and maintain key association.
536
     *
537
     * @param \callable $function
538
     *
539
     * @throws \InvalidArgumentException
540
     *
541
     * @return static
542
     *                <p>(Mutable) Return this Arrayy object.</p>
543
     */
544 View Code Duplication
    public function uasort($function)
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...
545
    {
546
        if (!\is_callable($function)) {
547
            throw new \InvalidArgumentException(
548
                'Passed function must be callable'
549
            );
550
        }
551
552
        \uasort($this->array, $function);
553
554
        return $this;
555
    }
556
557
    /**
558
     * Sort the entries by keys using a user-defined comparison function.
559
     *
560
     * @param \callable $function
561
     *
562
     * @throws \InvalidArgumentException
563
     *
564
     * @return static
565
     *                <p>(Mutable) Return this Arrayy object.</p>
566
     */
567 5
    public function uksort($function)
568
    {
569 5
        return $this->customSortKeys($function);
570
    }
571
572
    /**
573
     * Unserialize an string and return the instance of the "Arrayy"-class.
574
     *
575
     * @param string $string
576
     *
577
     * @return static
578
     */
579 2
    public function unserialize($string)
580
    {
581 2
        parent::unserialize($string);
582
583 2
        return $this;
584
    }
585
586
    /**
587
     * Append a (key) + values to the current array.
588
     *
589
     * @param array $values
590
     * @param mixed $key
591
     *
592
     * @return static
593
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
594
     */
595 1
    public function appendArrayValues(array $values, $key = null)
596
    {
597 1
        if ($key !== null) {
598
            if (
599 1
                isset($this->array[$key])
600
                &&
601 1
                \is_array($this->array[$key])
602
            ) {
603 1
                foreach ($values as $value) {
604 1
                    $this->array[$key][] = $value;
605
                }
606
            } else {
607
                foreach ($values as $value) {
608 1
                    $this->array[$key] = $value;
609
                }
610
            }
611
        } else {
612
            foreach ($values as $value) {
613
                $this->array[] = $value;
614
            }
615
        }
616
617 1
        return $this;
618
    }
619
620
    /**
621
     * Add a suffix to each key.
622
     *
623
     * @param mixed $prefix
624
     *
625
     * @return static
626
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
627
     */
628 10 View Code Duplication
    public function appendToEachKey($prefix)
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...
629
    {
630
        // init
631 10
        $result = [];
632
633 10
        foreach ($this->getGenerator() as $key => $item) {
634 9
            if ($item instanceof self) {
635
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
636 9
            } elseif (\is_array($item)) {
637
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
638
                    ->appendToEachKey($prefix)
639
                    ->toArray();
640
            } else {
641 9
                $result[$prefix . $key] = $item;
642
            }
643
        }
644
645 10
        return self::create($result, $this->iteratorClass, false);
646
    }
647
648
    /**
649
     * Add a prefix to each value.
650
     *
651
     * @param mixed $prefix
652
     *
653
     * @return static
654
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
655
     */
656 10 View Code Duplication
    public function appendToEachValue($prefix)
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...
657
    {
658
        // init
659 10
        $result = [];
660
661 10
        foreach ($this->getGenerator() as $key => $item) {
662 9
            if ($item instanceof self) {
663
                $result[$key] = $item->appendToEachValue($prefix);
664 9
            } elseif (\is_array($item)) {
665
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
666 9
            } elseif (\is_object($item)) {
667 1
                $result[$key] = $item;
668
            } else {
669 9
                $result[$key] = $prefix . $item;
670
            }
671
        }
672
673 10
        return self::create($result, $this->iteratorClass, false);
674
    }
675
676
    /**
677
     * Sort an array in reverse order and maintain index association.
678
     *
679
     * @return static
680
     *                <p>(Mutable) Return this Arrayy object.</p>
681
     */
682 4
    public function arsort()
683
    {
684 4
        \arsort($this->array);
685
686 4
        return $this;
687
    }
688
689
    /**
690
     * Iterate over the current array and execute a callback for each loop.
691
     *
692
     * @param \Closure $closure
693
     *
694
     * @return static
695
     *                <p>(Immutable)</p>
696
     */
697 2
    public function at(\Closure $closure)
698
    {
699 2
        $arrayy = clone $this;
700
701 2
        foreach ($arrayy->getGenerator() as $key => $value) {
702 2
            $closure($value, $key);
703
        }
704
705 2
        return static::create(
706 2
            $arrayy->toArray(),
707 2
            $this->iteratorClass,
708 2
            false
709
        );
710
    }
711
712
    /**
713
     * Returns the average value of the current array.
714
     *
715
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
716
     *
717
     * @return float|int
718
     *                   <p>The average value.</p>
719
     */
720 10
    public function average($decimals = 0)
721
    {
722 10
        $count = \count($this->array, \COUNT_NORMAL);
723
724 10
        if (!$count) {
725 2
            return 0;
726
        }
727
728 8
        if (!\is_int($decimals)) {
729 3
            $decimals = 0;
730
        }
731
732 8
        return \round(\array_sum($this->array) / $count, $decimals);
733
    }
734
735
    /**
736
     * Changes all keys in an array.
737
     *
738
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
739
     *                  or <strong>CASE_LOWER</strong> (default)</p>
740
     *
741
     * @return static
742
     *                <p>(Immutable)</p>
743
     */
744 1
    public function changeKeyCase(int $case = \CASE_LOWER)
745
    {
746
        if (
747 1
            $case !== \CASE_LOWER
748
            &&
749 1
            $case !== \CASE_UPPER
750
        ) {
751
            $case = \CASE_LOWER;
752
        }
753
754 1
        $return = [];
755 1
        foreach ($this->array as $key => $value) {
756 1
            if ($case === \CASE_LOWER) {
757
                /** @noinspection PhpComposerExtensionStubsInspection */
758 1
                $key = \mb_strtolower((string) $key);
759
            } else {
760
                /** @noinspection PhpComposerExtensionStubsInspection */
761 1
                $key = \mb_strtoupper((string) $key);
762
            }
763
764 1
            $return[$key] = $value;
765
        }
766
767 1
        return static::create(
768 1
            $return,
769 1
            $this->iteratorClass,
770 1
            false
771
        );
772
    }
773
774
    /**
775
     * Change the path separator of the array wrapper.
776
     *
777
     * By default, the separator is: "."
778
     *
779
     * @param string $separator <p>Separator to set.</p>
780
     *
781
     * @return static
782
     *                <p>Mutable</p>
783
     */
784 1
    public function changeSeparator($separator)
785
    {
786 1
        $this->pathSeparator = $separator;
787
788 1
        return $this;
789
    }
790
791
    /**
792
     * Create a chunked version of the current array.
793
     *
794
     * @param int  $size         <p>Size of each chunk.</p>
795
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
796
     *
797
     * @return static
798
     *                <p>(Immutable) A new array of chunks from the original array.</p>
799
     */
800 4
    public function chunk($size, $preserveKeys = false)
801
    {
802 4
        return static::create(
803 4
            \array_chunk($this->array, $size, $preserveKeys),
804 4
            $this->iteratorClass,
805 4
            false
806
        );
807
    }
808
809
    /**
810
     * Clean all falsy values from the current array.
811
     *
812
     * @return static
813
     *                <p>(Immutable)</p>
814
     */
815 8
    public function clean()
816
    {
817 8
        return $this->filter(
818
            static function ($value) {
819 7
                return (bool) $value;
820 8
            }
821
        );
822
    }
823
824
    /**
825
     * WARNING!!! -> Clear the current array.
826
     *
827
     * @return static
828
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
829
     */
830 4
    public function clear()
831
    {
832 4
        $this->array = [];
833
834 4
        return $this;
835
    }
836
837
    /**
838
     * Check if an item is in the current array.
839
     *
840
     * @param float|int|string $value
841
     * @param bool             $recursive
842
     * @param bool             $strict
843
     *
844
     * @return bool
845
     */
846 23
    public function contains($value, $recursive = false, $strict = true): bool
847
    {
848 23
        if ($recursive === true) {
849 19
            return $this->in_array_recursive($value, $this->array, $strict);
850
        }
851
852 13
        return \in_array($value, $this->array, $strict);
853
    }
854
855
    /**
856
     * Check if an (case-insensitive) string is in the current array.
857
     *
858
     * @param string $value
859
     * @param bool   $recursive
860
     *
861
     * @return bool
862
     */
863 26
    public function containsCaseInsensitive($value, $recursive = false): bool
864
    {
865 26
        if ($recursive === true) {
866
            /** @noinspection PhpComposerExtensionStubsInspection */
867 26
            return $this->in_array_recursive(
868 26
                \mb_strtoupper((string) $value),
869 26
                $this->walk(
870
                    static function (&$val) {
871
                        /** @noinspection PhpComposerExtensionStubsInspection */
872 22
                        $val = \mb_strtoupper((string) $val);
873 26
                    },
874 26
                    true
875 26
                )->getArray(),
876 26
                true
877
            );
878
        }
879
880
        /** @noinspection PhpComposerExtensionStubsInspection */
881 13
        return \in_array(
882 13
            \mb_strtoupper((string) $value),
883 13
            $this->walk(
884
                static function (&$val) {
885
                    /** @noinspection PhpComposerExtensionStubsInspection */
886 11
                    $val = \mb_strtoupper((string) $val);
887 13
                },
888 13
                false
889 13
            )->getArray(),
890 13
            true
891
        );
892
    }
893
894
    /**
895
     * Check if the given key/index exists in the array.
896
     *
897
     * @param float|int|string $key <p>key/index to search for</p>
898
     *
899
     * @return bool
900
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
901
     */
902 4
    public function containsKey($key): bool
903
    {
904 4
        return $this->offsetExists($key);
905
    }
906
907
    /**
908
     * Check if all given needles are present in the array as key/index.
909
     *
910
     * @param array $needles   <p>The keys you are searching for.</p>
911
     * @param bool  $recursive
912
     *
913
     * @return bool
914
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
915
     */
916 2
    public function containsKeys(array $needles, $recursive = false): bool
917
    {
918 2
        if ($recursive === true) {
919 2
            return \count(
920 2
                       \array_intersect($needles, $this->keys(true)->getArray()),
921 2
                       \COUNT_RECURSIVE
922
                   )
923
                   ===
924 2
                   \count(
925 2
                       $needles,
926 2
                       \COUNT_RECURSIVE
927
                   );
928
        }
929
930 1
        return \count(
931 1
                   \array_intersect($needles, $this->keys()->getArray()),
932 1
                   \COUNT_NORMAL
933
               )
934
               ===
935 1
               \count(
936 1
                   $needles,
937 1
                   \COUNT_NORMAL
938
               );
939
    }
940
941
    /**
942
     * Check if all given needles are present in the array as key/index.
943
     *
944
     * @param array $needles <p>The keys you are searching for.</p>
945
     *
946
     * @return bool
947
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
948
     */
949 1
    public function containsKeysRecursive(array $needles): bool
950
    {
951 1
        return $this->containsKeys($needles, true);
952
    }
953
954
    /**
955
     * alias: for "Arrayy->contains()"
956
     *
957
     * @see Arrayy::contains()
958
     *
959
     * @param float|int|string $value
960
     *
961
     * @return bool
962
     */
963 9
    public function containsValue($value): bool
964
    {
965 9
        return $this->contains($value);
966
    }
967
968
    /**
969
     * alias: for "Arrayy->contains($value, true)"
970
     *
971
     * @see Arrayy::contains()
972
     *
973
     * @param float|int|string $value
974
     *
975
     * @return bool
976
     */
977 18
    public function containsValueRecursive($value): bool
978
    {
979 18
        return $this->contains($value, true);
980
    }
981
982
    /**
983
     * Check if all given needles are present in the array.
984
     *
985
     * @param array $needles
986
     *
987
     * @return bool
988
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
989
     */
990 1
    public function containsValues(array $needles): bool
991
    {
992 1
        return \count(\array_intersect($needles, $this->array), \COUNT_NORMAL)
993
               ===
994 1
               \count($needles, \COUNT_NORMAL);
995
    }
996
997
    /**
998
     * Counts all the values of an array
999
     *
1000
     * @see http://php.net/manual/en/function.array-count-values.php
1001
     *
1002
     * @return static
1003
     *                <p>
1004
     *                (Immutable)
1005
     *                An associative Arrayy-object of values from input as
1006
     *                keys and their count as value.
1007
     *                </p>
1008
     */
1009 1
    public function countValues(): self
1010
    {
1011 1
        return new static(\array_count_values($this->array));
1012
    }
1013
1014
    /**
1015
     * Creates an Arrayy object.
1016
     *
1017
     * @param mixed  $array
1018
     * @param string $iteratorClass
1019
     * @param bool   $checkForMissingPropertiesInConstructor
1020
     *
1021
     * @return static
1022
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1023
     */
1024 535
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1025
    {
1026 535
        return new static($array, $iteratorClass, $checkForMissingPropertiesInConstructor);
1027
    }
1028
1029
    /**
1030
     * WARNING: Creates an Arrayy object by reference.
1031
     *
1032
     * @param array $array
1033
     *
1034
     * @return static
1035
     *                <p>(Mutable) Return this Arrayy object.</p>
1036
     */
1037 1
    public function createByReference(array &$array = []): self
1038
    {
1039 1
        $array = $this->fallbackForArray($array);
1040
1041 1
        $this->array = &$array;
1042
1043 1
        return $this;
1044
    }
1045
1046
    /**
1047
     * Create an new instance filled with a copy of values from a "Generator"-object.
1048
     *
1049
     * @param \Generator $generator
1050
     *
1051
     * @return static
1052
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1053
     */
1054 4
    public static function createFromGeneratorImmutable(\Generator $generator)
1055
    {
1056
        // init
1057 4
        $array = new static();
1058
1059 4
        foreach ($generator as $key => $value) {
1060 3
            $array[$key] = $value;
1061
        }
1062
1063 4
        return $array;
1064
    }
1065
1066
    /**
1067
     * Create an new Arrayy object via JSON.
1068
     *
1069
     * @param string $json
1070
     *
1071
     * @return static
1072
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1073
     */
1074 5
    public static function createFromJson(string $json)
1075
    {
1076
        /** @noinspection PhpComposerExtensionStubsInspection */
1077 5
        return static::create(\json_decode($json, true));
1078
    }
1079
1080
    /**
1081
     * Create an new instance filled with values from an object that have implemented ArrayAccess.
1082
     *
1083
     * @param \ArrayAccess $object <p>Object that implements ArrayAccess</p>
1084
     *
1085
     * @return static
1086
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1087
     */
1088 4
    public static function createFromObject(\ArrayAccess $object)
1089
    {
1090
        // init
1091 4
        $array = new static();
1092
1093 4
        if ($object instanceof self) {
1094 4
            $objectArray = $object->getGenerator();
1095
        } else {
1096
            $objectArray = $object;
1097
        }
1098
1099 4
        foreach ($objectArray as $key => $value) {
1100 3
            $array[$key] = $value;
1101
        }
1102
1103 4
        return $array;
1104
    }
1105
1106
    /**
1107
     * Create an new instance filled with values from an object.
1108
     *
1109
     * @param object $object
1110
     *
1111
     * @return static
1112
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1113
     */
1114 5
    public static function createFromObjectVars($object): self
1115
    {
1116 5
        return new static(self::objectToArray($object));
1117
    }
1118
1119
    /**
1120
     * Create an new Arrayy object via string.
1121
     *
1122
     * @param string      $str       <p>The input string.</p>
1123
     * @param string|null $delimiter <p>The boundary string.</p>
1124
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1125
     *                               used.</p>
1126
     *
1127
     * @return static
1128
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1129
     */
1130 8
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null)
1131
    {
1132 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...
1133 1
            \preg_match_all($regEx, $str, $array);
1134
1135 1
            if (!empty($array)) {
1136 1
                $array = $array[0];
1137
            }
1138
        } else {
1139 7
            $array = \explode($delimiter, $str);
1140
        }
1141
1142
        // trim all string in the array
1143 8
        \array_walk(
1144
            $array,
1145
            static function (&$val) {
1146
                /** @noinspection ReferenceMismatchInspection */
1147 8
                if (\is_string($val)) {
1148 8
                    $val = \trim($val);
1149
                }
1150 8
            }
1151
        );
1152
1153 8
        return static::create($array);
1154
    }
1155
1156
    /**
1157
     * Create an new instance containing a range of elements.
1158
     *
1159
     * @param mixed $low  <p>First value of the sequence.</p>
1160
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1161
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1162
     *
1163
     * @return static
1164
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1165
     */
1166 1
    public static function createWithRange($low, $high, int $step = 1)
1167
    {
1168 1
        return static::create(\range($low, $high, $step));
1169
    }
1170
1171
    /**
1172
     * Custom sort by index via "uksort".
1173
     *
1174
     * @see http://php.net/manual/en/function.uksort.php
1175
     *
1176
     * @param \callable $function
1177
     *
1178
     * @throws \InvalidArgumentException
1179
     *
1180
     * @return static
1181
     *                <p>(Mutable) Return this Arrayy object.</p>
1182
     */
1183 5 View Code Duplication
    public function customSortKeys($function)
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...
1184
    {
1185 5
        if (!\is_callable($function)) {
1186
            throw new \InvalidArgumentException(
1187
                'Passed function must be callable'
1188
            );
1189
        }
1190
1191 5
        \uksort($this->array, $function);
1192
1193 5
        return $this;
1194
    }
1195
1196
    /**
1197
     * Custom sort by value via "usort".
1198
     *
1199
     * @see http://php.net/manual/en/function.usort.php
1200
     *
1201
     * @param \callable $function
1202
     *
1203
     * @throws \InvalidArgumentException
1204
     *
1205
     * @return static
1206
     *                <p>(Mutable) Return this Arrayy object.</p>
1207
     */
1208 5 View Code Duplication
    public function customSortValues($function)
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...
1209
    {
1210 5
        if (!\is_callable($function)) {
1211
            throw new \InvalidArgumentException(
1212
                'Passed function must be callable'
1213
            );
1214
        }
1215
1216 5
        \usort($this->array, $function);
1217
1218 5
        return $this;
1219
    }
1220
1221
    /**
1222
     * Return values that are only in the current array.
1223
     *
1224
     * @param array $array
1225
     *
1226
     * @return static
1227
     *                <p>(Immutable)</p>
1228
     */
1229 12
    public function diff(array $array = [])
1230
    {
1231 12
        $result = \array_diff($this->array, $array);
1232
1233 12
        return static::create($result, $this->iteratorClass, false);
1234
    }
1235
1236
    /**
1237
     * Return values that are only in the current multi-dimensional array.
1238
     *
1239
     * @param array      $array
1240
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1241
     *
1242
     * @return static
1243
     *                <p>(Immutable)</p>
1244
     */
1245 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null)
1246
    {
1247
        // init
1248 1
        $result = [];
1249
1250
        if (
1251 1
            $helperVariableForRecursion !== null
1252
            &&
1253 1
            \is_array($helperVariableForRecursion)
1254
        ) {
1255
            $arrayForTheLoop = $helperVariableForRecursion;
1256
        } else {
1257 1
            $arrayForTheLoop = $this->getGenerator();
1258
        }
1259
1260 1
        foreach ($arrayForTheLoop as $key => $value) {
1261 1
            if ($value instanceof self) {
1262
                $value = $value->getArray();
1263
            }
1264
1265 1
            if (\array_key_exists($key, $array)) {
1266 1
                if ($value !== $array[$key]) {
1267 1
                    $result[$key] = $value;
1268
                }
1269
            } else {
1270 1
                $result[$key] = $value;
1271
            }
1272
        }
1273
1274 1
        return static::create($result, $this->iteratorClass, false);
1275
    }
1276
1277
    /**
1278
     * Return values that are only in the new $array.
1279
     *
1280
     * @param array $array
1281
     *
1282
     * @return static
1283
     *                <p>(Immutable)</p>
1284
     */
1285 8
    public function diffReverse(array $array = [])
1286
    {
1287 8
        return static::create(
1288 8
            \array_diff($array, $this->array),
1289 8
            $this->iteratorClass,
1290 8
            false
1291
        );
1292
    }
1293
1294
    /**
1295
     * Divide an array into two arrays. One with keys and the other with values.
1296
     *
1297
     * @return static
1298
     *                <p>(Immutable)</p>
1299
     */
1300 1
    public function divide()
1301
    {
1302 1
        return static::create(
1303
            [
1304 1
                $this->keys(),
1305 1
                $this->values(),
1306
            ],
1307 1
            $this->iteratorClass,
1308 1
            false
1309
        );
1310
    }
1311
1312
    /**
1313
     * Iterate over the current array and modify the array's value.
1314
     *
1315
     * @param \Closure $closure
1316
     *
1317
     * @return static
1318
     *                <p>(Immutable)</p>
1319
     */
1320 4 View Code Duplication
    public function each(\Closure $closure)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1321
    {
1322
        // init
1323 4
        $array = [];
1324
1325 4
        foreach ($this->getGenerator() as $key => $value) {
1326 4
            $array[$key] = $closure($value, $key);
1327
        }
1328
1329 4
        return static::create($array, $this->iteratorClass, false);
1330
    }
1331
1332
    /**
1333
     * Check if a value is in the current array using a closure.
1334
     *
1335
     * @param \Closure $closure
1336
     *
1337
     * @return bool
1338
     *              <p>Returns true if the given value is found, false otherwise.</p>
1339
     */
1340 4
    public function exists(\Closure $closure): bool
1341
    {
1342
        // init
1343 4
        $isExists = false;
1344
1345 4
        foreach ($this->getGenerator() as $key => $value) {
1346 3
            if ($closure($value, $key)) {
1347 1
                $isExists = true;
1348
1349 3
                break;
1350
            }
1351
        }
1352
1353 4
        return $isExists;
1354
    }
1355
1356
    /**
1357
     * Fill the array until "$num" with "$default" values.
1358
     *
1359
     * @param int   $num
1360
     * @param mixed $default
1361
     *
1362
     * @return static
1363
     *                <p>(Immutable)</p>
1364
     */
1365 8
    public function fillWithDefaults(int $num, $default = null)
1366
    {
1367 8
        if ($num < 0) {
1368 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1369
        }
1370
1371 7
        $tmpArray = $this->array;
1372
1373 7
        $count = \count($tmpArray);
1374
1375 7
        while ($count < $num) {
1376 4
            $tmpArray[] = $default;
1377 4
            ++$count;
1378
        }
1379
1380 7
        return static::create($tmpArray, $this->iteratorClass, false);
1381
    }
1382
1383
    /**
1384
     * Find all items in an array that pass the truth test.
1385
     *
1386
     * @param \Closure|null $closure [optional] <p>
1387
     *                               The callback function to use
1388
     *                               </p>
1389
     *                               <p>
1390
     *                               If no callback is supplied, all entries of
1391
     *                               input equal to false (see
1392
     *                               converting to
1393
     *                               boolean) will be removed.
1394
     *                               </p>
1395
     *                               * @param int $flag [optional] <p>
1396
     *                               Flag determining what arguments are sent to <i>callback</i>:
1397
     *                               </p><ul>
1398
     *                               <li>
1399
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1400
     *                               to <i>callback</i> instead of the value</span>
1401
     *                               </li>
1402
     *                               <li>
1403
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1404
     *                               arguments to <i>callback</i> instead of the value</span>
1405
     *                               </li>
1406
     *                               </ul>
1407
     *
1408
     * @return static
1409
     *                <p>(Immutable)</p>
1410
     */
1411 10
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
1412
    {
1413 10
        if (!$closure) {
1414 1
            return $this->clean();
1415
        }
1416
1417 10
        return static::create(
1418 10
            \array_filter($this->array, $closure, $flag),
1419 10
            $this->iteratorClass,
1420 10
            false
1421
        );
1422
    }
1423
1424
    /**
1425
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1426
     * property within that.
1427
     *
1428
     * @param string          $property
1429
     * @param string|string[] $value
1430
     * @param string          $comparisonOp
1431
     *                                      <p>
1432
     *                                      'eq' (equals),<br />
1433
     *                                      'gt' (greater),<br />
1434
     *                                      'gte' || 'ge' (greater or equals),<br />
1435
     *                                      'lt' (less),<br />
1436
     *                                      'lte' || 'le' (less or equals),<br />
1437
     *                                      'ne' (not equals),<br />
1438
     *                                      'contains',<br />
1439
     *                                      'notContains',<br />
1440
     *                                      'newer' (via strtotime),<br />
1441
     *                                      'older' (via strtotime),<br />
1442
     *                                      </p>
1443
     *
1444
     * @return static
1445
     *                <p>(Immutable)</p>
1446
     */
1447 1
    public function filterBy(string $property, $value, string $comparisonOp = null)
1448
    {
1449 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...
1450 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1451
        }
1452
1453
        $ops = [
1454
            'eq' => static function ($item, $prop, $value) {
1455 1
                return $item[$prop] === $value;
1456 1
            },
1457
            'gt' => static function ($item, $prop, $value) {
1458
                return $item[$prop] > $value;
1459 1
            },
1460
            'ge' => static function ($item, $prop, $value) {
1461
                return $item[$prop] >= $value;
1462 1
            },
1463
            'gte' => static function ($item, $prop, $value) {
1464
                return $item[$prop] >= $value;
1465 1
            },
1466
            'lt' => static function ($item, $prop, $value) {
1467 1
                return $item[$prop] < $value;
1468 1
            },
1469
            'le' => static function ($item, $prop, $value) {
1470
                return $item[$prop] <= $value;
1471 1
            },
1472
            'lte' => static function ($item, $prop, $value) {
1473
                return $item[$prop] <= $value;
1474 1
            },
1475
            'ne' => static function ($item, $prop, $value) {
1476
                return $item[$prop] !== $value;
1477 1
            },
1478
            'contains' => static function ($item, $prop, $value) {
1479 1
                return \in_array($item[$prop], (array) $value, true);
1480 1
            },
1481
            'notContains' => static function ($item, $prop, $value) {
1482
                return !\in_array($item[$prop], (array) $value, true);
1483 1
            },
1484
            'newer' => static function ($item, $prop, $value) {
1485
                return \strtotime($item[$prop]) > \strtotime($value);
1486 1
            },
1487
            'older' => static function ($item, $prop, $value) {
1488
                return \strtotime($item[$prop]) < \strtotime($value);
1489 1
            },
1490
        ];
1491
1492 1
        $result = \array_values(
1493 1
            \array_filter(
1494 1
                (array) $this->array,
1495
                static function ($item) use (
1496 1
                    $property,
1497 1
                    $value,
1498 1
                    $ops,
1499 1
                    $comparisonOp
1500
                ) {
1501 1
                    $item = (array) $item;
1502 1
                    $itemArrayy = new self($item);
1503 1
                    $item[$property] = $itemArrayy->get($property, []);
1504
1505 1
                    return $ops[$comparisonOp]($item, $property, $value);
1506 1
                }
1507
            )
1508
        );
1509
1510 1
        return static::create($result, $this->iteratorClass, false);
1511
    }
1512
1513
    /**
1514
     * Find the first item in an array that passes the truth test,
1515
     *  otherwise return false
1516
     *
1517
     * @param \Closure $closure
1518
     *
1519
     * @return false|mixed
1520
     *                     <p>Return false if we did not find the value.</p>
1521
     */
1522 8
    public function find(\Closure $closure)
1523
    {
1524 8
        foreach ($this->getGenerator() as $key => $value) {
1525 6
            if ($closure($value, $key)) {
1526 6
                return $value;
1527
            }
1528
        }
1529
1530 3
        return false;
1531
    }
1532
1533
    /**
1534
     * find by ...
1535
     *
1536
     * @param string          $property
1537
     * @param string|string[] $value
1538
     * @param string          $comparisonOp
1539
     *
1540
     * @return static
1541
     *                <p>(Immutable)</p>
1542
     */
1543
    public function findBy(string $property, $value, string $comparisonOp = 'eq')
1544
    {
1545
        return $this->filterBy($property, $value, $comparisonOp);
1546
    }
1547
1548
    /**
1549
     * Get the first value from the current array.
1550
     *
1551
     * @return mixed
1552
     *               <p>Return null if there wasn't a element.</p>
1553
     */
1554 14
    public function first()
1555
    {
1556 14
        $tmpArray = $this->array;
1557
1558 14
        return \array_shift($tmpArray);
1559
    }
1560
1561
    /**
1562
     * Get the first value(s) from the current array.
1563
     *
1564
     * @param int|null $number <p>How many values you will take?</p>
1565
     *
1566
     * @return static
1567
     *                <p>(Immutable)</p>
1568
     */
1569 28 View Code Duplication
    public function firstsImmutable(int $number = null)
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...
1570
    {
1571 28
        if ($number === null) {
1572 7
            $arrayTmp = $this->array;
1573 7
            $array = (array) \array_shift($arrayTmp);
1574
        } else {
1575 21
            $number = (int) $number;
1576 21
            $arrayTmp = $this->array;
1577 21
            $array = \array_splice($arrayTmp, 0, $number);
1578
        }
1579
1580 28
        return static::create($array, $this->iteratorClass, false);
1581
    }
1582
1583
    /**
1584
     * Get the first value(s) from the current array.
1585
     *
1586
     * @param int|null $number <p>How many values you will take?</p>
1587
     *
1588
     * @return static
1589
     *                <p>(Mutable)</p>
1590
     */
1591 26
    public function firstsMutable(int $number = null)
1592
    {
1593 26
        if ($number === null) {
1594 11
            $this->array = (array) \array_shift($this->array);
1595
        } else {
1596 15
            $number = (int) $number;
1597 15
            $this->array = \array_splice($this->array, 0, $number);
1598
        }
1599
1600 26
        return $this;
1601
    }
1602
1603
    /**
1604
     * Exchanges all keys with their associated values in an array.
1605
     *
1606
     * @return static
1607
     *                <p>(Immutable)</p>
1608
     */
1609 1
    public function flip()
1610
    {
1611 1
        return static::create(
1612 1
            \array_flip($this->array),
1613 1
            $this->iteratorClass,
1614 1
            false
1615
        );
1616
    }
1617
1618
    /**
1619
     * Get a value from an array (optional using dot-notation).
1620
     *
1621
     * @param mixed $key      <p>The key to look for.</p>
1622
     * @param mixed $fallback <p>Value to fallback to.</p>
1623
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1624
     *                        class.</p>
1625
     *
1626
     * @return mixed|static
1627
     */
1628 68
    public function get($key, $fallback = null, array $array = null)
1629
    {
1630 68
        if ($array !== null) {
1631 4
            $usedArray = $array;
1632
        } else {
1633 64
            $usedArray = $this->array;
1634
        }
1635
1636 68
        if ($key === null) {
1637 1
            return static::create($usedArray, $this->iteratorClass, false);
1638
        }
1639
1640
        // php cast "bool"-index into "int"-index
1641 68
        if ((bool) $key === $key) {
1642 3
            $key = (int) $key;
1643
        }
1644
1645 68
        if (\array_key_exists($key, $usedArray) === true) {
1646 58
            if (\is_array($usedArray[$key])) {
1647 9
                return static::create($usedArray[$key], $this->iteratorClass, false);
1648
            }
1649
1650 51
            return $usedArray[$key];
1651
        }
1652
1653
        // Crawl through array, get key according to object or not
1654 22
        foreach (\explode($this->pathSeparator, (string) $key) as $segment) {
1655 22
            if (!isset($usedArray[$segment])) {
1656 21
                return $fallback instanceof \Closure ? $fallback() : $fallback;
1657
            }
1658
1659 6
            $usedArray = $usedArray[$segment];
1660
        }
1661
1662 6
        if (\is_array($usedArray)) {
1663 1
            return static::create($usedArray, $this->iteratorClass, false);
1664
        }
1665
1666 6
        return $usedArray;
1667
    }
1668
1669
    /**
1670
     * Get the current array from the "Arrayy"-object.
1671
     *
1672
     * @return array
1673
     */
1674 581
    public function getArray(): array
1675
    {
1676
        // init
1677 581
        $array = [];
1678
1679 581
        foreach ($this->array as $key => &$value) {
1680 478
            if ($value instanceof self) {
1681
                $array[$key] = $value->getArray();
1682
            } else {
1683 478
                $array[$key] = $value;
1684
            }
1685
        }
1686
1687 581
        return $array;
1688
    }
1689
1690
    /**
1691
     * Returns the values from a single column of the input array, identified by
1692
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1693
     *
1694
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1695
     * array by the values from the $indexKey column in the input array.
1696
     *
1697
     * @param mixed $columnKey
1698
     * @param mixed $indexKey
1699
     *
1700
     * @return static
1701
     *                <p>(Immutable)</p>
1702
     */
1703 1
    public function getColumn($columnKey = null, $indexKey = null)
1704
    {
1705 1
        return static::create(
1706 1
            \array_column($this->array, $columnKey, $indexKey),
1707 1
            $this->iteratorClass,
1708 1
            false
1709
        );
1710
    }
1711
1712
    /**
1713
     * Get the current array from the "Arrayy"-object as generator.
1714
     *
1715
     * @return \Generator
1716
     */
1717 125
    public function getGenerator(): \Generator
1718
    {
1719 125
        yield from $this->array;
1720 93
    }
1721
1722
    /**
1723
     * alias: for "Arrayy->keys()"
1724
     *
1725
     * @see Arrayy::keys()
1726
     *
1727
     * @return static
1728
     *                <p>(Immutable)</p>
1729
     */
1730 1
    public function getKeys()
1731
    {
1732 1
        return $this->keys();
1733
    }
1734
1735
    /**
1736
     * Get the current array from the "Arrayy"-object as object.
1737
     *
1738
     * @return \stdClass
1739
     */
1740 4
    public function getObject(): \stdClass
1741
    {
1742 4
        return self::arrayToObject($this->getArray());
1743
    }
1744
1745
    /**
1746
     * alias: for "Arrayy->randomImmutable()"
1747
     *
1748
     * @see Arrayy::randomImmutable()
1749
     *
1750
     * @return static
1751
     *                <p>(Immutable)</p>
1752
     */
1753 4
    public function getRandom()
1754
    {
1755 4
        return $this->randomImmutable();
1756
    }
1757
1758
    /**
1759
     * alias: for "Arrayy->randomKey()"
1760
     *
1761
     * @see Arrayy::randomKey()
1762
     *
1763
     * @return mixed
1764
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
1765
     */
1766 3
    public function getRandomKey()
1767
    {
1768 3
        return $this->randomKey();
1769
    }
1770
1771
    /**
1772
     * alias: for "Arrayy->randomKeys()"
1773
     *
1774
     * @see Arrayy::randomKeys()
1775
     *
1776
     * @param int $number
1777
     *
1778
     * @return static
1779
     *                <p>(Immutable)</p>
1780
     */
1781 8
    public function getRandomKeys(int $number)
1782
    {
1783 8
        return $this->randomKeys($number);
1784
    }
1785
1786
    /**
1787
     * alias: for "Arrayy->randomValue()"
1788
     *
1789
     * @see Arrayy::randomValue()
1790
     *
1791
     * @return mixed
1792
     *               <p>Get a random value or null if there wasn't a value.</p>
1793
     */
1794 3
    public function getRandomValue()
1795
    {
1796 3
        return $this->randomValue();
1797
    }
1798
1799
    /**
1800
     * alias: for "Arrayy->randomValues()"
1801
     *
1802
     * @see Arrayy::randomValues()
1803
     *
1804
     * @param int $number
1805
     *
1806
     * @return static
1807
     *                <p>(Immutable)</p>
1808
     */
1809 6
    public function getRandomValues(int $number)
1810
    {
1811 6
        return $this->randomValues($number);
1812
    }
1813
1814
    /**
1815
     * Group values from a array according to the results of a closure.
1816
     *
1817
     * @param \callable $grouper  <p>A callable function name.</p>
1818
     * @param bool      $saveKeys
1819
     *
1820
     * @return static
1821
     *                <p>(Immutable)</p>
1822
     */
1823 4
    public function group($grouper, bool $saveKeys = false)
1824
    {
1825
        // init
1826 4
        $result = [];
1827
1828
        // Iterate over values, group by property/results from closure.
1829 4
        foreach ($this->getGenerator() as $key => $value) {
1830 4
            $groupKey = \is_callable($grouper) ? $grouper($value, $key) : $this->get($grouper, null, $this->array);
1831 4
            $newValue = $this->get($groupKey, null, $result);
1832
1833 4
            if ($groupKey instanceof self) {
1834
                $groupKey = $groupKey->getArray();
1835
            }
1836
1837 4
            if ($newValue instanceof self) {
1838 4
                $newValue = $newValue->getArray();
1839
            }
1840
1841
            // Add to results.
1842 4
            if ($groupKey !== null) {
1843 3
                if ($saveKeys) {
1844 2
                    $result[$groupKey] = $newValue;
1845 2
                    $result[$groupKey][$key] = $value;
1846
                } else {
1847 1
                    $result[$groupKey] = $newValue;
1848 4
                    $result[$groupKey][] = $value;
1849
                }
1850
            }
1851
        }
1852
1853 4
        return static::create($result, $this->iteratorClass, false);
1854
    }
1855
1856
    /**
1857
     * Check if an array has a given key.
1858
     *
1859
     * @param mixed $key
1860
     *
1861
     * @return bool
1862
     */
1863 23
    public function has($key): bool
1864
    {
1865 23
        static $UN_FOUND = null;
1866
1867 23
        if ($UN_FOUND === null) {
1868
            // Generate unique string to use as marker.
1869 1
            $UN_FOUND = \uniqid('arrayy', true);
1870
        }
1871
1872 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
1873
    }
1874
1875
    /**
1876
     * Implodes the values of this array.
1877
     *
1878
     * @param string $glue
1879
     *
1880
     * @return string
1881
     */
1882 27
    public function implode(string $glue = ''): string
1883
    {
1884 27
        return $this->implode_recursive($glue, $this->array, false);
1885
    }
1886
1887
    /**
1888
     * Implodes the keys of this array.
1889
     *
1890
     * @param string $glue
1891
     *
1892
     * @return string
1893
     */
1894 8
    public function implodeKeys(string $glue = ''): string
1895
    {
1896 8
        return $this->implode_recursive($glue, $this->array, true);
1897
    }
1898
1899
    /**
1900
     * Given a list and an iterate-function that returns
1901
     * a key for each element in the list (or a property name),
1902
     * returns an object with an index of each item.
1903
     *
1904
     * @param mixed $key
1905
     *
1906
     * @return static
1907
     *                <p>(Immutable)</p>
1908
     */
1909 4
    public function indexBy($key)
1910
    {
1911
        // init
1912 4
        $results = [];
1913
1914 4
        foreach ($this->getGenerator() as $a) {
1915 4
            if (\array_key_exists($key, $a) === true) {
1916 4
                $results[$a[$key]] = $a;
1917
            }
1918
        }
1919
1920 4
        return static::create($results, $this->iteratorClass, false);
1921
    }
1922
1923
    /**
1924
     * alias: for "Arrayy->searchIndex()"
1925
     *
1926
     * @see Arrayy::searchIndex()
1927
     *
1928
     * @param mixed $value <p>The value to search for.</p>
1929
     *
1930
     * @return mixed
1931
     */
1932 4
    public function indexOf($value)
1933
    {
1934 4
        return $this->searchIndex($value);
1935
    }
1936
1937
    /**
1938
     * Get everything but the last..$to items.
1939
     *
1940
     * @param int $to
1941
     *
1942
     * @return static
1943
     *                <p>(Immutable)</p>
1944
     */
1945 12
    public function initial(int $to = 1)
1946
    {
1947 12
        return $this->firstsImmutable(\count($this->array, \COUNT_NORMAL) - $to);
1948
    }
1949
1950
    /**
1951
     * Return an array with all elements found in input array.
1952
     *
1953
     * @param array $search
1954
     *
1955
     * @return static
1956
     *                <p>(Immutable)</p>
1957
     */
1958 2
    public function intersection(array $search)
1959
    {
1960 2
        return static::create(
1961 2
            \array_values(\array_intersect($this->array, $search)),
1962 2
            $this->iteratorClass,
1963 2
            false
1964
        );
1965
    }
1966
1967
    /**
1968
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
1969
     *
1970
     * @param array $search
1971
     *
1972
     * @return bool
1973
     */
1974 1
    public function intersects(array $search): bool
1975
    {
1976 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
1977
    }
1978
1979
    /**
1980
     * Invoke a function on all of an array's values.
1981
     *
1982
     * @param mixed $callable
1983
     * @param mixed $arguments
1984
     *
1985
     * @return static
1986
     *                <p>(Immutable)</p>
1987
     */
1988 1
    public function invoke($callable, $arguments = [])
1989
    {
1990
        // If one argument given for each iteration, create an array for it.
1991 1
        if (!\is_array($arguments)) {
1992 1
            $arguments = StaticArrayy::repeat(
1993 1
                $arguments,
1994 1
                \count($this->array, \COUNT_NORMAL)
1995 1
            )->getArray();
1996
        }
1997
1998
        // If the callable has arguments, pass them.
1999 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...
2000 1
            $array = \array_map($callable, $this->array, $arguments);
2001
        } else {
2002 1
            $array = \array_map($callable, $this->array);
2003
        }
2004
2005 1
        return static::create($array, $this->iteratorClass, false);
2006
    }
2007
2008
    /**
2009
     * Check whether array is associative or not.
2010
     *
2011
     * @param bool $recursive
2012
     *
2013
     * @return bool
2014
     *              <p>Returns true if associative, false otherwise.</p>
2015
     */
2016 15
    public function isAssoc(bool $recursive = false): bool
2017
    {
2018 15
        if ($this->isEmpty()) {
2019 3
            return false;
2020
        }
2021
2022 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2023 13
            if (!\is_string($key)) {
2024 13
                return false;
2025
            }
2026
        }
2027
2028 3
        return true;
2029
    }
2030
2031
    /**
2032
     * Check whether the array is empty or not.
2033
     *
2034
     * @return bool
2035
     *              <p>Returns true if empty, false otherwise.</p>
2036
     */
2037 92
    public function isEmpty(): bool
2038
    {
2039 92
        return !$this->array;
2040
    }
2041
2042
    /**
2043
     * Check if the current array is equal to the given "$array" or not.
2044
     *
2045
     * @param array $array
2046
     *
2047
     * @return bool
2048
     */
2049
    public function isEqual(array $array): bool
2050
    {
2051
        return $this->array === $array;
2052
    }
2053
2054
    /**
2055
     * Check if the current array is a multi-array.
2056
     *
2057
     * @return bool
2058
     */
2059 14
    public function isMultiArray(): bool
2060
    {
2061
        return !(
2062 14
            \count($this->array, \COUNT_NORMAL)
2063
            ===
2064 14
            \count($this->array, \COUNT_RECURSIVE)
2065
        );
2066
    }
2067
2068
    /**
2069
     * Check whether array is numeric or not.
2070
     *
2071
     * @return bool
2072
     *              <p>Returns true if numeric, false otherwise.</p>
2073
     */
2074 5
    public function isNumeric(): bool
2075
    {
2076 5
        if ($this->isEmpty()) {
2077 2
            return false;
2078
        }
2079
2080 4
        foreach ($this->keys()->getGenerator() as $key) {
2081 4
            if (!\is_int($key)) {
2082 4
                return false;
2083
            }
2084
        }
2085
2086 2
        return true;
2087
    }
2088
2089
    /**
2090
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2091
     *
2092
     * @param bool $recursive
2093
     *
2094
     * @return bool
2095
     */
2096 1
    public function isSequential(bool $recursive = false): bool
2097
    {
2098
2099
        // recursive
2100
2101 1
        if ($recursive === true) {
2102
            return $this->array_keys_recursive($this->array)
2103
                   ===
2104
                   \range(0, \count($this->array, \COUNT_RECURSIVE) - 1);
2105
        }
2106
2107
        // non recursive
2108
2109 1
        return \array_keys($this->array)
2110
               ===
2111 1
               \range(0, \count($this->array, \COUNT_NORMAL) - 1);
2112
    }
2113
2114
    /**
2115
     * @return array
2116
     */
2117
    public function jsonSerialize(): array
2118
    {
2119
        return $this->getArray();
2120
    }
2121
2122
    /**
2123
     * Get all keys from the current array.
2124
     *
2125
     * @param bool  $recursive    [optional] <p>
2126
     *                            Get all keys, also from all sub-arrays from an multi-dimensional array.
2127
     *                            </p>
2128
     * @param mixed $search_value [optional] <p>
2129
     *                            If specified, then only keys containing these values are returned.
2130
     *                            </p>
2131
     * @param bool  $strict       [optional] <p>
2132
     *                            Determines if strict comparison (===) should be used during the search.
2133
     *                            </p>
2134
     *
2135
     * @return static
2136
     *                <p>(Immutable) An array of all the keys in input.</p>
2137
     */
2138 26
    public function keys(bool $recursive = false, $search_value = null, bool $strict = true)
2139
    {
2140
2141
        // recursive
2142
2143 26
        if ($recursive === true) {
2144 3
            if ($search_value === null) {
2145 3
                $array = $this->array_keys_recursive($this->array);
2146
            } else {
2147
                $array = $this->array_keys_recursive($this->array, $search_value, $strict);
2148
            }
2149
2150 3
            return static::create($array, $this->iteratorClass, false);
2151
        }
2152
2153
        // non recursive
2154
2155 25
        if ($search_value === null) {
2156 25
            $array = \array_keys($this->array);
2157
        } else {
2158
            $array = \array_keys($this->array, $search_value, $strict);
2159
        }
2160
2161 25
        return static::create($array, $this->iteratorClass, false);
2162
    }
2163
2164
    /**
2165
     * Sort an array by key in reverse order.
2166
     *
2167
     * @param int $sort_flags [optional] <p>
2168
     *                        You may modify the behavior of the sort using the optional
2169
     *                        parameter sort_flags, for details
2170
     *                        see sort.
2171
     *                        </p>
2172
     *
2173
     * @return static
2174
     *                <p>(Mutable) Return this Arrayy object.</p>
2175
     */
2176 4
    public function krsort(int $sort_flags = 0)
2177
    {
2178 4
        \krsort($this->array, $sort_flags);
2179
2180 4
        return $this;
2181
    }
2182
2183
    /**
2184
     * Get the last value from the current array.
2185
     *
2186
     * @return mixed
2187
     *               <p>Return null if there wasn't a element.</p>
2188
     */
2189 4
    public function last()
2190
    {
2191 4
        return $this->pop();
2192
    }
2193
2194
    /**
2195
     * Get the last value(s) from the current array.
2196
     *
2197
     * @param int|null $number
2198
     *
2199
     * @return static
2200
     *                <p>(Immutable)</p>
2201
     */
2202 13
    public function lastsImmutable(int $number = null)
2203
    {
2204 13
        if ($this->isEmpty()) {
2205 1
            return static::create([], $this->iteratorClass, false);
2206
        }
2207
2208 12
        if ($number === null) {
2209 8
            $poppedValue = $this->pop();
2210
2211 8
            if ($poppedValue === null) {
2212 1
                $poppedValue = [$poppedValue];
2213
            } else {
2214 7
                $poppedValue = (array) $poppedValue;
2215
            }
2216
2217 8
            $arrayy = static::create($poppedValue, $this->iteratorClass, false);
2218
        } else {
2219 4
            $number = (int) $number;
2220 4
            $arrayy = $this->rest(-$number);
2221
        }
2222
2223 12
        return $arrayy;
2224
    }
2225
2226
    /**
2227
     * Get the last value(s) from the current array.
2228
     *
2229
     * @param int|null $number
2230
     *
2231
     * @return static
2232
     *                <p>(Mutable)</p>
2233
     */
2234 13
    public function lastsMutable(int $number = null)
2235
    {
2236 13
        if ($this->isEmpty()) {
2237 1
            return $this;
2238
        }
2239
2240 12
        if ($number === null) {
2241 8
            $poppedValue = $this->pop();
2242
2243 8
            if ($poppedValue === null) {
2244 1
                $poppedValue = [$poppedValue];
2245
            } else {
2246 7
                $poppedValue = (array) $poppedValue;
2247
            }
2248
2249 8
            $this->array = static::create($poppedValue, $this->iteratorClass, false)->array;
2250
        } else {
2251 4
            $number = (int) $number;
2252 4
            $this->array = $this->rest(-$number)->array;
2253
        }
2254
2255 12
        return $this;
2256
    }
2257
2258
    /**
2259
     * Count the values from the current array.
2260
     *
2261
     * alias: for "Arrayy->count()"
2262
     *
2263
     * @see Arrayy::count()
2264
     *
2265
     * @param int $mode
2266
     *
2267
     * @return int
2268
     */
2269 20
    public function length(int $mode = \COUNT_NORMAL): int
2270
    {
2271 20
        return $this->count($mode);
2272
    }
2273
2274
    /**
2275
     * Apply the given function to the every element of the array,
2276
     * collecting the results.
2277
     *
2278
     * @param \callable $callable
2279
     *
2280
     * @return static
2281
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2282
     */
2283 4
    public function map($callable)
2284
    {
2285 4
        return static::create(
2286 4
            \array_map($callable, $this->array),
2287 4
            $this->iteratorClass,
2288 4
            false
2289
        );
2290
    }
2291
2292
    /**
2293
     * Check if all items in current array match a truth test.
2294
     *
2295
     * @param \Closure $closure
2296
     *
2297
     * @return bool
2298
     */
2299 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...
2300
    {
2301 15
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2302 2
            return false;
2303
        }
2304
2305 13
        foreach ($this->getGenerator() as $key => $value) {
2306 13
            $value = $closure($value, $key);
2307
2308 13
            if ($value === false) {
2309 13
                return false;
2310
            }
2311
        }
2312
2313 7
        return true;
2314
    }
2315
2316
    /**
2317
     * Check if any item in the current array matches a truth test.
2318
     *
2319
     * @param \Closure $closure
2320
     *
2321
     * @return bool
2322
     */
2323 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...
2324
    {
2325 14
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2326 2
            return false;
2327
        }
2328
2329 12
        foreach ($this->getGenerator() as $key => $value) {
2330 12
            $value = $closure($value, $key);
2331
2332 12
            if ($value === true) {
2333 12
                return true;
2334
            }
2335
        }
2336
2337 4
        return false;
2338
    }
2339
2340
    /**
2341
     * Get the max value from an array.
2342
     *
2343
     * @return mixed
2344
     */
2345 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...
2346
    {
2347 10
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2348 1
            return false;
2349
        }
2350
2351 9
        return \max($this->array);
2352
    }
2353
2354
    /**
2355
     * Merge the new $array into the current array.
2356
     *
2357
     * - keep key,value from the current array, also if the index is in the new $array
2358
     *
2359
     * @param array $array
2360
     * @param bool  $recursive
2361
     *
2362
     * @return static
2363
     *                <p>(Immutable)</p>
2364
     */
2365 25 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false)
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...
2366
    {
2367 25
        if ($recursive === true) {
2368 4
            $result = \array_replace_recursive($this->array, $array);
2369
        } else {
2370 21
            $result = \array_replace($this->array, $array);
2371
        }
2372
2373 25
        return static::create($result, $this->iteratorClass, false);
2374
    }
2375
2376
    /**
2377
     * Merge the new $array into the current array.
2378
     *
2379
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2380
     * - create new indexes
2381
     *
2382
     * @param array $array
2383
     * @param bool  $recursive
2384
     *
2385
     * @return static
2386
     *                <p>(Immutable)</p>
2387
     */
2388 16 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false)
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...
2389
    {
2390 16
        if ($recursive === true) {
2391 4
            $result = \array_merge_recursive($this->array, $array);
2392
        } else {
2393 12
            $result = \array_merge($this->array, $array);
2394
        }
2395
2396 16
        return static::create($result, $this->iteratorClass, false);
2397
    }
2398
2399
    /**
2400
     * Merge the the current array into the $array.
2401
     *
2402
     * - use key,value from the new $array, also if the index is in the current array
2403
     *
2404
     * @param array $array
2405
     * @param bool  $recursive
2406
     *
2407
     * @return static
2408
     *                <p>(Immutable)</p>
2409
     */
2410 16 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false)
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...
2411
    {
2412 16
        if ($recursive === true) {
2413 4
            $result = \array_replace_recursive($array, $this->array);
2414
        } else {
2415 12
            $result = \array_replace($array, $this->array);
2416
        }
2417
2418 16
        return static::create($result, $this->iteratorClass, false);
2419
    }
2420
2421
    /**
2422
     * Merge the current array into the new $array.
2423
     *
2424
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2425
     * - create new indexes
2426
     *
2427
     * @param array $array
2428
     * @param bool  $recursive
2429
     *
2430
     * @return static
2431
     *                <p>(Immutable)</p>
2432
     */
2433 17 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false)
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...
2434
    {
2435 17
        if ($recursive === true) {
2436 4
            $result = \array_merge_recursive($array, $this->array);
2437
        } else {
2438 13
            $result = \array_merge($array, $this->array);
2439
        }
2440
2441 17
        return static::create($result, $this->iteratorClass, false);
2442
    }
2443
2444
    /**
2445
     * @return ArrayyMeta|static
2446
     */
2447 8
    public static function meta()
2448
    {
2449 8
        return (new ArrayyMeta())->getMetaObject(static::class);
2450
    }
2451
2452
    /**
2453
     * Get the min value from an array.
2454
     *
2455
     * @return mixed
2456
     */
2457 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...
2458
    {
2459 10
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2460 1
            return false;
2461
        }
2462
2463 9
        return \min($this->array);
2464
    }
2465
2466
    /**
2467
     * Move an array element to a new index.
2468
     *
2469
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2470
     *
2471
     * @param int|string $from
2472
     * @param int|string $to
2473
     *
2474
     * @return static
2475
     *                <p>(Immutable)</p>
2476
     */
2477 1
    public function moveElement($from, $to)
2478
    {
2479 1
        $array = $this->array;
2480
2481 1
        if (\is_int($from)) {
2482 1
            $tmp = \array_splice($array, $from, 1);
2483 1
            \array_splice($array, $to, 0, $tmp);
2484 1
            $output = $array;
2485 1
        } elseif (\is_string($from)) {
2486 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2487 1
            $itemToMove = $array[$from];
2488 1
            \array_splice($array, $indexToMove, 1);
2489 1
            $i = 0;
2490 1
            $output = [];
2491 1
            foreach ($array as $key => $item) {
2492 1
                if ($i === $to) {
2493 1
                    $output[$from] = $itemToMove;
2494
                }
2495 1
                $output[$key] = $item;
2496 1
                ++$i;
2497
            }
2498
        } else {
2499
            $output = [];
2500
        }
2501
2502 1
        return static::create($output, $this->iteratorClass, false);
2503
    }
2504
2505
    /**
2506
     * Get a subset of the items from the given array.
2507
     *
2508
     * @param mixed[] $keys
2509
     *
2510
     * @return static
2511
     *                <p>(Immutable)</p>
2512
     */
2513
    public function only(array $keys)
2514
    {
2515
        $array = $this->array;
2516
2517
        return static::create(
2518
            \array_intersect_key($array, \array_flip($keys)),
2519
            $this->iteratorClass,
2520
            false
2521
        );
2522
    }
2523
2524
    /**
2525
     * Pad array to the specified size with a given value.
2526
     *
2527
     * @param int   $size  <p>Size of the result array.</p>
2528
     * @param mixed $value <p>Empty value by default.</p>
2529
     *
2530
     * @return static
2531
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
2532
     */
2533 4
    public function pad(int $size, $value)
2534
    {
2535 4
        return static::create(
2536 4
            \array_pad($this->array, $size, $value),
2537 4
            $this->iteratorClass,
2538 4
            false
2539
        );
2540
    }
2541
2542
    /**
2543
     * Pop a specified value off the end of the current array.
2544
     *
2545
     * @return mixed
2546
     *               <p>(Mutable) The popped element from the current array.</p>
2547
     */
2548 16
    public function pop()
2549
    {
2550 16
        return \array_pop($this->array);
2551
    }
2552
2553
    /**
2554
     * Prepend a (key) + value to the current array.
2555
     *
2556
     * @param mixed $value
2557
     * @param mixed $key
2558
     *
2559
     * @return static
2560
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
2561
     */
2562 8
    public function prepend($value, $key = null)
2563
    {
2564 8
        if ($key === null) {
2565 8
            \array_unshift($this->array, $value);
2566
        } else {
2567
            /** @noinspection AdditionOperationOnArraysInspection */
2568 1
            $this->array = [$key => $value] + $this->array;
2569
        }
2570
2571 8
        return $this;
2572
    }
2573
2574
    /**
2575
     * Add a suffix to each key.
2576
     *
2577
     * @param mixed $suffix
2578
     *
2579
     * @return static
2580
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
2581
     */
2582 10 View Code Duplication
    public function prependToEachKey($suffix)
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...
2583
    {
2584
        // init
2585 10
        $result = [];
2586
2587 10
        foreach ($this->getGenerator() as $key => $item) {
2588 9
            if ($item instanceof self) {
2589
                $result[$key] = $item->prependToEachKey($suffix);
2590 9
            } elseif (\is_array($item)) {
2591
                $result[$key] = self::create($item, $this->iteratorClass, false)->prependToEachKey($suffix)->toArray();
2592
            } else {
2593 9
                $result[$key . $suffix] = $item;
2594
            }
2595
        }
2596
2597 10
        return self::create($result, $this->iteratorClass, false);
2598
    }
2599
2600
    /**
2601
     * Add a suffix to each value.
2602
     *
2603
     * @param mixed $suffix
2604
     *
2605
     * @return static
2606
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
2607
     */
2608 10 View Code Duplication
    public function prependToEachValue($suffix)
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...
2609
    {
2610
        // init
2611 10
        $result = [];
2612
2613 10
        foreach ($this->getGenerator() as $key => $item) {
2614 9
            if ($item instanceof self) {
2615
                $result[$key] = $item->prependToEachValue($suffix);
2616 9
            } elseif (\is_array($item)) {
2617
                $result[$key] = self::create($item, $this->iteratorClass, false)
2618
                    ->prependToEachValue($suffix)
2619
                    ->toArray();
2620 9
            } elseif (\is_object($item)) {
2621 1
                $result[$key] = $item;
2622
            } else {
2623 9
                $result[$key] = $item . $suffix;
2624
            }
2625
        }
2626
2627 10
        return self::create($result, $this->iteratorClass, false);
2628
    }
2629
2630
    /**
2631
     * Push one or more values onto the end of array at once.
2632
     *
2633
     * @return static
2634
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
2635
     */
2636 4 View Code Duplication
    public function push(/* variadic arguments allowed */)
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...
2637
    {
2638 4
        if (\func_num_args()) {
2639 4
            $args = \array_merge([&$this->array], \func_get_args());
2640 4
            \array_push(...$args);
2641
        }
2642
2643 4
        return $this;
2644
    }
2645
2646
    /**
2647
     * Get a random value from the current array.
2648
     *
2649
     * @param int|null $number <p>How many values you will take?</p>
2650
     *
2651
     * @return static
2652
     *                <p>(Immutable)</p>
2653
     */
2654 18
    public function randomImmutable(int $number = null)
2655
    {
2656 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...
2657 1
            return static::create([], $this->iteratorClass, false);
2658
        }
2659
2660 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...
2661
            /** @noinspection NonSecureArrayRandUsageInspection */
2662 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
2663
2664 13
            return static::create($arrayRandValue, $this->iteratorClass, false);
2665
        }
2666
2667 5
        $arrayTmp = $this->array;
2668
        /** @noinspection NonSecureShuffleUsageInspection */
2669 5
        \shuffle($arrayTmp);
2670
2671 5
        return static::create($arrayTmp, $this->iteratorClass, false)->firstsImmutable($number);
2672
    }
2673
2674
    /**
2675
     * Pick a random key/index from the keys of this array.
2676
     *
2677
     * @throws \RangeException If array is empty
2678
     *
2679
     * @return mixed
2680
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2681
     */
2682 4
    public function randomKey()
2683
    {
2684 4
        $result = $this->randomKeys(1);
2685
2686 4
        if (!isset($result[0])) {
2687
            $result[0] = null;
2688
        }
2689
2690 4
        return $result[0];
2691
    }
2692
2693
    /**
2694
     * Pick a given number of random keys/indexes out of this array.
2695
     *
2696
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
2697
     *
2698
     * @throws \RangeException If array is empty
2699
     *
2700
     * @return static
2701
     *                <p>(Immutable)</p>
2702
     */
2703 13
    public function randomKeys(int $number)
2704
    {
2705 13
        $count = \count($this->array, \COUNT_NORMAL);
2706
2707 13
        if ($number === 0 || $number > $count) {
2708 2
            throw new \RangeException(
2709 2
                \sprintf(
2710 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
2711 2
                    $number,
2712 2
                    $count
2713
                )
2714
            );
2715
        }
2716
2717 11
        $result = (array) \array_rand($this->array, $number);
2718
2719 11
        return static::create($result, $this->iteratorClass, false);
2720
    }
2721
2722
    /**
2723
     * Get a random value from the current array.
2724
     *
2725
     * @param int|null $number <p>How many values you will take?</p>
2726
     *
2727
     * @return static
2728
     *                <p>(Mutable)</p>
2729
     */
2730 17
    public function randomMutable(int $number = null)
2731
    {
2732 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...
2733
            return static::create([], $this->iteratorClass, false);
2734
        }
2735
2736 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...
2737
            /** @noinspection NonSecureArrayRandUsageInspection */
2738 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
2739 7
            $this->array = $arrayRandValue;
2740
2741 7
            return $this;
2742
        }
2743
2744
        /** @noinspection NonSecureShuffleUsageInspection */
2745 11
        \shuffle($this->array);
2746
2747 11
        return $this->firstsMutable($number);
2748
    }
2749
2750
    /**
2751
     * Pick a random value from the values of this array.
2752
     *
2753
     * @return mixed
2754
     *               <p>Get a random value or null if there wasn't a value.</p>
2755
     */
2756 4
    public function randomValue()
2757
    {
2758 4
        $result = $this->randomImmutable();
2759
2760 4
        if (!isset($result[0])) {
2761
            $result[0] = null;
2762
        }
2763
2764 4
        return $result[0];
2765
    }
2766
2767
    /**
2768
     * Pick a given number of random values out of this array.
2769
     *
2770
     * @param int $number
2771
     *
2772
     * @return static
2773
     *                <p>(Mutable)</p>
2774
     */
2775 7
    public function randomValues(int $number)
2776
    {
2777 7
        return $this->randomMutable($number);
2778
    }
2779
2780
    /**
2781
     * Get a random value from an array, with the ability to skew the results.
2782
     *
2783
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
2784
     *
2785
     * @param array    $array
2786
     * @param int|null $number <p>How many values you will take?</p>
2787
     *
2788
     * @return static
2789
     *                <p>(Immutable)</p>
2790
     */
2791 9
    public function randomWeighted(array $array, int $number = null)
2792
    {
2793
        // init
2794 9
        $options = [];
2795
2796 9
        foreach ($array as $option => $weight) {
2797 9
            if ($this->searchIndex($option) !== false) {
2798 9
                for ($i = 0; $i < $weight; ++$i) {
2799 1
                    $options[] = $option;
2800
                }
2801
            }
2802
        }
2803
2804 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
2805
    }
2806
2807
    /**
2808
     * Reduce the current array via callable e.g. anonymous-function.
2809
     *
2810
     * @param \callable $callable
2811
     * @param array     $init
2812
     *
2813
     * @return static
2814
     *                <p>(Immutable)</p>
2815
     */
2816 4
    public function reduce($callable, array $init = [])
2817
    {
2818 4
        $result = \array_reduce($this->array, $callable, $init);
2819
2820 4
        if ($result === null) {
2821
            $this->array = [];
2822
        } else {
2823 4
            $this->array = (array) $result;
2824
        }
2825
2826 4
        return static::create($this->array, $this->iteratorClass, false);
2827
    }
2828
2829
    /**
2830
     * Create a numerically re-indexed Arrayy object.
2831
     *
2832
     * @return static
2833
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
2834
     */
2835 9
    public function reindex()
2836
    {
2837 9
        $this->array = \array_values($this->array);
2838
2839 9
        return $this;
2840
    }
2841
2842
    /**
2843
     * Return all items that fail the truth test.
2844
     *
2845
     * @param \Closure $closure
2846
     *
2847
     * @return static
2848
     *                <p>(Immutable)</p>
2849
     */
2850 1 View Code Duplication
    public function reject(\Closure $closure)
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...
2851
    {
2852 1
        $filtered = [];
2853
2854 1
        foreach ($this->getGenerator() as $key => $value) {
2855 1
            if (!$closure($value, $key)) {
2856 1
                $filtered[$key] = $value;
2857
            }
2858
        }
2859
2860 1
        return static::create($filtered, $this->iteratorClass, false);
2861
    }
2862
2863
    /**
2864
     * Remove a value from the current array (optional using dot-notation).
2865
     *
2866
     * @param mixed $key
2867
     *
2868
     * @return static
2869
     *                <p>(Immutable)</p>
2870
     */
2871 18
    public function remove($key)
2872
    {
2873
        // recursive call
2874 18
        if (\is_array($key)) {
2875
            foreach ($key as $k) {
2876
                $this->internalRemove($k);
2877
            }
2878
2879
            return static::create($this->array, $this->iteratorClass, false);
2880
        }
2881
2882 18
        $this->internalRemove($key);
2883
2884 18
        return static::create($this->array, $this->iteratorClass, false);
2885
    }
2886
2887
    /**
2888
     * Remove the first value from the current array.
2889
     *
2890
     * @return static
2891
     *                <p>(Immutable)</p>
2892
     */
2893 7
    public function removeFirst()
2894
    {
2895 7
        $tmpArray = $this->array;
2896 7
        \array_shift($tmpArray);
2897
2898 7
        return static::create($tmpArray, $this->iteratorClass, false);
2899
    }
2900
2901
    /**
2902
     * Remove the last value from the current array.
2903
     *
2904
     * @return static
2905
     *                <p>(Immutable)</p>
2906
     */
2907 7
    public function removeLast()
2908
    {
2909 7
        $tmpArray = $this->array;
2910 7
        \array_pop($tmpArray);
2911
2912 7
        return static::create($tmpArray, $this->iteratorClass, false);
2913
    }
2914
2915
    /**
2916
     * Removes a particular value from an array (numeric or associative).
2917
     *
2918
     * @param mixed $value
2919
     *
2920
     * @return static
2921
     *                <p>(Immutable)</p>
2922
     */
2923 7
    public function removeValue($value)
2924
    {
2925 7
        $isNumericArray = true;
2926 7
        foreach ($this->getGenerator() as $key => $item) {
2927 6
            if ($item === $value) {
2928 6
                if (!\is_int($key)) {
2929
                    $isNumericArray = false;
2930
                }
2931 6
                unset($this->array[$key]);
2932
            }
2933
        }
2934
2935 7
        if ($isNumericArray) {
2936 7
            $this->array = \array_values($this->array);
2937
        }
2938
2939 7
        return static::create($this->array, $this->iteratorClass, false);
2940
    }
2941
2942
    /**
2943
     * Generate array of repeated arrays.
2944
     *
2945
     * @param int $times <p>How many times has to be repeated.</p>
2946
     *
2947
     * @return static
2948
     *                <p>(Immutable)</p>
2949
     */
2950 1
    public function repeat($times): self
2951
    {
2952 1
        if ($times === 0) {
2953 1
            return new static();
2954
        }
2955
2956 1
        return static::create(
2957 1
            \array_fill(0, (int) $times, $this->array),
2958 1
            $this->iteratorClass,
2959 1
            false
2960
        );
2961
    }
2962
2963
    /**
2964
     * Replace a key with a new key/value pair.
2965
     *
2966
     * @param mixed $replace
2967
     * @param mixed $key
2968
     * @param mixed $value
2969
     *
2970
     * @return static
2971
     *                <p>(Immutable)</p>
2972
     */
2973 2
    public function replace($replace, $key, $value)
2974
    {
2975 2
        $that = $this->remove($replace);
2976
2977 2
        return $that->set($key, $value);
2978
    }
2979
2980
    /**
2981
     * Create an array using the current array as values and the other array as keys.
2982
     *
2983
     * @param array $keys <p>An array of keys.</p>
2984
     *
2985
     * @return static
2986
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
2987
     */
2988 2
    public function replaceAllKeys(array $keys)
2989
    {
2990 2
        return static::create(
2991 2
            \array_combine($keys, $this->array),
2992 2
            $this->iteratorClass,
2993 2
            false
2994
        );
2995
    }
2996
2997
    /**
2998
     * Create an array using the current array as keys and the other array as values.
2999
     *
3000
     * @param array $array <p>An array o values.</p>
3001
     *
3002
     * @return static
3003
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3004
     */
3005 2
    public function replaceAllValues(array $array)
3006
    {
3007 2
        return static::create(
3008 2
            \array_combine($this->array, $array),
3009 2
            $this->iteratorClass,
3010 2
            false
3011
        );
3012
    }
3013
3014
    /**
3015
     * Replace the keys in an array with another set.
3016
     *
3017
     * @param array $keys <p>An array of keys matching the array's size</p>
3018
     *
3019
     * @return static
3020
     *                <p>(Immutable)</p>
3021
     */
3022 1
    public function replaceKeys(array $keys)
3023
    {
3024 1
        $values = \array_values($this->array);
3025 1
        $result = \array_combine($keys, $values);
3026
3027 1
        return static::create($result, $this->iteratorClass, false);
3028
    }
3029
3030
    /**
3031
     * Replace the first matched value in an array.
3032
     *
3033
     * @param mixed $search      <p>The value to replace.</p>
3034
     * @param mixed $replacement <p>The value to replace.</p>
3035
     *
3036
     * @return static
3037
     *                <p>(Immutable)</p>
3038
     */
3039 3
    public function replaceOneValue($search, $replacement = '')
3040
    {
3041 3
        $array = $this->array;
3042 3
        $key = \array_search($search, $array, true);
3043
3044 3
        if ($key !== false) {
3045 3
            $array[$key] = $replacement;
3046
        }
3047
3048 3
        return static::create($array, $this->iteratorClass, false);
3049
    }
3050
3051
    /**
3052
     * Replace values in the current array.
3053
     *
3054
     * @param mixed $search      <p>The value to replace.</p>
3055
     * @param mixed $replacement <p>What to replace it with.</p>
3056
     *
3057
     * @return static
3058
     *                <p>(Immutable)</p>
3059
     */
3060 1
    public function replaceValues($search, $replacement = '')
3061
    {
3062 1
        $array = $this->each(
3063
            static function ($value) use ($search, $replacement) {
3064 1
                return \str_replace($search, $replacement, $value);
3065 1
            }
3066
        );
3067
3068 1
        return $array;
3069
    }
3070
3071
    /**
3072
     * Get the last elements from index $from until the end of this array.
3073
     *
3074
     * @param int $from
3075
     *
3076
     * @return static
3077
     *                <p>(Immutable)</p>
3078
     */
3079 15
    public function rest(int $from = 1)
3080
    {
3081 15
        $tmpArray = $this->array;
3082
3083 15
        return static::create(
3084 15
            \array_splice($tmpArray, $from),
3085 15
            $this->iteratorClass,
3086 15
            false
3087
        );
3088
    }
3089
3090
    /**
3091
     * Return the array in the reverse order.
3092
     *
3093
     * @return static
3094
     *                <p>(Mutable) Return this Arrayy object.</p>
3095
     */
3096 8
    public function reverse()
3097
    {
3098 8
        $this->array = \array_reverse($this->array);
3099
3100 8
        return $this;
3101
    }
3102
3103
    /**
3104
     * Sort an array in reverse order.
3105
     *
3106
     * @param int $sort_flags [optional] <p>
3107
     *                        You may modify the behavior of the sort using the optional
3108
     *                        parameter sort_flags, for details
3109
     *                        see sort.
3110
     *                        </p>
3111
     *
3112
     * @return static
3113
     *                <p>(Mutable) Return this Arrayy object.</p>
3114
     */
3115 4
    public function rsort(int $sort_flags = 0)
3116
    {
3117 4
        \rsort($this->array, $sort_flags);
3118
3119 4
        return $this;
3120
    }
3121
3122
    /**
3123
     * Search for the first index of the current array via $value.
3124
     *
3125
     * @param mixed $value
3126
     *
3127
     * @return float|int|string
3128
     */
3129 20
    public function searchIndex($value)
3130
    {
3131 20
        return \array_search($value, $this->array, true);
3132
    }
3133
3134
    /**
3135
     * Search for the value of the current array via $index.
3136
     *
3137
     * @param mixed $index
3138
     *
3139
     * @return static
3140
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3141
     */
3142 9
    public function searchValue($index)
3143
    {
3144
        // init
3145 9
        $return = [];
3146
3147 9
        if ($this->isEmpty()) {
3148
            return static::create([], $this->iteratorClass, false);
3149
        }
3150
3151
        // php cast "bool"-index into "int"-index
3152 9
        if ((bool) $index === $index) {
3153 1
            $index = (int) $index;
3154
        }
3155
3156 9
        if (\array_key_exists($index, $this->array) === true) {
3157 7
            $return = [$this->array[$index]];
3158
        }
3159
3160 9
        return static::create($return, $this->iteratorClass, false);
3161
    }
3162
3163
    /**
3164
     * Set a value for the current array (optional using dot-notation).
3165
     *
3166
     * @param string $key   <p>The key to set.</p>
3167
     * @param mixed  $value <p>Its value.</p>
3168
     *
3169
     * @return static
3170
     *                <p>(Mutable)</p>
3171
     */
3172 17
    public function set($key, $value)
3173
    {
3174 17
        $this->internalSet($key, $value);
3175
3176 17
        return $this;
3177
    }
3178
3179
    /**
3180
     * Get a value from a array and set it if it was not.
3181
     *
3182
     * WARNING: this method only set the value, if the $key is not already set
3183
     *
3184
     * @param mixed $key      <p>The key</p>
3185
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3186
     *
3187
     * @return mixed
3188
     *               <p>(Mutable)</p>
3189
     */
3190 11
    public function setAndGet($key, $fallback = null)
3191
    {
3192
        // If the key doesn't exist, set it.
3193 11
        if (!$this->has($key)) {
3194 4
            $this->array = $this->set($key, $fallback)->getArray();
3195
        }
3196
3197 11
        return $this->get($key);
3198
    }
3199
3200
    /**
3201
     * Shifts a specified value off the beginning of array.
3202
     *
3203
     * @return mixed
3204
     *               <p>(Mutable) A shifted element from the current array.</p>
3205
     */
3206 4
    public function shift()
3207
    {
3208 4
        return \array_shift($this->array);
3209
    }
3210
3211
    /**
3212
     * Shuffle the current array.
3213
     *
3214
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3215
     * @param array $array  [optional]
3216
     *
3217
     * @return static
3218
     *                <p>(Immutable)</p>
3219
     */
3220 1
    public function shuffle(bool $secure = false, array $array = null)
3221
    {
3222 1
        if ($array === null) {
3223 1
            $array = $this->array;
3224
        }
3225
3226 1
        if ($secure !== true) {
3227
            /** @noinspection NonSecureShuffleUsageInspection */
3228 1
            \shuffle($array);
3229
        } else {
3230 1
            $size = \count($array, \COUNT_NORMAL);
3231 1
            $keys = \array_keys($array);
3232 1
            for ($i = $size - 1; $i > 0; --$i) {
3233
                try {
3234 1
                    $r = \random_int(0, $i);
3235
                } catch (\Exception $e) {
3236
                    /** @noinspection RandomApiMigrationInspection */
3237
                    $r = \mt_rand(0, $i);
3238
                }
3239 1
                if ($r !== $i) {
3240 1
                    $temp = $array[$keys[$r]];
3241 1
                    $array[$keys[$r]] = $array[$keys[$i]];
3242 1
                    $array[$keys[$i]] = $temp;
3243
                }
3244
            }
3245
3246
            // reset indices
3247 1
            $array = \array_values($array);
3248
        }
3249
3250 1
        foreach ($array as $key => $value) {
3251
            // check if recursive is needed
3252 1
            if (\is_array($value) === true) {
3253 1
                $array[$key] = $this->shuffle($secure, $value);
3254
            }
3255
        }
3256
3257 1
        return static::create($array, $this->iteratorClass, false);
3258
    }
3259
3260
    /**
3261
     * Count the values from the current array.
3262
     *
3263
     * alias: for "Arrayy->count()"
3264
     *
3265
     * @param int $mode
3266
     *
3267
     * @return int
3268
     */
3269 20
    public function size(int $mode = \COUNT_NORMAL): int
3270
    {
3271 20
        return $this->count($mode);
3272
    }
3273
3274
    /**
3275
     * Counts all elements in an array, or something in an object.
3276
     *
3277
     * <p>
3278
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
3279
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
3280
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
3281
     * implemented and used in PHP.
3282
     * </p>
3283
     *
3284
     * @return int
3285
     *             <p>
3286
     *             The number of elements in var, which is
3287
     *             typically an array, since anything else will have one
3288
     *             element.
3289
     *             </p>
3290
     *             <p>
3291
     *             If var is not an array or an object with
3292
     *             implemented Countable interface,
3293
     *             1 will be returned.
3294
     *             There is one exception, if var is &null;,
3295
     *             0 will be returned.
3296
     *             </p>
3297
     *             <p>
3298
     *             Caution: count may return 0 for a variable that isn't set,
3299
     *             but it may also return 0 for a variable that has been initialized with an
3300
     *             empty array. Use isset to test if a variable is set.
3301
     *             </p>
3302
     */
3303 10
    public function sizeRecursive(): int
3304
    {
3305 10
        return \count($this->array, \COUNT_RECURSIVE);
3306
    }
3307
3308
    /**
3309
     * Extract a slice of the array.
3310
     *
3311
     * @param int      $offset       <p>Slice begin index.</p>
3312
     * @param int|null $length       <p>Length of the slice.</p>
3313
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
3314
     *
3315
     * @return static
3316
     *                <p>A slice of the original array with length $length.</p>
3317
     */
3318 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
3319
    {
3320 4
        return static::create(
3321 4
            \array_slice($this->array, $offset, $length, $preserveKeys),
3322 4
            $this->iteratorClass,
3323 4
            false
3324
        );
3325
    }
3326
3327
    /**
3328
     * Sort the current array and optional you can keep the keys.
3329
     *
3330
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3331
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
3332
     *                              <strong>SORT_NATURAL</strong></p>
3333
     * @param bool       $keepKeys
3334
     *
3335
     * @return static
3336
     *                <p>(Mutable) Return this Arrayy object.</p>
3337
     */
3338 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false)
3339
    {
3340 20
        return $this->sorting($this->array, $direction, $strategy, $keepKeys);
3341
    }
3342
3343
    /**
3344
     * Sort the current array by key.
3345
     *
3346
     * @see http://php.net/manual/en/function.ksort.php
3347
     * @see http://php.net/manual/en/function.krsort.php
3348
     *
3349
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3350
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3351
     *                              <strong>SORT_NATURAL</strong></p>
3352
     *
3353
     * @return static
3354
     *                <p>(Mutable) Return this Arrayy object.</p>
3355
     */
3356 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3357
    {
3358
        /** @noinspection UnusedFunctionResultInspection */
3359 18
        $this->sorterKeys($this->array, $direction, $strategy);
3360
3361 18
        return $this;
3362
    }
3363
3364
    /**
3365
     * Sort the current array by value.
3366
     *
3367
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3368
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3369
     *                              <strong>SORT_NATURAL</strong></p>
3370
     *
3371
     * @return static
3372
     *                <p>(Mutable)</p>
3373
     */
3374 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3375
    {
3376 1
        return $this->sort($direction, $strategy, true);
3377
    }
3378
3379
    /**
3380
     * Sort the current array by value.
3381
     *
3382
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3383
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3384
     *                              <strong>SORT_NATURAL</strong></p>
3385
     *
3386
     * @return static
3387
     *                <p>(Mutable)</p>
3388
     */
3389 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3390
    {
3391 1
        return $this->sort($direction, $strategy, false);
3392
    }
3393
3394
    /**
3395
     * Sort a array by value, by a closure or by a property.
3396
     *
3397
     * - If the sorter is null, the array is sorted naturally.
3398
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
3399
     *
3400
     * @param \callable|null $sorter
3401
     * @param int|string     $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3402
     * @param int            $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3403
     *                                  <strong>SORT_NATURAL</strong></p>
3404
     *
3405
     * @return static
3406
     *                <p>(Immutable)</p>
3407
     */
3408 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3409
    {
3410 1
        $array = (array) $this->array;
3411 1
        $direction = $this->getDirection($direction);
3412
3413
        // Transform all values into their results.
3414 1
        if ($sorter) {
3415 1
            $arrayy = static::create($array, $this->iteratorClass, false);
3416
3417 1
            $that = $this;
3418 1
            $results = $arrayy->each(
3419
                static function ($value) use ($sorter, $that) {
3420 1
                    return \is_callable($sorter) ? $sorter($value) : $that->get($sorter, null, $value);
3421 1
                }
3422
            );
3423
3424 1
            $results = $results->getArray();
3425
        } else {
3426 1
            $results = $array;
3427
        }
3428
3429
        // Sort by the results and replace by original values
3430 1
        \array_multisort($results, $direction, $strategy, $array);
3431
3432 1
        return static::create($array, $this->iteratorClass, false);
3433
    }
3434
3435
    /**
3436
     * Split an array in the given amount of pieces.
3437
     *
3438
     * @param int  $numberOfPieces
3439
     * @param bool $keepKeys
3440
     *
3441
     * @return static
3442
     *                <p>(Immutable)</p>
3443
     */
3444 1 View Code Duplication
    public function split(int $numberOfPieces = 2, bool $keepKeys = false)
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...
3445
    {
3446 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
3447
3448 1
        if ($arrayCount === 0) {
3449 1
            $result = [];
3450
        } else {
3451 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
3452 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
3453
        }
3454
3455 1
        return static::create($result, $this->iteratorClass, false);
3456
    }
3457
3458
    /**
3459
     * Stripe all empty items.
3460
     *
3461
     * @return static
3462
     *                <p>(Immutable)</p>
3463
     */
3464 1
    public function stripEmpty()
3465
    {
3466 1
        return $this->filter(
3467
            static function ($item) {
3468 1
                if ($item === null) {
3469 1
                    return false;
3470
                }
3471
3472 1
                return (bool) \trim((string) $item);
3473 1
            }
3474
        );
3475
    }
3476
3477
    /**
3478
     * Swap two values between positions by key.
3479
     *
3480
     * @param int|string $swapA <p>a key in the array</p>
3481
     * @param int|string $swapB <p>a key in the array</p>
3482
     *
3483
     * @return static
3484
     *                <p>(Immutable)</p>
3485
     */
3486 1
    public function swap($swapA, $swapB)
3487
    {
3488 1
        $array = $this->array;
3489
3490 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
3491
3492 1
        return static::create($array, $this->iteratorClass, false);
3493
    }
3494
3495
    /**
3496
     * alias: for "Arrayy->getArray()"
3497
     *
3498
     * @see Arrayy::getArray()
3499
     */
3500 192
    public function toArray()
3501
    {
3502 192
        return $this->getArray();
3503
    }
3504
3505
    /**
3506
     * Convert the current array to JSON.
3507
     *
3508
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
3509
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
3510
     *
3511
     * @return string
3512
     */
3513 6
    public function toJson(int $options = 0, int $depth = 512): string
3514
    {
3515
        /** @noinspection PhpComposerExtensionStubsInspection */
3516 6
        return \json_encode($this->array, $options, $depth);
3517
    }
3518
3519
    /**
3520
     * Implodes array to a string with specified separator.
3521
     *
3522
     * @param string $separator [optional] <p>The element's separator.</p>
3523
     *
3524
     * @return string
3525
     *                <p>The string representation of array, separated by ",".</p>
3526
     */
3527 19
    public function toString(string $separator = ','): string
3528
    {
3529 19
        return $this->implode($separator);
3530
    }
3531
3532
    /**
3533
     * Return a duplicate free copy of the current array.
3534
     *
3535
     * @return static
3536
     *                <p>(Mutable)</p>
3537
     */
3538 10
    public function unique()
3539
    {
3540
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
3541
3542 10
        $this->array = \array_reduce(
0 ignored issues
show
Documentation Bug introduced by
It seems like \array_reduce($this->arr...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...
3543 10
            $this->array,
3544
            static function ($resultArray, $value) {
3545 9
                if (!\in_array($value, $resultArray, true)) {
3546 9
                    $resultArray[] = $value;
3547
                }
3548
3549 9
                return $resultArray;
3550 10
            },
3551 10
            []
3552
        );
3553
3554 10 View Code Duplication
        if ($this->array === 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...
3555
            $this->array = [];
3556
        } else {
3557 10
            $this->array = (array) $this->array;
3558
        }
3559
3560 10
        return $this;
3561
    }
3562
3563
    /**
3564
     * Return a duplicate free copy of the current array. (with the old keys)
3565
     *
3566
     * @return static
3567
     *                <p>(Mutable)</p>
3568
     */
3569 11
    public function uniqueKeepIndex()
3570
    {
3571
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
3572
3573
        // init
3574 11
        $array = $this->array;
3575
3576 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...
3577 11
            \array_keys($array),
3578
            static function ($resultArray, $key) use ($array) {
3579 10
                if (!\in_array($array[$key], $resultArray, true)) {
3580 10
                    $resultArray[$key] = $array[$key];
3581
                }
3582
3583 10
                return $resultArray;
3584 11
            },
3585 11
            []
3586
        );
3587
3588 11 View Code Duplication
        if ($this->array === 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...
3589
            $this->array = [];
3590
        } else {
3591 11
            $this->array = (array) $this->array;
3592
        }
3593
3594 11
        return $this;
3595
    }
3596
3597
    /**
3598
     * alias: for "Arrayy->unique()"
3599
     *
3600
     * @see Arrayy::unique()
3601
     *
3602
     * @return static
3603
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
3604
     */
3605 10
    public function uniqueNewIndex()
3606
    {
3607 10
        return $this->unique();
3608
    }
3609
3610
    /**
3611
     * Prepends one or more values to the beginning of array at once.
3612
     *
3613
     * @return static
3614
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
3615
     */
3616 4 View Code Duplication
    public function unshift(/* variadic arguments allowed */)
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...
3617
    {
3618 4
        if (\func_num_args()) {
3619 4
            $args = \array_merge([&$this->array], \func_get_args());
3620 4
            \array_unshift(...$args);
3621
        }
3622
3623 4
        return $this;
3624
    }
3625
3626
    /**
3627
     * Get all values from a array.
3628
     *
3629
     * @return static
3630
     *                <p>(Immutable)</p>
3631
     */
3632 2
    public function values()
3633
    {
3634 2
        return static::create(
3635 2
            \array_values((array) $this->array),
3636 2
            $this->iteratorClass,
3637 2
            false
3638
        );
3639
    }
3640
3641
    /**
3642
     * Apply the given function to every element in the array, discarding the results.
3643
     *
3644
     * @param \callable $callable
3645
     * @param bool      $recursive <p>Whether array will be walked recursively or no</p>
3646
     *
3647
     * @return static
3648
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
3649
     */
3650 37
    public function walk($callable, bool $recursive = false)
3651
    {
3652 37
        if ($recursive === true) {
3653 32
            \array_walk_recursive($this->array, $callable);
3654
        } else {
3655 18
            \array_walk($this->array, $callable);
3656
        }
3657
3658 37
        return $this;
3659
    }
3660
3661
    /**
3662
     * Convert an array into a object.
3663
     *
3664
     * @param array $array PHP array
3665
     *
3666
     * @return \stdClass
3667
     */
3668 4
    protected static function arrayToObject(array $array = []): \stdClass
3669
    {
3670
        // init
3671 4
        $object = new \stdClass();
3672
3673 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
3674 1
            return $object;
3675
        }
3676
3677 3
        foreach ($array as $name => $value) {
3678 3
            if (\is_array($value)) {
3679 1
                $object->{$name} = self::arrayToObject($value);
3680
            } else {
3681 3
                $object->{$name} = $value;
3682
            }
3683
        }
3684
3685 3
        return $object;
3686
    }
3687
3688
    /**
3689
     * @param array|\Generator|null $input        <p>
3690
     *                                            An array containing keys to return.
3691
     *                                            </p>
3692
     * @param mixed                 $search_value [optional] <p>
3693
     *                                            If specified, then only keys containing these values are returned.
3694
     *                                            </p>
3695
     * @param bool                  $strict       [optional] <p>
3696
     *                                            Determines if strict comparison (===) should be used during the
3697
     *                                            search.
3698
     *                                            </p>
3699
     *
3700
     * @return array
3701
     *               <p>an array of all the keys in input</p>
3702
     */
3703 10
    protected function array_keys_recursive($input = null, $search_value = null, bool $strict = true): array
3704
    {
3705
        // init
3706 10
        $keys = [];
3707 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
3708
3709 10
        if ($input === null) {
3710
            $input = $this->getGenerator();
3711
        }
3712
3713 10
        foreach ($input as $key => $value) {
3714
            if (
3715 10
                $search_value === null
3716
                ||
3717
                (
3718
                    \is_array($search_value) === true
3719
                    &&
3720 10
                    \in_array($key, $search_value, $strict)
3721
                )
3722
            ) {
3723 10
                $keys[] = $key;
3724
            }
3725
3726
            // check if recursive is needed
3727 10
            if (\is_array($value) === true) {
3728 10
                $keysTmp[] = $this->array_keys_recursive($value);
3729
            }
3730
        }
3731
3732 10
        return \array_merge($keys, ...$keysTmp);
3733
    }
3734
3735
    /**
3736
     * @param mixed      $path
3737
     * @param \callable  $callable
3738
     * @param array|null $currentOffset
3739
     */
3740 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
3741
    {
3742 4
        if ($currentOffset === null) {
3743 4
            $currentOffset = &$this->array;
3744
        }
3745
3746 4
        $explodedPath = \explode($this->pathSeparator, $path);
3747 4
        $nextPath = \array_shift($explodedPath);
3748
3749 4
        if (!isset($currentOffset[$nextPath])) {
3750
            return;
3751
        }
3752
3753 4
        if (!empty($explodedPath)) {
3754 1
            $this->callAtPath(
3755 1
                \implode($this->pathSeparator, $explodedPath),
3756 1
                $callable,
3757 1
                $currentOffset[$nextPath]
3758
            );
3759
        } else {
3760 4
            $callable($currentOffset[$nextPath]);
3761
        }
3762 4
    }
3763
3764
    /**
3765
     * create a fallback for array
3766
     *
3767
     * 1. use the current array, if it's a array
3768
     * 2. fallback to empty array, if there is nothing
3769
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
3770
     * 4. call "createFromObject()" on object, if there is a "\ArrayAccess"-object
3771
     * 5. call "__toArray()" on object, if the method exists
3772
     * 6. cast a string or object with "__toString()" into an array
3773
     * 7. throw a "InvalidArgumentException"-Exception
3774
     *
3775
     * @param mixed $array
3776
     *
3777
     * @throws \InvalidArgumentException
3778
     *
3779
     * @return array
3780
     */
3781 890
    protected function fallbackForArray(&$array): array
3782
    {
3783 890
        if (\is_array($array)) {
3784 887
            return $array;
3785
        }
3786
3787 11
        if (!$array) {
3788 6
            return [];
3789
        }
3790
3791 10
        $isObject = \is_object($array);
3792
3793 10
        if ($isObject && $array instanceof self) {
3794 1
            return $array->getArray();
3795
        }
3796
3797 9
        if ($isObject && $array instanceof \ArrayAccess) {
3798
            /** @noinspection ReferenceMismatchInspection */
3799
            return static::createFromObject($array)->getArray();
3800
        }
3801
3802 9
        if ($isObject && $array instanceof \ArrayObject) {
3803
            return $array->getArrayCopy();
3804
        }
3805
3806 9
        if ($isObject && \method_exists($array, '__toArray')) {
3807
            return (array) $array->__toArray();
3808
        }
3809
3810
        /** @noinspection ReferenceMismatchInspection */
3811
        if (
3812 9
            \is_string($array)
3813
            ||
3814 9
            ($isObject && \method_exists($array, '__toString'))
3815
        ) {
3816 7
            return [(string) $array];
3817
        }
3818
3819 2
        throw new \InvalidArgumentException(
3820 2
            'Passed value should be a array'
3821
        );
3822
    }
3823
3824
    /**
3825
     * Get correct PHP constant for direction.
3826
     *
3827
     * @param int|string $direction
3828
     *
3829
     * @return int
3830
     */
3831 39
    protected function getDirection($direction): int
3832
    {
3833 39
        if (\is_string($direction)) {
3834 10
            $direction = \strtolower($direction);
3835
3836 10
            if ($direction === 'desc') {
3837 2
                $direction = \SORT_DESC;
3838
            } else {
3839 8
                $direction = \SORT_ASC;
3840
            }
3841
        }
3842
3843
        if (
3844 39
            $direction !== \SORT_DESC
3845
            &&
3846 39
            $direction !== \SORT_ASC
3847
        ) {
3848
            $direction = \SORT_ASC;
3849
        }
3850
3851 39
        return $direction;
3852
    }
3853
3854
    /**
3855
     * @return array|Property[]
3856
     */
3857 9
    protected function getPublicProperties(): array
3858
    {
3859 9
        static $PROPERTY_CACHE = [];
3860 9
        $cacheKey = 'Class::' . static::class;
3861
3862 9
        if (isset($PROPERTY_CACHE[$cacheKey])) {
3863 8
            return $PROPERTY_CACHE[$cacheKey];
3864
        }
3865
3866
        // init
3867 2
        $properties = [];
3868
3869 2
        $reflector = new \ReflectionClass($this);
3870 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
3871 2
        $docblock = $factory->create($reflector->getDocComment());
3872 2
        foreach ($docblock->getTagsByName('property') as $tag) {
3873
            /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
3874 2
            $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
3875
        }
3876
3877 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
3878
    }
3879
3880
    /**
3881
     * @param mixed               $glue
3882
     * @param array|static|string $pieces
3883
     * @param bool                $useKeys
3884
     *
3885
     * @return string
3886
     */
3887 35
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
3888
    {
3889 35
        if ($pieces instanceof self) {
3890 1
            $pieces = $pieces->getArray();
3891
        }
3892
3893 35
        if (\is_array($pieces)) {
3894 35
            $pieces_count = \count($pieces, \COUNT_NORMAL);
3895 35
            $pieces_count_not_zero = $pieces_count > 0;
3896
3897 35
            return \implode(
3898 35
                $glue,
3899 35
                \array_map(
3900 35
                    [$this, 'implode_recursive'],
3901 35
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
3902 35
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
3903
                )
3904
            );
3905
        }
3906
3907 35
        return (string) $pieces;
3908
    }
3909
3910
    /**
3911
     * @param mixed                 $needle   <p>
3912
     *                                        The searched value.
3913
     *                                        </p>
3914
     *                                        <p>
3915
     *                                        If needle is a string, the comparison is done
3916
     *                                        in a case-sensitive manner.
3917
     *                                        </p>
3918
     * @param array|\Generator|null $haystack <p>
3919
     *                                        The array.
3920
     *                                        </p>
3921
     * @param bool                  $strict   [optional] <p>
3922
     *                                        If the third parameter strict is set to true
3923
     *                                        then the in_array function will also check the
3924
     *                                        types of the
3925
     *                                        needle in the haystack.
3926
     *                                        </p>
3927
     *
3928
     * @return bool
3929
     *              <p>true if needle is found in the array, false otherwise</p>
3930
     */
3931 45
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
3932
    {
3933 45
        if ($haystack === null) {
3934
            $haystack = $this->getGenerator();
3935
        }
3936
3937 45
        foreach ($haystack as $item) {
3938 37
            if (\is_array($item) === true) {
3939 9
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
3940
            } else {
3941 37
                $returnTmp = ($strict === true ? $item === $needle : $item == $needle);
3942
            }
3943
3944 37
            if ($returnTmp === true) {
3945 37
                return true;
3946
            }
3947
        }
3948
3949 18
        return false;
3950
    }
3951
3952
    /**
3953
     * @param mixed $value
3954
     */
3955
    protected function internalGetArray(&$value)
3956
    {
3957
        if ($value instanceof self) {
3958
            $valueTmp = $value->getArray();
3959
            if (\count($valueTmp, \COUNT_NORMAL) === 0) {
3960
                $value = [];
3961
            } else {
3962
                /** @noinspection PhpUnusedLocalVariableInspection */
3963
                $value = &$valueTmp;
3964
            }
3965
        }
3966
3967
        /** @noinspection PhpComposerExtensionStubsInspection */
3968
        /** @noinspection NotOptimalIfConditionsInspection */
3969
        if (
3970
            \class_exists('JsonSerializable')
3971
            &&
3972
            $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...
3973
        ) {
3974
3975
            /** @noinspection PhpUnusedLocalVariableInspection */
3976
            $value = &$value->jsonSerialize();
3977
        }
3978
    }
3979
3980
    /**
3981
     * Internal mechanics of remove method.
3982
     *
3983
     * @param mixed $key
3984
     *
3985
     * @return bool
3986
     */
3987 18
    protected function internalRemove($key): bool
3988
    {
3989 18
        $path = \explode($this->pathSeparator, (string) $key);
3990
3991
        // Crawl though the keys
3992 18
        while (\count($path, \COUNT_NORMAL) > 1) {
3993
            $key = \array_shift($path);
3994
3995
            if (!$this->has($key)) {
3996
                return false;
3997
            }
3998
3999
            $this->array = &$this->array[$key];
4000
        }
4001
4002 18
        $key = \array_shift($path);
4003
4004 18
        unset($this->array[$key]);
4005
4006 18
        return true;
4007
    }
4008
4009
    /**
4010
     * Internal mechanic of set method.
4011
     *
4012
     * @param string|null $key
4013
     * @param mixed       $value
4014
     *
4015
     * @return bool
4016
     */
4017 779
    protected function internalSet($key, $value): bool
4018
    {
4019 779
        if ($this->checkPropertyTypes === true) {
4020 7
            if (isset($this->properties[$key]) === false) {
4021
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4022
            }
4023
4024 7
            $this->properties[$key]->checkType($value);
4025
        }
4026
4027 778
        if ($key === null) {
4028
            return false;
4029
        }
4030
4031
        // init
4032 778
        $array = &$this->array;
4033 778
        $path = \explode($this->pathSeparator, (string) $key);
4034
4035
        // Crawl through the keys
4036 778
        while (\count($path, \COUNT_NORMAL) > 1) {
4037 3
            $key = \array_shift($path);
4038
4039 3
            $array = &$array[$key];
4040
        }
4041
4042 778
        $array[\array_shift($path)] = $value;
4043
4044 778
        return true;
4045
    }
4046
4047
    /**
4048
     * Convert a object into an array.
4049
     *
4050
     * @param object $object
4051
     *
4052
     * @return mixed
4053
     */
4054 5
    protected static function objectToArray($object)
4055
    {
4056 5
        if (!\is_object($object)) {
4057 4
            return $object;
4058
        }
4059
4060 5
        if (\is_object($object)) {
4061 5
            $object = \get_object_vars($object);
4062
        }
4063
4064 5
        return \array_map(['self', 'objectToArray'], $object);
4065
    }
4066
4067
    /**
4068
     * sorting keys
4069
     *
4070
     * @param array      $elements
4071
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4072
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4073
     *                              <strong>SORT_NATURAL</strong></p>
4074
     *
4075
     * @return static
4076
     *                <p>(Mutable) Return this Arrayy object.</p>
4077
     */
4078 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
4079
    {
4080 18
        $direction = $this->getDirection($direction);
4081
4082
        switch ($direction) {
4083 18
            case 'desc':
4084 18
            case \SORT_DESC:
4085 6
                \krsort($elements, $strategy);
4086
4087 6
                break;
4088 13
            case 'asc':
4089 13
            case \SORT_ASC:
4090
            default:
4091 13
                \ksort($elements, $strategy);
4092
        }
4093
4094 18
        return $this;
4095
    }
4096
4097
    /**
4098
     * @param array      $elements  <p>Warning: used as reference</p>
4099
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4100
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4101
     *                              <strong>SORT_NATURAL</strong></p>
4102
     * @param bool       $keepKeys
4103
     *
4104
     * @return static
4105
     *                <p>(Mutable) Return this Arrayy object.</p>
4106
     */
4107 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false)
4108
    {
4109 20
        $direction = $this->getDirection($direction);
4110
4111 20
        if (!$strategy) {
4112 20
            $strategy = \SORT_REGULAR;
4113
        }
4114
4115
        switch ($direction) {
4116 20
            case 'desc':
4117 20
            case \SORT_DESC:
4118 9
                if ($keepKeys) {
4119 5
                    \arsort($elements, $strategy);
4120
                } else {
4121 4
                    \rsort($elements, $strategy);
4122
                }
4123
4124 9
                break;
4125 11
            case 'asc':
4126 11
            case \SORT_ASC:
4127
            default:
4128 11
                if ($keepKeys) {
4129 4
                    \asort($elements, $strategy);
4130
                } else {
4131 7
                    \sort($elements, $strategy);
4132
                }
4133
        }
4134
4135 20
        return $this;
4136
    }
4137
}
4138