Completed
Push — master ( 24f92d...8dbc4c )
by Lars
02:45
created

Arrayy::sorting()   B

Complexity

Conditions 8
Paths 20

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
nc 20
nop 4
dl 0
loc 29
ccs 16
cts 16
cp 1
crap 8
rs 8.2114
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
/** @noinspection ClassReImplementsParentInterfaceInspection */
8
/**
9
 * Methods to manage arrays.
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \Countable
15
{
16
  /**
17
   * @var array
18
   */
19
  protected $array = [];
20
21
  /**
22
   * @var string
23
   */
24
  protected $iteratorClass = ArrayyIterator::class;
25
26
  /**
27
   * @var string
28
   */
29
  protected $pathSeparator = '.';
30
31
  /**
32
   * @var bool
33
   */
34
  protected $checkPropertyTypes = false;
35
36
  /**
37
   * @var bool
38
   */
39
  protected $checkPropertiesMismatchInConstructor = false;
40
41
  /**
42
   * @var array|Property[]
43
   */
44
  protected $properties = [];
45
46
  /** @noinspection MagicMethodsValidityInspection */
47
  /** @noinspection PhpMissingParentConstructorInspection */
48
  /**
49
   * Initializes
50
   *
51
   * @param mixed  $array                                  <p>
52
   *                                                       Should be an array, otherwise it will try to convert
53
   *                                                       it into an array.
54
   *                                                       </p>
55
   * @param string $iteratorClass                          optional <p>
56
   *                                                       You can overwrite the ArrayyIterator, but mostly you don't
57
   *                                                       need this option.
58
   *                                                       </p>
59
   * @param bool   $checkForMissingPropertiesInConstructor optional <p>
60
   *                                                       You need to extend the "Arrayy"-class and you need to set
61
   *                                                       the $checkPropertiesMismatchInConstructor class property to
62
   *                                                       true, otherwise this option didn't not work anyway.
63
   *                                                       </p>
64
   */
65 889
  public function __construct($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true)
66
  {
67 889
    $array = $this->fallbackForArray($array);
68
69
    if (
70 887
      $this->checkPropertyTypes === true
71
      ||
72
      (
73 878
        $this->checkForMissingPropertiesInConstructor === true
0 ignored issues
show
Bug introduced by
The property checkForMissingPropertiesInConstructor does not seem to exist. Did you mean properties?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
74
        &&
75 887
        $checkForMissingPropertiesInConstructor === true
76
      )
77
    ) {
78 9
      $this->properties = $this->getPublicProperties();
79
    }
80
81
    if (
82 887
      $this->checkPropertiesMismatchInConstructor === true
83
      &&
84 887
      \count($array) !== 0
85
      &&
86 887
      \count(\array_diff_key($this->properties, $array)) > 0
87
    ) {
88 1
      throw new \InvalidArgumentException('Property mismatch - input: ' . print_r(array_keys($array), true) . ' | expected: ' . print_r(array_keys($this->properties), true));
89
    }
90
91 886
    foreach ($array as $key => $value) {
92 774
      $this->internalSet($key, $value);
93
    }
94
95 883
    $this->setIteratorClass($iteratorClass);
96 883
  }
97
98
  /**
99
   * Get a value by key.
100
   *
101
   * @param mixed $key
102
   *
103
   * @return mixed <p>Get a Value from the current array.</p>
104
   */
105 879
  public function &__get($key)
106
  {
107 879
    $return = $this->get($key);
108
109 879
    if (\is_array($return)) {
110
      return static::create($return, $this->iteratorClass, false);
111
    }
112
113 879
    return $return;
114
  }
115
116
  /**
117
   * Call object as function.
118
   *
119
   * @param mixed $key
120
   *
121
   * @return mixed
122
   */
123 1
  public function __invoke($key = null)
124
  {
125 1
    if ($key !== null) {
126 1
      return $this->array[$key] ?? false;
127
    }
128
129
    return (array)$this->array;
130
  }
131
132
  /**
133
   * Whether or not an element exists by key.
134
   *
135
   * @param mixed $key
136
   *
137
   * @return bool <p>True is the key/index exists, otherwise false.</p>
138
   */
139
  public function __isset($key)
140
  {
141
    return $this->offsetExists($key);
142
  }
143
144
  /**
145
   * Assigns a value to the specified element.
146
   *
147
   * @param mixed $key
148
   * @param mixed $value
149
   */
150 2
  public function __set($key, $value)
151
  {
152 2
    $this->internalSet($key, $value);
153 2
  }
154
155
  /**
156
   * magic to string
157
   *
158
   * @return string
159
   */
160 15
  public function __toString()
161
  {
162 15
    return $this->toString();
163
  }
164
165
  /**
166
   * Unset element by key.
167
   *
168
   * @param mixed $key
169
   */
170
  public function __unset($key)
171
  {
172
    $this->internalRemove($key);
173
  }
174
175
  /**
176
   * alias: for "Arrayy->append()"
177
   *
178
   * @see Arrayy::append()
179
   *
180
   * @param mixed $value
181
   *
182
   * @return static <p>(Mutable) Return this Arrayy object, with the appended values.</p>
183
   */
184 1
  public function add($value)
185
  {
186 1
    return $this->append($value);
187
  }
188
189
  /**
190
   * Append a (key) + value to the current array.
191
   *
192
   * @param mixed $value
193
   * @param mixed $key
194
   *
195
   * @return static <p>(Mutable) Return this Arrayy object, with the appended values.</p>
196
   */
197 9
  public function append($value, $key = null)
198
  {
199 9
    if ($key !== null) {
200
      if (
201
        isset($this->array[$key])
202
        &&
203
        \is_array($this->array[$key])
204
      ) {
205
        $this->array[$key][] = $value;
206
      } else {
207
        $this->array[$key] = $value;
208
      }
209
    } else {
210 9
      $this->array[] = $value;
211
    }
212
213 9
    return $this;
214
  }
215
216
  /**
217
   * Sort the entries by value.
218
   *
219
   * @param int $sort_flags [optional] <p>
220
   *                        You may modify the behavior of the sort using the optional
221
   *                        parameter sort_flags, for details
222
   *                        see sort.
223
   *                        </p>
224
   *
225
   * @return static <p>(Mutable) Return this Arrayy object.</p>
226
   */
227 4
  public function asort(int $sort_flags = 0)
228
  {
229 4
    \asort($this->array, $sort_flags);
230
231 4
    return $this;
232
  }
233
234
  /**
235
   * Counts all elements in an array, or something in an object.
236
   * <p>For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
237
   * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
238
   * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
239
   * implemented and used in PHP.
240
   *
241
   * @link http://php.net/manual/en/function.count.php
242
   *
243
   * @param int $mode [optional] If the optional mode parameter is set to
244
   *                  COUNT_RECURSIVE (or 1), count
245
   *                  will recursively count the array. This is particularly useful for
246
   *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
247
   *
248
   * @return int the number of elements in var, which is
249
   * typically an array, since anything else will have one
250
   * element.
251
   * </p>
252
   * <p>
253
   * If var is not an array or an object with
254
   * implemented Countable interface,
255
   * 1 will be returned.
256
   * There is one exception, if var is &null;,
257
   * 0 will be returned.
258
   * </p>
259
   * <p>
260
   * Caution: count may return 0 for a variable that isn't set,
261
   * but it may also return 0 for a variable that has been initialized with an
262
   * empty array. Use isset to test if a variable is set.
263
   *
264
   * @return int
265
   */
266 37
  public function count(int $mode = COUNT_NORMAL): int
267
  {
268 37
    return \count($this->array, $mode);
269
  }
270
271
  /**
272
   * Exchange the array for another one.
273
   *
274
   * @param array|static $data
275
   *
276
   * @return array
277
   */
278 1
  public function exchangeArray($data): array
279
  {
280 1
    $this->array = $this->fallbackForArray($data);
281
282 1
    return $this->array;
283
  }
284
285
  /**
286
   * Creates a copy of the ArrayyObject.
287
   *
288
   * @return array
289
   */
290 1
  public function getArrayCopy(): array
291
  {
292 1
    return $this->array;
293
  }
294
295
  /**
296
   * Returns a new ArrayyIterator, thus implementing the \ArrayIterator interface.
297
   *
298
   * @return \ArrayIterator <p>An iterator for the values in the array.</p>
299
   */
300 12
  public function getIterator(): \ArrayIterator
301
  {
302 12
    $iterator = $this->getIteratorClass();
303
304 12
    return new $iterator($this->array);
305
  }
306
307
  /**
308
   * Gets the iterator classname for the ArrayObject.
309
   *
310
   * @return string
311
   */
312 12
  public function getIteratorClass(): string
313
  {
314 12
    return $this->iteratorClass;
315
  }
316
317
  /**
318
   * Sort the entries by key
319
   *
320
   * @param int $sort_flags [optional] <p>
321
   *                        You may modify the behavior of the sort using the optional
322
   *                        parameter sort_flags, for details
323
   *                        see sort.
324
   *                        </p>
325
   *
326
   * @return static <p>(Mutable) Return this Arrayy object.</p>
327
   */
328 4
  public function ksort(int $sort_flags = 0)
329
  {
330 4
    \ksort($this->array, $sort_flags);
331
332 4
    return $this;
333
  }
334
335
  /**
336
   * Sort an array using a case insensitive "natural order" algorithm
337
   *
338
   * @return static <p>(Mutable) Return this Arrayy object.</p>
339
   */
340
  public function natcasesort()
341
  {
342
    \natcasesort($this->array);
343
344
    return $this;
345
  }
346
347
  /**
348
   * Sort entries using a "natural order" algorithm
349
   *
350
   * @return static <p>(Mutable) Return this Arrayy object.</p>
351
   */
352 1
  public function natsort()
353
  {
354 1
    \natsort($this->array);
355
356 1
    return $this;
357
  }
358
359
  /**
360
   * Whether or not an offset exists.
361
   *
362
   * @param int|float|string $offset
363
   *
364
   * @return bool
365
   */
366 44
  public function offsetExists($offset): bool
367
  {
368 44
    if ($this->isEmpty()) {
369 4
      return false;
370
    }
371
372
    // php cast "bool"-index into "int"-index
373 40
    if ((bool)$offset === $offset) {
374 1
      $offset = (int)$offset;
375
    }
376
377 40
    $tmpReturn = \array_key_exists($offset, $this->array);
378
379
    if (
380 40
      $tmpReturn === true
381
      ||
382
      (
383 16
        $tmpReturn === false
384
        &&
385 40
        \strpos((string)$offset, $this->pathSeparator) === false
386
      )
387
    ) {
388 38
      return $tmpReturn;
389
    }
390
391 3
    $offsetExists = false;
392
393 3
    if (\strpos((string)$offset, $this->pathSeparator) !== false) {
394
395 3
      $offsetExists = false;
396 3
      $explodedPath = \explode($this->pathSeparator, (string)$offset);
397 3
      $lastOffset = \array_pop($explodedPath);
398 3
      $containerPath = \implode($this->pathSeparator, $explodedPath);
399
400 3
      $this->callAtPath(
401 3
        $containerPath,
402 3
        function ($container) use ($lastOffset, &$offsetExists) {
0 ignored issues
show
Documentation introduced by
function ($container) us...tOffset, $container); } is of type object<Closure>, but the function expects a object<callable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
403 3
          $offsetExists = \array_key_exists($lastOffset, $container);
404 3
        }
405
      );
406
    }
407
408 3
    return $offsetExists;
409
  }
410
411
  /**
412
   * Returns the value at specified offset.
413
   *
414
   * @param int|float|string $offset
415
   *
416
   * @return mixed <p>Will return null if the offset did not exists.</p>
417
   */
418 30
  public function offsetGet($offset)
419
  {
420 30
    return $this->offsetExists($offset) ? $this->get($offset) : null;
421
  }
422
423
  /**
424
   * Assigns a value to the specified offset.
425
   *
426
   * @param null|int|string $offset
427
   * @param mixed           $value
428
   */
429 20
  public function offsetSet($offset, $value)
430
  {
431 20
    if ($offset === null) {
432 4
      $this->array[] = $value;
433
    } else {
434 16
      $this->internalSet($offset, $value);
435
    }
436 20
  }
437
438
  /**
439
   * Unset an offset.
440
   *
441
   * @param int|float|string $offset
442
   */
443 7
  public function offsetUnset($offset)
444
  {
445 7
    if ($this->isEmpty()) {
446 1
      return;
447
    }
448
449 6
    if (\array_key_exists($offset, $this->array)) {
450 4
      unset($this->array[$offset]);
451
452 4
      return;
453
    }
454
455 3
    if (\strpos((string)$offset, $this->pathSeparator) !== false) {
456
457 2
      $path = \explode($this->pathSeparator, (string)$offset);
458 2
      $pathToUnset = \array_pop($path);
459
460 2
      $this->callAtPath(
461 2
        \implode($this->pathSeparator, $path),
462 2
        function (&$offset) use ($pathToUnset) {
0 ignored issues
show
Documentation introduced by
function (&$offset) use(...ffset[$pathToUnset]); } is of type object<Closure>, but the function expects a object<callable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
463 2
          unset($offset[$pathToUnset]);
464 2
        }
465
      );
466
467
    }
468 3
  }
469
470
  /** @noinspection SenselessProxyMethodInspection | can not add return type, because of the "Serializable" interface */
471
  /**
472
   * Serialize the current "Arrayy"-object.
473
   *
474
   * @return string
475
   */
476 1
  public function serialize()
477
  {
478 1
    return parent::serialize();
479
  }
480
481
  /**
482
   * Sets the iterator classname for the current "Arrayy"-object.
483
   *
484
   * @param string $class
485
   *
486
   * @return void
487
   *
488
   * @throws \InvalidArgumentException
489
   */
490 883
  public function setIteratorClass($class)
491
  {
492 883
    if (\class_exists($class)) {
493 883
      $this->iteratorClass = $class;
494
495 883
      return;
496
    }
497
498
    if (\strpos($class, '\\') === 0) {
499
      $class = '\\' . $class;
500
      if (\class_exists($class)) {
501
        $this->iteratorClass = $class;
502
503
        return;
504
      }
505
    }
506
507
    throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
508
  }
509
510
  /**
511
   * Sort the entries with a user-defined comparison function and maintain key association.
512
   *
513
   * @param \callable $function
514
   *
515
   * @return static <p>(Mutable) Return this Arrayy object.</p>
516
   *
517
   * @throws \InvalidArgumentException
518
   */
519 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...
520
  {
521
    if (!\is_callable($function)) {
522
      throw new \InvalidArgumentException(
523
        'Passed function must be callable'
524
      );
525
    }
526
527
    \uasort($this->array, $function);
528
529
    return $this;
530
  }
531
532
  /**
533
   * Sort the entries by keys using a user-defined comparison function.
534
   *
535
   * @param \callable $function
536
   *
537
   * @return static <p>(Mutable) Return this Arrayy object.</p>
538
   *
539
   * @throws \InvalidArgumentException
540
   */
541 5
  public function uksort($function)
542
  {
543 5
    return $this->customSortKeys($function);
544
  }
545
546
  /**
547
   * Unserialize an string and return this object.
548
   *
549
   * @param string $string
550
   *
551
   * @return static <p>(Mutable)</p>
552
   */
553 1
  public function unserialize($string)
554
  {
555 1
    parent::unserialize($string);
556
557 1
    return $this;
558
  }
559
560
  /**
561
   * Append a (key) + values to the current array.
562
   *
563
   * @param array $values
564
   * @param mixed $key
565
   *
566
   * @return static <p>(Mutable) Return this Arrayy object, with the appended values.</p>
567
   */
568 1
  public function appendArrayValues(array $values, $key = null)
569
  {
570 1
    if ($key !== null) {
571
      if (
572 1
        isset($this->array[$key])
573
        &&
574 1
        \is_array($this->array[$key])
575
      ) {
576 1
        foreach ($values as $value) {
577 1
          $this->array[$key][] = $value;
578
        }
579
      } else {
580
        foreach ($values as $value) {
581 1
          $this->array[$key] = $value;
582
        }
583
      }
584
    } else {
585
      foreach ($values as $value) {
586
        $this->array[] = $value;
587
      }
588
    }
589
590 1
    return $this;
591
  }
592
593
  /**
594
   * Add a suffix to each key.
595
   *
596
   * @param mixed $prefix
597
   *
598
   * @return static <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
599
   */
600 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...
601
  {
602
    // init
603 10
    $result = [];
604
605 10
    foreach ($this->getGenerator() as $key => $item) {
606 9
      if ($item instanceof self) {
607
        $result[$prefix . $key] = $item->appendToEachKey($prefix);
608 9
      } elseif (\is_array($item)) {
609
        $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
610
                                      ->appendToEachKey($prefix)
611
                                      ->toArray();
612
      } else {
613 9
        $result[$prefix . $key] = $item;
614
      }
615
    }
616
617 10
    return self::create($result, $this->iteratorClass, false);
618
  }
619
620
  /**
621
   * Add a prefix to each value.
622
   *
623
   * @param mixed $prefix
624
   *
625
   * @return static <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
626
   */
627 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...
628
  {
629
    // init
630 10
    $result = [];
631
632 10
    foreach ($this->getGenerator() as $key => $item) {
633 9
      if ($item instanceof self) {
634
        $result[$key] = $item->appendToEachValue($prefix);
635 9
      } elseif (\is_array($item)) {
636
        $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
637 9
      } elseif (\is_object($item)) {
638 1
        $result[$key] = $item;
639
      } else {
640 9
        $result[$key] = $prefix . $item;
641
      }
642
    }
643
644 10
    return self::create($result, $this->iteratorClass, false);
645
  }
646
647
  /**
648
   * Convert an array into a object.
649
   *
650
   * @param array $array PHP array
651
   *
652
   * @return \stdClass (object)
653
   */
654 4
  protected static function arrayToObject(array $array = []): \stdClass
655
  {
656
    // init
657 4
    $object = new \stdClass();
658
659 4
    if (\count($array, COUNT_NORMAL) <= 0) {
660 1
      return $object;
661
    }
662
663 3
    foreach ($array as $name => $value) {
664 3
      if (\is_array($value)) {
665 1
        $object->{$name} = self::arrayToObject($value);
666
      } else {
667 3
        $object->{$name} = $value;
668
      }
669
    }
670
671 3
    return $object;
672
  }
673
674
  /**
675
   * @param array|\Generator $input        <p>
676
   *                                       An array containing keys to return.
677
   *                                       </p>
678
   * @param mixed            $search_value [optional] <p>
679
   *                                       If specified, then only keys containing these values are returned.
680
   *                                       </p>
681
   * @param bool             $strict       [optional] <p>
682
   *                                       Determines if strict comparison (===) should be used during the search.
683
   *                                       </p>
684
   *
685
   * @return array an array of all the keys in input.
686
   */
687 10
  protected function array_keys_recursive(array $input = null, $search_value = null, bool $strict = true): array
688
  {
689
    // init
690 10
    $keys = [];
691 10
    $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
692
693 10
    if ($input === null) {
694
      $input = $this->getGenerator();
695
    }
696
697 10
    foreach ($input as $key => $value) {
698
699
      if (
700 10
        $search_value === null
701
        ||
702
        (
703
          \is_array($search_value) === true
704
          &&
705 10
          \in_array($key, $search_value, $strict)
706
        )
707
      ) {
708 10
        $keys[] = $key;
709
      }
710
711
      // check if recursive is needed
712 10
      if (\is_array($value) === true) {
713 10
        $keysTmp[] = $this->array_keys_recursive($value);
714
      }
715
    }
716
717 10
    $keys = \array_merge($keys, ...$keysTmp);
718
719 10
    return $keys;
720
  }
721
722
  /**
723
   * Sort an array in reverse order and maintain index association.
724
   *
725
   * @return static <p>(Mutable) Return this Arrayy object.</p>
726
   */
727 4
  public function arsort()
728
  {
729 4
    \arsort($this->array);
730
731 4
    return $this;
732
  }
733
734
  /**
735
   * Iterate over the current array and execute a callback for each loop.
736
   *
737
   * @param \Closure $closure
738
   *
739
   * @return static <p>(Immutable)</p>
740
   */
741 2
  public function at(\Closure $closure)
742
  {
743 2
    $arrayy = clone $this;
744
745 2
    foreach ($arrayy->getGenerator() as $key => $value) {
746 2
      $closure($value, $key);
747
    }
748
749 2
    return static::create(
750 2
      $arrayy->toArray(),
751 2
      $this->iteratorClass,
752 2
      false
753
    );
754
  }
755
756
  /**
757
   * Returns the average value of the current array.
758
   *
759
   * @param int $decimals <p>The number of decimal-numbers to return.</p>
760
   *
761
   * @return int|double <p>The average value.</p>
762
   */
763 10
  public function average($decimals = 0)
764
  {
765 10
    $count = \count($this->array, COUNT_NORMAL);
766
767 10
    if (!$count) {
768 2
      return 0;
769
    }
770
771 8
    if (!\is_int($decimals)) {
772 3
      $decimals = 0;
773
    }
774
775 8
    return \round(\array_sum($this->array) / $count, $decimals);
776
  }
777
778
  /**
779
   * @param mixed      $path
780
   * @param \callable  $callable
781
   * @param null|array $currentOffset
782
   */
783 4
  protected function callAtPath($path, $callable, &$currentOffset = null)
784
  {
785 4
    if ($currentOffset === null) {
786 4
      $currentOffset = &$this->array;
787
    }
788
789 4
    $explodedPath = \explode($this->pathSeparator, $path);
790 4
    $nextPath = \array_shift($explodedPath);
791
792 4
    if (!isset($currentOffset[$nextPath])) {
793
      return;
794
    }
795
796 4
    if (!empty($explodedPath)) {
797 1
      $this->callAtPath(
798 1
        \implode($this->pathSeparator, $explodedPath),
799 1
        $callable,
800 1
        $currentOffset[$nextPath]
801
      );
802
    } else {
803 4
      $callable($currentOffset[$nextPath]);
804
    }
805 4
  }
806
807
  /**
808
   * Changes all keys in an array.
809
   *
810
   * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
811
   *                  or <strong>CASE_LOWER</strong> (default)</p>
812
   *
813
   * @return static <p>(Immutable)</p>
814
   */
815 1
  public function changeKeyCase(int $case = CASE_LOWER)
816
  {
817
    if (
818 1
        $case !== CASE_LOWER
819
        &&
820 1
        $case !== CASE_UPPER
821
    ) {
822
      $case = CASE_LOWER;
823
    }
824
825 1
    $return = [];
826 1
    foreach ($this->array as $key => $value) {
827
828 1
      if ($case === CASE_LOWER) {
829
        /** @noinspection PhpComposerExtensionStubsInspection */
830 1
        $key = \mb_strtolower((string)$key);
831
      } else {
832
        /** @noinspection PhpComposerExtensionStubsInspection */
833 1
        $key = \mb_strtoupper((string)$key);
834
      }
835
836 1
      $return[$key] = $value;
837
    }
838
839 1
    return static::create(
840 1
        $return,
841 1
      $this->iteratorClass,
842 1
      false
843
    );
844
  }
845
846
  /**
847
   * Change the path separator of the array wrapper.
848
   *
849
   * By default, the separator is: "."
850
   *
851
   * @param string $separator <p>Separator to set.</p>
852
   *
853
   * @return static <p>Mutable</p>
854
   */
855 1
  public function changeSeparator($separator)
856
  {
857 1
    $this->pathSeparator = $separator;
858
859 1
    return $this;
860
  }
861
862
  /**
863
   * Create a chunked version of the current array.
864
   *
865
   * @param int  $size         <p>Size of each chunk.</p>
866
   * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
867
   *
868
   * @return static <p>(Immutable) A new array of chunks from the original array.</p>
869
   */
870 4
  public function chunk($size, $preserveKeys = false)
871
  {
872 4
    return static::create(
873 4
      \array_chunk($this->array, $size, $preserveKeys),
874 4
      $this->iteratorClass,
875 4
      false
876
    );
877
  }
878
879
  /**
880
   * Clean all falsy values from the current array.
881
   *
882
   * @return static <p>(Immutable)</p>
883
   */
884 8
  public function clean()
885
  {
886 8
    return $this->filter(
887 8
      function ($value) {
888 7
        return (bool)$value;
889 8
      }
890
    );
891
  }
892
893
  /**
894
   * WARNING!!! -> Clear the current array.
895
   *
896
   * @return static <p>(Mutable) Return this Arrayy object, with an empty array.</p>
897
   */
898 4
  public function clear()
899
  {
900 4
    $this->array = [];
901
902 4
    return $this;
903
  }
904
905
  /**
906
   * Check if an item is in the current array.
907
   *
908
   * @param string|int|float $value
909
   * @param bool             $recursive
910
   * @param bool             $strict
911
   *
912
   * @return bool
913
   */
914 22
  public function contains($value, $recursive = false, $strict = true): bool
915
  {
916 22
    if ($recursive === true) {
917 18
      return $this->in_array_recursive($value, $this->array, $strict);
918
    }
919
920 13
    return \in_array($value, $this->array, $strict);
921
  }
922
923
  /**
924
   * Check if an (case-insensitive) string is in the current array.
925
   *
926
   * @param string $value
927
   * @param bool   $recursive
928
   *
929
   * @return bool
930
   */
931 26
  public function containsCaseInsensitive($value, $recursive = false): bool
932
  {
933 26
    if ($recursive === true) {
934
      /** @noinspection PhpComposerExtensionStubsInspection */
935 26
      return $this->in_array_recursive(
936 26
        \mb_strtoupper((string)$value),
937 26
        $this->walk(
938 26
          function (&$val) {
0 ignored issues
show
Documentation introduced by
function (&$val) { $...upper((string) $val); } is of type object<Closure>, but the function expects a object<callable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
939
            /** @noinspection PhpComposerExtensionStubsInspection */
940 22
            $val = \mb_strtoupper((string)$val);
941 26
          },
942 26
          true
943 26
        )->getArray(),
944 26
        true
945
      );
946
    }
947
948
    /** @noinspection PhpComposerExtensionStubsInspection */
949 13
    return \in_array(
950 13
      \mb_strtoupper((string)$value),
951 13
      $this->walk(
952 13
        function (&$val) {
0 ignored issues
show
Documentation introduced by
function (&$val) { $...upper((string) $val); } is of type object<Closure>, but the function expects a object<callable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

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