Completed
Push — master ( e77e30...3fb8e6 )
by Lars
01:56
created

Arrayy::slice()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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