Completed
Push — master ( 7bb865...4a2dd3 )
by Lars
02:34
created

Arrayy::slice()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 3
crap 1
1
<?php
2
3
namespace Arrayy;
4
5
use ArrayAccess;
6
use Closure;
7
use voku\helper\UTF8;
8
9
/**
10
 * Methods to manage arrays.
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
class Arrayy extends \ArrayObject implements \Countable, \IteratorAggregate, \ArrayAccess, \Serializable
16
{
17
  /**
18
   * @var array
19
   */
20
  protected $array = array();
21
22
  /**
23
   * Initializes
24
   *
25
   * @param array $array
26
   */
27 563
  public function __construct($array = array())
28
  {
29 563
    $array = $this->fallbackForArray($array);
30
31 561
    $this->array = $array;
32 561
  }
33
34
  /**
35
   * create a fallback for array
36
   *
37
   * 1. fallback to empty array, if there is nothing
38
   * 2. cast a String or Object with "__toString" into an array
39
   * 3. call "__toArray" on Object, if the method exists
40
   * 4. throw a "InvalidArgumentException"-Exception
41
   *
42
   * @param $array
43
   *
44
   * @return array
45
   */
46 563
  protected function fallbackForArray(&$array)
47
  {
48 563
    if (is_array($array)) {
49 561
      return $array;
50
    }
51
52 5
    if ($array instanceof self) {
53 1
      return $array->getArray();
54
    }
55
56 4
    if (!$array) {
57 1
      return array();
58
    }
59
60
    if (
61 3
        is_string($array)
62
        ||
63 2
        (is_object($array) && method_exists($array, '__toString'))
64 3
    ) {
65 1
      return (array)$array;
66
    }
67
68 2
    if (is_object($array) && method_exists($array, '__toArray')) {
69
      return (array)$array->__toArray();
70
    }
71
72 2
    throw new \InvalidArgumentException(
73
        'Passed value must be a array'
74 2
    );
75
  }
76
77
  /**
78
   * Get the current array from the "Arrayy"-object
79
   *
80
   * @return array
81
   */
82 417
  public function getArray()
83
  {
84 417
    return $this->array;
85
  }
86
87
  /**
88
   * Create a new Arrayy object via string.
89
   *
90
   * @param string      $str       The input string.
91
   * @param string|null $delimiter The boundary string.
92
   * @param string|null $regEx     Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be used.
93
   *
94
   * @return Arrayy Returns created instance
95
   */
96 8
  public static function createFromString($str, $delimiter, $regEx = null)
97
  {
98 8
    if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type string|null 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...
99 1
      preg_match_all($regEx, $str, $array);
100
101 1
      if (count($array) > 0) {
102 1
        $array = $array[0];
103 1
      }
104
105 1
    } else {
106 7
      $array = explode($delimiter, $str);
107
    }
108
109
    // trim all string in the array
110 8
    array_walk(
111 8
        $array,
112
        function (&$val) {
113 8
          if (is_string($val)) {
114 8
            $val = trim($val);
115 8
          }
116 8
        }
117 8
    );
118
119 8
    return static::create($array);
120
  }
121
122
  /**
123
   * Creates a Arrayy object.
124
   *
125
   * @param array $array
126
   *
127
   * @return Arrayy Returns created instance
128
   */
129 402
  public static function create($array = array())
130
  {
131 402
    return new static($array);
132
  }
133
134
  /**
135
   * create a new Arrayy object via JSON,
136
   *
137
   * @param string $json
138
   *
139
   * @return Arrayy Returns created instance
140
   */
141 5
  public static function createFromJson($json)
142
  {
143 5
    $array = UTF8::json_decode($json, true);
144
145 5
    return static::create($array);
146
  }
147
148
  /**
149
   * Create a new instance filled with values from an object implementing ArrayAccess.
150
   *
151
   * @param ArrayAccess $elements Object that implements ArrayAccess
152
   *
153
   * @return Arrayy Returns created instance
154
   */
155 4
  public static function createFromObject(ArrayAccess $elements)
156
  {
157 4
    $array = new static();
158 4
    foreach ($elements as $key => $value) {
159
      /** @noinspection OffsetOperationsInspection */
160 3
      $array[$key] = $value;
161 4
    }
162
163 4
    return $array;
164
  }
165
166
  /**
167
   * Create a new instance containing a range of elements.
168
   *
169
   * @param mixed $low  First value of the sequence
170
   * @param mixed $high The sequence is ended upon reaching the end value
171
   * @param int   $step Used as the increment between elements in the sequence
172
   *
173
   * @return Arrayy The created array
174
   */
175 1
  public static function createWithRange($low, $high, $step = 1)
176
  {
177 1
    return static::create(range($low, $high, $step));
178
  }
179
180
  /**
181
   * alias: for "Arrayy->random()"
182
   *
183
   * @return Arrayy
184
   */
185
  public function getRandom()
186
  {
187
    return $this->random();
188
  }
189
190
  /**
191
   * Get a random value from the current array.
192
   *
193
   * @param null|int $number how many values you will take?
194
   *
195
   * @return Arrayy
196
   */
197 18
  public function random($number = null)
198
  {
199 18
    if ($this->count() === 0) {
200
      return static::create();
201
    }
202
203 18
    if ($number === null) {
204 12
      $arrayRandValue = (array)$this->array[array_rand($this->array)];
205
206 12
      return static::create($arrayRandValue);
207
    }
208
209 8
    shuffle($this->array);
210
211 8
    return $this->first($number);
212
  }
213
214
  /**
215
   * Count the values from the current array.
216
   *
217
   * INFO: only a alias for "$arrayy->size()"
218
   *
219
   * @return int
220
   */
221 77
  public function count()
222
  {
223 77
    return $this->size();
224
  }
225
226
  /**
227
   * Get the size of an array.
228
   *
229
   * @return int
230
   */
231 77
  public function size()
232
  {
233 77
    return count($this->array);
234
  }
235
236
  /**
237
   * Get the first value(s) from the current array.
238
   *
239
   * @param int|null $number how many values you will take?
240
   *
241
   * @return Arrayy
242
   */
243 33
  public function first($number = null)
244
  {
245 33
    if ($number === null) {
246 8
      $array = (array)array_shift($this->array);
247 8
    } else {
248 25
      $number = (int)$number;
249 25
      $array = array_splice($this->array, 0, $number, true);
250
    }
251
252 33
    return static::create($array);
253
  }
254
255
  /**
256
   * Append a value to an array.
257
   *
258
   * @param mixed $value
259
   *
260
   * @return Arrayy
261
   */
262 8
  public function append($value)
263
  {
264 8
    $this->array[] = $value;
265
266 8
    return static::create($this->array);
267
  }
268
269
  /**
270
   * @return mixed
271
   */
272
  public function serialize()
273
  {
274
    return serialize($this->array);
275
  }
276
277
  /**
278
   * @param string $array
279
   */
280
  public function unserialize($array)
281
  {
282
    $this->array = unserialize($array);
283
  }
284
285
  /**
286
   * Assigns a value to the specified offset.
287
   *
288
   * @param mixed $offset
289
   * @param mixed $value
290
   */
291 13
  public function offsetSet($offset, $value)
292
  {
293 13
    if (null === $offset) {
294 4
      $this->array[] = $value;
295 4
    } else {
296 9
      $this->array[$offset] = $value;
297
    }
298 13
  }
299
300
  /**
301
   * alias: for "Arrayy->randomValue()"
302
   *
303
   * @return Arrayy
304
   */
305
  public function getRandomValue()
306
  {
307
    return $this->randomValue();
308
  }
309
310
  /**
311
   * Pick a random value from the values of this array.
312
   *
313
   * @return Arrayy
314
   */
315
  public function randomValue()
316
  {
317
    return $this->random(1);
318
  }
319
320
  /**
321
   * alias: for "Arrayy->randomValues()"
322
   *
323
   * @param int $number
324
   *
325
   * @return Arrayy
326
   */
327
  public function getRandomValues($number)
328
  {
329
    return $this->randomValues($number);
330
  }
331
332
  /**
333
   * Pick a given number of random values out of this array.
334
   *
335
   * @param int $number
336
   *
337
   * @return Arrayy
338
   */
339
  public function randomValues($number)
340
  {
341
    $number = (int)$number;
342
343
    return $this->random($number);
344
  }
345
346
  /**
347
   * alias: for "Arrayy->randomKey()"
348
   *
349
   * @return Arrayy
350
   */
351
  public function getRandomKey()
352
  {
353
    return $this->randomKey();
354
  }
355
356
  /**
357
   * Pick a random key/index from the keys of this array.
358
   *
359
   * @return Arrayy
360
   *
361
   * @throws \RangeException If array is empty
362
   */
363
  public function randomKey()
364
  {
365
    return $this->randomKeys(1);
366
  }
367
368
  /**
369
   * Pick a given number of random keys/indexes out of this array.
370
   *
371
   * @param int $number The number of keys/indexes (should be <= $this->count())
372
   *
373
   * @return Arrayy
374
   *
375
   * @throws \RangeException If array is empty
376
   */
377
  public function randomKeys($number)
378
  {
379
    $number = (int)$number;
380
    $count = $this->count();
381
382
    if ($number === 0 || $number > $count) {
383
      throw new \RangeException(
384
          sprintf(
385
              'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
386
              $number,
387
              $count
388
          )
389
      );
390
    }
391
392
    $result = (array)array_rand($this->array, $number);
393
394
    return static::create($result);
395
  }
396
397
  /**
398
   * alias: for "Arrayy->randomKeys()"
399
   *
400
   * @param int $number
401
   *
402
   * @return Arrayy
403
   */
404
  public function getRandomKeys($number)
405
  {
406
    return $this->randomKeys($number);
407
  }
408
409
  /**
410
   * find by ...
411
   *
412
   * @param        $property
413
   * @param        $value
414
   * @param string $comparisonOp
415
   *
416
   * @return Arrayy
417
   */
418
  public function findBy($property, $value, $comparisonOp = 'eq')
419
  {
420
    $array = $this->filterBy($property, $value, $comparisonOp);
421
422
    return static::create($array);
0 ignored issues
show
Documentation introduced by
$array is of type object<Arrayy\Arrayy>, but the function expects a array.

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...
423
  }
424
425
  /**
426
   * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular property
427
   * within that.
428
   *
429
   * @param        $property
430
   * @param        $value
431
   * @param string $comparisonOp
432
   *
433
   * @return Arrayy
434
   */
435 1
  public function filterBy($property, $value, $comparisonOp = null)
436
  {
437 1
    if (!$comparisonOp) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $comparisonOp of type string|null 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...
438 1
      $comparisonOp = is_array($value) ? 'contains' : 'eq';
439 1
    }
440
441
    $ops = array(
442
        'eq'          => function ($item, $prop, $value) {
443 1
          return $item[$prop] === $value;
444 1
        },
445
        'gt'          => function ($item, $prop, $value) {
446
          return $item[$prop] > $value;
447 1
        },
448
        'gte'         => function ($item, $prop, $value) {
449
          return $item[$prop] >= $value;
450 1
        },
451
        'lt'          => function ($item, $prop, $value) {
452 1
          return $item[$prop] < $value;
453 1
        },
454
        'lte'         => function ($item, $prop, $value) {
455
          return $item[$prop] <= $value;
456 1
        },
457
        'ne'          => function ($item, $prop, $value) {
458
          return $item[$prop] !== $value;
459 1
        },
460
        'contains'    => function ($item, $prop, $value) {
461 1
          return in_array($item[$prop], (array)$value, true);
462 1
        },
463
        'notContains' => function ($item, $prop, $value) {
464
          return !in_array($item[$prop], (array)$value, true);
465 1
        },
466
        'newer'       => function ($item, $prop, $value) {
467
          return strtotime($item[$prop]) > strtotime($value);
468 1
        },
469
        'older'       => function ($item, $prop, $value) {
470
          return strtotime($item[$prop]) < strtotime($value);
471 1
        },
472 1
    );
473
474 1
    $result = array_values(
475 1
        array_filter(
476 1
            (array)$this->array,
477
            function ($item) use (
478 1
                $property,
479 1
                $value,
480 1
                $ops,
481 1
                $comparisonOp
482
            ) {
483 1
              $item = (array)$item;
484 1
              $itemArrayy = new Arrayy($item);
485 1
              $item[$property] = $itemArrayy->get($property, array());
486
487 1
              return $ops[$comparisonOp]($item, $property, $value);
488
            }
489 1
        )
490 1
    );
491
492 1
    return static::create($result);
493
  }
494
495
  /**
496
   * Get a value from an array (optional using dot-notation).
497
   *
498
   * @param string $key     The key to look for
499
   * @param mixed  $default Default value to fallback to
500
   * @param array  $array   The array to get from,
501
   *                        if it's set to "null" we use the current array from the class
502
   *
503
   * @return mixed
504
   */
505 31
  public function get($key, $default = null, $array = null)
506
  {
507 31
    if (is_array($array) === true) {
508 3
      $usedArray = $array;
509 3
    } else {
510 29
      $usedArray = $this->array;
511
    }
512
513 31
    if (null === $key) {
514 1
      return $usedArray;
515
    }
516
517 31
    if (isset($usedArray[$key])) {
518 22
      return $usedArray[$key];
519
    }
520
521
    // Crawl through array, get key according to object or not
522 16
    foreach (explode('.', $key) as $segment) {
523 16
      if (!isset($usedArray[$segment])) {
524 16
        return $default instanceof Closure ? $default() : $default;
525
      }
526
527
      $usedArray = $usedArray[$segment];
528
    }
529
530
    return $usedArray;
531
  }
532
533
  /**
534
   * WARNING: Creates a Arrayy object by reference.
535
   *
536
   * @param array $array
537
   *
538
   * @return $this
539
   */
540
  public function createByReference(&$array = array())
541
  {
542
    $array = $this->fallbackForArray($array);
543
544
    $this->array = &$array;
545
546
    return $this;
547
  }
548
549
  /**
550
   * Get all values from a array.
551
   *
552
   * @return Arrayy
553
   */
554 1
  public function values()
555
  {
556 1
    $array = array_values((array)$this->array);
557
558 1
    return static::create($array);
559
  }
560
561
  /**
562
   * Group values from a array according to the results of a closure.
563
   *
564
   * @param string $grouper a callable function name
565
   * @param bool   $saveKeys
566
   *
567
   * @return Arrayy
568
   */
569 3
  public function group($grouper, $saveKeys = false)
570
  {
571 3
    $array = (array)$this->array;
572 3
    $result = array();
573
574
    // Iterate over values, group by property/results from closure
575 3
    foreach ($array as $key => $value) {
576 3
      $groupKey = is_callable($grouper) ? $grouper($value, $key) : $this->get($grouper, null, $value);
577 3
      $newValue = $this->get($groupKey, null, $result);
578
579
      // Add to results
580 3
      if ($groupKey !== null) {
581 2
        if ($saveKeys) {
582 1
          $result[$groupKey] = $newValue;
583 1
          $result[$groupKey][$key] = $value;
584 1
        } else {
585 1
          $result[$groupKey] = $newValue;
586 1
          $result[$groupKey][] = $value;
587
        }
588 2
      }
589
590 3
    }
591
592 3
    return static::create($result);
593
  }
594
595
  /**
596
   * Given a list and an iterate-function that returns
597
   * a key for each element in the list (or a property name),
598
   * returns an object with an index of each item.
599
   *
600
   * Just like groupBy, but for when you know your keys are unique.
601
   *
602
   * @param mixed $key
603
   *
604
   * @return Arrayy
605
   */
606 3
  public function indexBy($key)
607
  {
608 3
    $results = array();
609
610 3
    foreach ($this->array as $a) {
611 3
      if (isset($a[$key])) {
612 2
        $results[$a[$key]] = $a;
613 2
      }
614 3
    }
615
616 3
    return static::create($results);
617
  }
618
619
  /**
620
   * magic to string
621
   *
622
   * @return string
623
   */
624 14
  public function __toString()
625
  {
626 14
    return $this->toString();
627
  }
628
629
  /**
630
   * Implodes array to a string with specified separator.
631
   *
632
   * @param string $separator The element's separator
633
   *
634
   * @return string The string representation of array, separated by ","
635
   */
636 14
  public function toString($separator = ',')
637
  {
638 14
    return $this->implode($separator);
639
  }
640
641
  /**
642
   * Implodes an array.
643
   *
644
   * @param string $with What to implode it with
645
   *
646
   * @return string
647
   */
648 22
  public function implode($with = '')
649
  {
650 22
    return implode($with, $this->array);
651
  }
652
653
  /**
654
   * Push one or more values onto the end of array at once.
655
   *
656
   * @return $this An Arrayy object with pushed elements to the end of array
657
   */
658 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...
659
  {
660 4
    if (func_num_args()) {
661 4
      $args = array_merge(array(&$this->array), func_get_args());
662 4
      call_user_func_array('array_push', $args);
663 4
    }
664
665 4
    return $this;
666
  }
667
668
  /**
669
   * Shifts a specified value off the beginning of array.
670
   *
671
   * @return mixed A shifted element from the current array.
672
   */
673 4
  public function shift()
674
  {
675 4
    return array_shift($this->array);
676
  }
677
678
  /**
679
   * Prepends one or more values to the beginning of array at once.
680
   *
681
   * @return Arrayy Array object with prepended elements to the beginning of array
682
   */
683 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...
684
  {
685 4
    if (func_num_args()) {
686 4
      $args = array_merge(array(&$this->array), func_get_args());
687 4
      call_user_func_array('array_unshift', $args);
688 4
    }
689
690 4
    return $this;
691
  }
692
693
  /**
694
   * Get a value by key.
695
   *
696
   * @param $key
697
   *
698
   * @return mixed
699
   */
700
  public function &__get($key)
701
  {
702
    return $this->array[$key];
703
  }
704
705
  /**
706
   * Whether or not an offset exists.
707
   *
708
   * @param mixed $offset
709
   *
710
   * @return bool
711
   */
712 13
  public function offsetExists($offset)
713
  {
714 13
    return isset($this->array[$offset]);
715
  }
716
717
  /**
718
   * Assigns a value to the specified element.
719
   *
720
   * @param $key
721
   * @param $value
722
   */
723
  public function __set($key, $value)
724
  {
725
    $this->array[$key] = $value;
726
  }
727
728
  /**
729
   * Whether or not an element exists by key.
730
   *
731
   * @param $key
732
   *
733
   * @return bool
734
   */
735
  public function __isset($key)
736
  {
737
    return isset($this->array[$key]);
738
  }
739
740
  /**
741
   * Unset element by key
742
   *
743
   * @param mixed $key
744
   */
745
  public function __unset($key)
746
  {
747
    unset($this->array[$key]);
748
  }
749
750
  /**
751
   * Call object as function.
752
   *
753
   * @param mixed $key
754
   *
755
   * @return mixed
756
   */
757
  public function __invoke($key = null)
758
  {
759
    if ($key !== null) {
760
      if (isset($this->array[$key])) {
761
        return $this->array[$key];
762
      } else {
763
        return false;
764
      }
765
    }
766
767
    return (array)$this->array;
768
  }
769
770
  /**
771
   * Search for the value of the current array via $index.
772
   *
773
   * @param mixed $index
774
   *
775
   * @return Arrayy will return a empty Arrayy if the value wasn't found
776
   */
777 7
  public function searchValue($index)
778
  {
779
    // init
780 7
    $return = array();
781
782 7
    if (null !== $index) {
783 7
      $keyExists = isset($this->array[$index]);
784
785 7
      if ($keyExists !== false) {
786 5
        $return = array($this->array[$index]);
787 5
      }
788 7
    }
789
790 7
    return static::create($return);
791
  }
792
793
  /**
794
   * Check if all items in current array match a truth test.
795
   *
796
   * @param \Closure $closure
797
   *
798
   * @return bool
799
   */
800 9 View Code Duplication
  public function matches(\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...
801
  {
802
    // Reduce the array to only booleans
803 9
    $array = $this->each($closure);
804
805
    // Check the results
806 9
    if (count($array) === 0) {
807 2
      return true;
808
    }
809
810 7
    $array = array_search(false, $array->toArray(), false);
811
812 7
    return is_bool($array);
813
  }
814
815
  /**
816
   * Unset an offset.
817
   *
818
   * @param mixed $offset
819
   */
820 5
  public function offsetUnset($offset)
821
  {
822 5
    if ($this->offsetExists($offset)) {
823 3
      unset($this->array[$offset]);
824 3
    }
825 5
  }
826
827
  /**
828
   * Iterate over the current array and modify the array's value.
829
   *
830
   * @param \Closure $closure
831
   *
832
   * @return Arrayy
833
   */
834 22 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...
835
  {
836 22
    $array = $this->array;
837
838 22
    foreach ($array as $key => &$value) {
839 18
      $value = $closure($value, $key);
840 22
    }
841
842 22
    return static::create($array);
843
  }
844
845
  /**
846
   * alias: for "Arrayy->getArray()"
847
   */
848 143
  public function toArray()
849
  {
850 143
    return $this->getArray();
851
  }
852
853
  /**
854
   * Check if any item in the current array matches a truth test.
855
   *
856
   * @param \Closure $closure
857
   *
858
   * @return bool
859
   */
860 9 View Code Duplication
  public function matchesAny(\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...
861
  {
862
    // Reduce the array to only booleans
863 9
    $array = $this->each($closure);
864
865
    // Check the results
866 9
    if (count($array) === 0) {
867 2
      return true;
868
    }
869
870 7
    $array = array_search(true, $array->toArray(), false);
871
872 7
    return is_int($array);
873
  }
874
875
  /**
876
   * Check if we have named keys in the current array.
877
   *
878
   * @return bool
879
   */
880 12
  public function isAssoc()
881
  {
882 12
    if (count($this->array) === 0) {
883 1
      return false;
884
    }
885
886 11
    return (bool)count(array_filter(array_keys($this->array), 'is_string'));
887
  }
888
889
  /**
890
   * Check whether array is numeric or not.
891
   *
892
   * @return bool Returns true if numeric, false otherwise
893
   */
894
  public function isNumeric()
895
  {
896
    $isNumeric = true;
897
898
    if ($this->isEmpty()) {
899
      $isNumeric = false;
900
    } else {
901
      foreach ($this->getKeys() as $key) {
902
        if (!is_int($key)) {
903
          $isNumeric = false;
904
          break;
905
        }
906
      }
907
    }
908
909
    return $isNumeric;
910
  }
911
912
  /**
913
   * Check whether the array is empty or not.
914
   *
915
   * @return bool Returns true if empty, false otherwise
916
   */
917
  public function isEmpty()
918
  {
919
    return !$this->array;
920
  }
921
922
  /**
923
   * Returns the value at specified offset.
924
   *
925
   * @param mixed $offset
926
   *
927
   * @return mixed return null if the offset did not exists
928
   */
929 8
  public function offsetGet($offset)
930
  {
931 8
    return $this->offsetExists($offset) ? $this->array[$offset] : null;
932
  }
933
934
  /**
935
   * alias: for "Arrayy->keys()"
936
   *
937
   * @return Arrayy
938
   */
939
  public function getKeys()
940
  {
941
    return $this->keys();
942
  }
943
944
  /**
945
   * Get all keys from the current array.
946
   *
947
   * @return Arrayy
948
   */
949 1
  public function keys()
950
  {
951 1
    $array = array_keys((array)$this->array);
952
953 1
    return static::create($array);
954
  }
955
956
  /**
957
   * Check if the current array is a multi-array.
958
   *
959
   * @return bool
960
   */
961 13
  public function isMultiArray()
962
  {
963 13
    return !(count($this->array) === count($this->array, COUNT_RECURSIVE));
964
  }
965
966
  /**
967
   * Check if an item is in the current array.
968
   *
969
   * @param mixed $value
970
   *
971
   * @return bool
972
   */
973 9
  public function contains($value)
974
  {
975 9
    return in_array($value, $this->array, true);
976
  }
977
978
  /**
979
   * Check if the given key/index exists in the array.
980
   *
981
   * @param mixed $key Key/index to search for
982
   *
983
   * @return bool Returns true if the given key/index exists in the array, false otherwise
984
   */
985
  public function containsKey($key)
986
  {
987
    return array_key_exists($key, $this->array);
988
  }
989
990
  /**
991
   * Returns the average value of the current array.
992
   *
993
   * @param int $decimals The number of decimals to return
994
   *
995
   * @return int|double The average value
996
   */
997 10
  public function average($decimals = null)
998
  {
999 10
    $count = $this->count();
1000
1001 10
    if (!$count) {
1002 2
      return 0;
1003
    }
1004
1005 8
    if (!is_int($decimals)) {
1006 3
      $decimals = null;
1007 3
    }
1008
1009 8
    return round(array_sum($this->array) / $count, $decimals);
1010
  }
1011
1012
  /**
1013
   * Returns a new ArrayIterator, thus implementing the IteratorAggregate interface.
1014
   *
1015
   * @return \ArrayIterator An iterator for the values in the array.
1016
   */
1017 6
  public function getIterator()
1018
  {
1019 6
    return new \ArrayIterator($this->array);
1020
  }
1021
1022
  /**
1023
   * Count the values from the current array.
1024
   *
1025
   * INFO: only a alias for "$arrayy->size()"
1026
   *
1027
   * @return int
1028
   */
1029 10
  public function length()
1030
  {
1031 10
    return $this->size();
1032
  }
1033
1034
  /**
1035
   * Get the max value from an array.
1036
   *
1037
   * @return mixed
1038
   */
1039 10
  public function max()
1040
  {
1041 10
    if ($this->count() === 0) {
1042 1
      return false;
1043
    }
1044
1045 9
    return max($this->array);
1046
  }
1047
1048
  /**
1049
   * Get the min value from an array.
1050
   *
1051
   * @return mixed
1052
   */
1053 10
  public function min()
1054
  {
1055 10
    if ($this->count() === 0) {
1056 1
      return false;
1057
    }
1058
1059 9
    return min($this->array);
1060
  }
1061
1062
  /**
1063
   * Find the first item in an array that passes the truth test,
1064
   *  otherwise return false
1065
   *
1066
   * @param \Closure $closure
1067
   *
1068
   * @return mixed|false false if we did not find the value
1069
   */
1070 7
  public function find(\Closure $closure)
1071
  {
1072 7
    foreach ($this->array as $key => $value) {
1073 5
      if ($closure($value, $key)) {
1074 4
        return $value;
1075
      }
1076 4
    }
1077
1078 3
    return false;
1079
  }
1080
1081
  /**
1082
   * WARNING!!! -> Clear the current array.
1083
   *
1084
   * @return $this will always return an empty Arrayy object
1085
   */
1086 4
  public function clear()
1087
  {
1088 4
    $this->array = array();
1089
1090 4
    return $this;
1091
  }
1092
1093
  /**
1094
   * Clean all falsy values from an array.
1095
   *
1096
   * @return Arrayy
1097
   */
1098 8
  public function clean()
1099
  {
1100 8
    return $this->filter(
1101
        function ($value) {
1102 7
          return (bool)$value;
1103
        }
1104 8
    );
1105
  }
1106
1107
  /**
1108
   * Find all items in an array that pass the truth test.
1109
   *
1110
   * @param \Closure|null $closure
1111
   *
1112
   * @return Arrayy
1113
   */
1114 8
  public function filter($closure = null)
1115
  {
1116 8
    if (!$closure) {
1117 1
      return $this->clean();
1118
    }
1119
1120 8
    $array = array_filter($this->array, $closure);
1121
1122 8
    return static::create($array);
1123
  }
1124
1125
  /**
1126
   * Get a random value from an array, with the ability to skew the results.
1127
   *
1128
   * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
1129
   *
1130
   * @param array    $array
1131
   * @param null|int $number how many values you will take?
1132
   *
1133
   * @return Arrayy
1134
   */
1135 9
  public function randomWeighted(array $array, $number = null)
1136
  {
1137 9
    $options = array();
1138 9
    foreach ($array as $option => $weight) {
1139 9
      if ($this->searchIndex($option)->count() > 0) {
1140 2
        for ($i = 0; $i < $weight; ++$i) {
1141 1
          $options[] = $option;
1142 1
        }
1143 2
      }
1144 9
    }
1145
1146 9
    return $this->mergeAppendKeepIndex($options)->random($number);
1147
  }
1148
1149
  /**
1150
   * Search for the first index of the current array via $value.
1151
   *
1152
   * @param mixed $value
1153
   *
1154
   * @return Arrayy will return a empty Arrayy if the index was not found
1155
   */
1156 16
  public function searchIndex($value)
1157
  {
1158 16
    $key = array_search($value, $this->array, true);
1159
1160 16
    if ($key === false) {
1161 9
      $return = array();
1162 9
    } else {
1163 7
      $return = array($key);
1164
    }
1165
1166 16
    return static::create($return);
1167
  }
1168
1169
  /**
1170
   * Merge the new $array into the current array.
1171
   *
1172
   * - keep key,value from the current array, also if the index is in the new $array
1173
   *
1174
   * @param array $array
1175
   * @param bool  $recursive
1176
   *
1177
   * @return Arrayy
1178
   */
1179 25 View Code Duplication
  public function mergeAppendKeepIndex(array $array = array(), $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...
1180
  {
1181 25
    if (true === $recursive) {
1182 4
      $result = array_replace_recursive($this->array, $array);
1183 4
    } else {
1184 21
      $result = array_replace($this->array, $array);
1185
    }
1186
1187 25
    return static::create($result);
1188
  }
1189
1190
  /**
1191
   * alias: for "Arrayy->searchIndex()"
1192
   *
1193
   * @param mixed $value Value to search for
1194
   *
1195
   * @return Arrayy will return a empty Arrayy if the index was not found
1196
   */
1197
  public function indexOf($value)
1198
  {
1199
    return $this->searchIndex($value);
1200
  }
1201
1202
  /**
1203
   * Return a boolean flag which indicates whether the two input arrays have any common elements.
1204
   *
1205
   * @param array $search
1206
   *
1207
   * @return bool
1208
   */
1209 1
  public function intersects(array $search)
1210
  {
1211 1
    return count($this->intersection($search)->array) > 0;
1212
  }
1213
1214
  /**
1215
   * Return an array with all elements found in input array.
1216
   *
1217
   * @param array $search
1218
   *
1219
   * @return Arrayy
1220
   */
1221 2
  public function intersection(array $search)
1222
  {
1223 2
    $result = array_values(array_intersect($this->array, $search));
1224
1225 2
    return static::create($result);
1226
  }
1227
1228
  /**
1229
   * Get the last value(s) from the current array.
1230
   *
1231
   * @param int|null $number
1232
   *
1233
   * @return Arrayy
1234
   */
1235 11
  public function last($number = null)
1236
  {
1237 11
    if ($number === null) {
1238 7
      $poppedValue = (array)$this->pop();
1239 7
      $arrayy = static::create($poppedValue);
1240 7
    } else {
1241 4
      $number = (int)$number;
1242 4
      $arrayy = $this->rest(-$number);
1243
    }
1244
1245 11
    return $arrayy;
1246
  }
1247
1248
  /**
1249
   * Pop a specified value off the end of the current array.
1250
   *
1251
   * @return mixed The popped element from the current array.
1252
   */
1253 11
  public function pop()
1254
  {
1255 11
    return array_pop($this->array);
1256
  }
1257
1258
  /**
1259
   * Get the last elements from index $from until the end of this array.
1260
   *
1261
   * @param int $from
1262
   *
1263
   * @return Arrayy
1264
   */
1265 16
  public function rest($from = 1)
1266
  {
1267 16
    $result = array_splice($this->array, $from);
1268
1269 16
    return static::create($result);
1270
  }
1271
1272
  /**
1273
   * Get everything but the last..$to items.
1274
   *
1275
   * @param int $to
1276
   *
1277
   * @return Arrayy
1278
   */
1279 12
  public function initial($to = 1)
1280
  {
1281 12
    $slice = count($this->array) - $to;
1282
1283 12
    return $this->first($slice);
1284
  }
1285
1286
  /**
1287
   * Extract a slice of the array.
1288
   *
1289
   * @param int      $offset       Slice begin index
1290
   * @param int|null $length       Length of the slice
1291
   * @param bool     $preserveKeys Whether array keys are preserved or no
1292
   *
1293
   * @return static A slice of the original array with length $length
1294
   */
1295 4
  public function slice($offset, $length = null, $preserveKeys = false)
1296
  {
1297 4
    $result = array_slice($this->array, $offset, $length, $preserveKeys);
1298
1299 4
    return static::create($result);
1300
  }
1301
1302
  /**
1303
   * Iterate over an array and execute a callback for each loop.
1304
   *
1305
   * @param \Closure $closure
1306
   *
1307
   * @return Arrayy
1308
   */
1309 2 View Code Duplication
  public function at(\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...
1310
  {
1311 2
    $array = $this->array;
1312
1313 2
    foreach ($array as $key => $value) {
1314 2
      $closure($value, $key);
1315 2
    }
1316
1317 2
    return static::create($array);
1318
  }
1319
1320
  /**
1321
   * Merge the new $array into the current array.
1322
   *
1323
   * - replace duplicate assoc-keys from the current array with the key,values from the new $array
1324
   * - create new indexes
1325
   *
1326
   * @param array $array
1327
   * @param bool  $recursive
1328
   *
1329
   * @return Arrayy
1330
   */
1331 16 View Code Duplication
  public function mergeAppendNewIndex(array $array = array(), $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...
1332
  {
1333 16
    if (true === $recursive) {
1334 4
      $result = array_merge_recursive($this->array, $array);
1335 4
    } else {
1336 12
      $result = array_merge($this->array, $array);
1337
    }
1338
1339 16
    return static::create($result);
1340
  }
1341
1342
  /**
1343
   * Merge the current array into the new $array.
1344
   *
1345
   * - replace duplicate assoc-keys from new $array with the key,values from the current array
1346
   * - create new indexes
1347
   *
1348
   * @param array $array
1349
   * @param bool  $recursive
1350
   *
1351
   * @return Arrayy
1352
   */
1353 16 View Code Duplication
  public function mergePrependNewIndex(array $array = array(), $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...
1354
  {
1355 16
    if (true === $recursive) {
1356 4
      $result = array_merge_recursive($array, $this->array);
1357 4
    } else {
1358 12
      $result = array_merge($array, $this->array);
1359
    }
1360
1361 16
    return static::create($result);
1362
  }
1363
1364
  /**
1365
   * Merge the the current array into the $array.
1366
   *
1367
   * - use key,value from the new $array, also if the index is in the current array
1368
   *
1369
   * @param array $array
1370
   * @param bool  $recursive
1371
   *
1372
   * @return Arrayy
1373
   */
1374 16 View Code Duplication
  public function mergePrependKeepIndex(array $array = array(), $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...
1375
  {
1376 16
    if (true === $recursive) {
1377 4
      $result = array_replace_recursive($array, $this->array);
1378 4
    } else {
1379 12
      $result = array_replace($array, $this->array);
1380
    }
1381
1382 16
    return static::create($result);
1383
  }
1384
1385
  /**
1386
   * Return values that are only in the current array.
1387
   *
1388
   * @param array $array
1389
   *
1390
   * @return Arrayy
1391
   */
1392 12
  public function diff(array $array = array())
1393
  {
1394 12
    $result = array_diff($this->array, $array);
1395
1396 12
    return static::create($result);
1397
  }
1398
1399
  /**
1400
   * Return values that are only in the new $array.
1401
   *
1402
   * @param array $array
1403
   *
1404
   * @return Arrayy
1405
   */
1406 8
  public function diffReverse(array $array = array())
1407
  {
1408 8
    $result = array_diff($array, $this->array);
1409
1410 8
    return static::create($result);
1411
  }
1412
1413
  /**
1414
   * Replace the first matched value in an array.
1415
   *
1416
   * @param mixed $search
1417
   * @param mixed $replacement
1418
   *
1419
   * @return Arrayy
1420
   */
1421 3
  public function replaceOneValue($search, $replacement = '')
1422
  {
1423 3
    $array = $this->array;
1424 3
    $key = array_search($search, $array, true);
1425
1426 3
    if ($key !== false) {
1427 3
      $array[$key] = $replacement;
1428 3
    }
1429
1430 3
    return static::create($array);
1431
  }
1432
1433
  /**
1434
   * Replace values in the current array.
1435
   *
1436
   * @param string $search      The string to replace.
1437
   * @param string $replacement What to replace it with.
1438
   *
1439
   * @return Arrayy
1440
   */
1441 1
  public function replaceValues($search, $replacement = '')
1442
  {
1443 1
    $array = $this->each(
1444
        function ($value) use ($search, $replacement) {
1445 1
          return UTF8::str_replace($search, $replacement, $value);
1446
        }
1447 1
    );
1448
1449 1
    return static::create($array);
0 ignored issues
show
Documentation introduced by
$array is of type object<Arrayy\Arrayy>, but the function expects a array.

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...
1450
  }
1451
1452
  /**
1453
   * Replace the keys in an array with another set.
1454
   *
1455
   * @param array $keys An array of keys matching the array's size
1456
   *
1457
   * @return Arrayy
1458
   */
1459 1
  public function replaceKeys(array $keys)
1460
  {
1461 1
    $values = array_values($this->array);
1462 1
    $result = array_combine($keys, $values);
1463
1464 1
    return static::create($result);
1465
  }
1466
1467
  /**
1468
   * Create an array using the current array as keys and the other array as values.
1469
   *
1470
   * @param array $array Values array
1471
   *
1472
   * @return Arrayy Arrayy object with values from the other array.
1473
   */
1474 1
  public function replaceAllValues(array $array)
1475
  {
1476 1
    $result = array_combine($this->array, $array);
1477
1478 1
    return static::create($result);
1479
  }
1480
1481
  /**
1482
   * Create an array using the current array as values and the other array as keys.
1483
   *
1484
   * @param array $keys Keys array
1485
   *
1486
   * @return Arrayy Arrayy object with keys from the other array.
1487
   */
1488 1
  public function replaceAllKeys(array $keys)
1489
  {
1490 1
    $result = array_combine($keys, $this->array);
1491
1492 1
    return static::create($result);
1493
  }
1494
1495
  /**
1496
   * Shuffle the current array.
1497
   *
1498
   * @return Arrayy
1499
   */
1500 1
  public function shuffle()
1501
  {
1502 1
    $array = $this->array;
1503
1504 1
    shuffle($array);
1505
1506 1
    return static::create($array);
1507
  }
1508
1509
  /**
1510
   * Split an array in the given amount of pieces.
1511
   *
1512
   * @param int  $numberOfPieces
1513
   * @param bool $keepKeys
1514
   *
1515
   * @return array
1516
   */
1517 1
  public function split($numberOfPieces = 2, $keepKeys = false)
1518
  {
1519 1
    if (count($this->array) === 0) {
1520 1
      $result = array();
1521 1
    } else {
1522 1
      $numberOfPieces = (int)$numberOfPieces;
1523 1
      $splitSize = ceil(count($this->array) / $numberOfPieces);
1524 1
      $result = array_chunk($this->array, $splitSize, $keepKeys);
1525
    }
1526
1527 1
    return static::create($result);
1528
  }
1529
1530
  /**
1531
   * Create a chunked version of this array.
1532
   *
1533
   * @param int  $size         Size of each chunk
1534
   * @param bool $preserveKeys Whether array keys are preserved or no
1535
   *
1536
   * @return static A new array of chunks from the original array
1537
   */
1538 4
  public function chunk($size, $preserveKeys = false)
1539
  {
1540 4
    $result = array_chunk($this->array, $size, $preserveKeys);
1541
1542 4
    return static::create($result);
1543
  }
1544
1545
  /**
1546
   * Returns the values from a single column of the input array, identified by
1547
   * the $columnKey, can be used to extract data-columns from multi-arrays.
1548
   *
1549
   * Info: Optionally, you may provide an $indexKey to index the values in the returned
1550
   * array by the values from the $indexKey column in the input array.
1551
   *
1552
   * @param mixed $columnKey
1553
   * @param mixed $indexKey
1554
   *
1555
   * @return Arrayy
1556
   */
1557 1
  public function getColumn($columnKey = null, $indexKey = null)
1558
  {
1559 1
    $result = array_column($this->array, $columnKey, $indexKey);
1560
1561 1
    return static::create($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by array_column($this->array, $columnKey, $indexKey) on line 1559 can also be of type false or null; however, Arrayy\Arrayy::create() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1562
  }
1563
1564
  /**
1565
   * Invoke a function on all of an array's values.
1566
   *
1567
   * @param mixed $callable
1568
   * @param array $arguments
1569
   *
1570
   * @return Arrayy
1571
   */
1572 1
  public function invoke($callable, $arguments = array())
1573
  {
1574
    // If one argument given for each iteration, create an array for it.
1575 1
    if (!is_array($arguments)) {
1576 1
      $arguments = StaticArrayy::repeat($arguments, count($this->array))->getArray();
1577 1
    }
1578
1579
    // If the callable has arguments, pass them.
1580 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...
1581 1
      $array = array_map($callable, $this->array, $arguments);
1582 1
    } else {
1583 1
      $array = array_map($callable, $this->array);
1584
    }
1585
1586 1
    return static::create($array);
1587
  }
1588
1589
  /**
1590
   * Apply the given function to the every element of the array,
1591
   * collecting the results.
1592
   *
1593
   * @param callable $callable
1594
   *
1595
   * @return Arrayy Arrayy object with modified elements
1596
   */
1597 4
  public function map($callable)
1598
  {
1599 4
    $result = array_map($callable, $this->array);
1600
1601 4
    return static::create($result);
1602
  }
1603
1604
  /**
1605
   * Check if a value is in the current array using a closure.
1606
   *
1607
   * @param \Closure $closure
1608
   *
1609
   * @return bool Returns true if the given value is found, false otherwise
1610
   */
1611
  public function exists(\Closure $closure)
1612
  {
1613
    $isExists = false;
1614
    foreach ($this->array as $key => $value) {
1615
      if ($closure($value, $key)) {
1616
        $isExists = true;
1617
        break;
1618
      }
1619
    }
1620
1621
    return $isExists;
1622
  }
1623
1624
  /**
1625
   * Return all items that fail the truth test.
1626
   *
1627
   * @param \Closure $closure
1628
   *
1629
   * @return Arrayy
1630
   */
1631 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...
1632
  {
1633 1
    $filtered = array();
1634
1635 1
    foreach ($this->array as $key => $value) {
1636 1
      if (!$closure($value, $key)) {
1637 1
        $filtered[$key] = $value;
1638 1
      }
1639 1
    }
1640
1641 1
    return static::create($filtered);
1642
  }
1643
1644
  /**
1645
   * Replace a key with a new key/value pair.
1646
   *
1647
   * @param $replace
1648
   * @param $key
1649
   * @param $value
1650
   *
1651
   * @return Arrayy
1652
   */
1653 1
  public function replace($replace, $key, $value)
1654
  {
1655 1
    $this->remove($replace);
1656
1657 1
    return $this->set($key, $value);
1658
  }
1659
1660
  /**
1661
   * Remove a value from the current array (optional using dot-notation).
1662
   *
1663
   * @param mixed $key
1664
   *
1665
   * @return Arrayy
1666
   */
1667 17
  public function remove($key)
1668
  {
1669
    // Recursive call
1670 17
    if (is_array($key)) {
1671
      foreach ($key as $k) {
1672
        $this->internalRemove($k);
1673
      }
1674
1675
      return static::create($this->array);
1676
    }
1677
1678 17
    $this->internalRemove($key);
1679
1680 17
    return static::create($this->array);
1681
  }
1682
1683
  /**
1684
   * Internal mechanics of remove method.
1685
   *
1686
   * @param $key
1687
   *
1688
   * @return boolean
1689
   */
1690 17
  protected function internalRemove($key)
1691
  {
1692
    // Explode keys
1693 17
    $keys = explode('.', $key);
1694
1695
    // Crawl though the keys
1696 17 View Code Duplication
    while (count($keys) > 1) {
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...
1697
      $key = array_shift($keys);
1698
1699
      if (!$this->has($key)) {
1700
        return false;
1701
      }
1702
1703
      $this->array = &$this->array[$key];
1704
    }
1705
1706 17
    $key = array_shift($keys);
1707
1708 17
    unset($this->array[$key]);
1709
1710 17
    return true;
1711
  }
1712
1713
  /**
1714
   * Check if an array has a given key.
1715
   *
1716
   * @param mixed $key
1717
   *
1718
   * @return bool
1719
   */
1720 18
  public function has($key)
1721
  {
1722
    // Generate unique string to use as marker.
1723 18
    $unFound = (string)uniqid('arrayy', true);
1724
1725 18
    return $this->get($key, $unFound) !== $unFound;
1726
  }
1727
1728
  /**
1729
   * Set a value for the current array (optional using dot-notation).
1730
   *
1731
   * @param string $key   The key to set
1732
   * @param mixed  $value Its value
1733
   *
1734
   * @return Arrayy
1735
   */
1736 14
  public function set($key, $value)
1737
  {
1738 14
    $this->internalSet($key, $value);
1739
1740 14
    return static::create($this->array);
1741
  }
1742
1743
  /**
1744
   * Internal mechanic of set method.
1745
   *
1746
   * @param mixed $key
1747
   * @param mixed $value
1748
   *
1749
   * @return bool
1750
   */
1751 14
  protected function internalSet($key, $value)
1752
  {
1753 14
    if (null === $key) {
1754
      return false;
1755
    }
1756
1757
    // Explode the keys
1758 14
    $keys = explode('.', $key);
1759
1760
    // Crawl through the keys
1761 14 View Code Duplication
    while (count($keys) > 1) {
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...
1762
      $key = array_shift($keys);
1763
1764
      $this->array[$key] = $this->get(array(), null, $key);
0 ignored issues
show
Documentation introduced by
array() is of type array, but the function expects a string.

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...
1765
      $this->array = &$this->array[$key];
1766
    }
1767
1768
    // Bind final tree on the array
1769 14
    $key = array_shift($keys);
1770
1771 14
    $this->array[$key] = $value;
1772
1773 14
    return true;
1774
  }
1775
1776
  /**
1777
   * Get a value from a array and set it if it was not.
1778
   *
1779
   * WARNING: this method only set the value, if the $key is not already set
1780
   *
1781
   * @param string $key     The key
1782
   * @param mixed  $default The default value to set if it isn't
1783
   *
1784
   * @return mixed
1785
   */
1786 9
  public function setAndGet($key, $default = null)
1787
  {
1788
    // If the key doesn't exist, set it
1789 9
    if (!$this->has($key)) {
1790 4
      $this->array = $this->set($key, $default)->getArray();
1791 4
    }
1792
1793 9
    return $this->get($key);
1794
  }
1795
1796
  /**
1797
   * Remove the first value from the current array.
1798
   *
1799
   * @return Arrayy
1800
   */
1801 7
  public function removeFirst()
1802
  {
1803 7
    array_shift($this->array);
1804
1805 7
    return static::create($this->array);
1806
  }
1807
1808
  /**
1809
   * Remove the last value from the current array.
1810
   *
1811
   * @return Arrayy
1812
   */
1813 7
  public function removeLast()
1814
  {
1815 7
    array_pop($this->array);
1816
1817 7
    return static::create($this->array);
1818
  }
1819
1820
  /**
1821
   * Removes a particular value from an array (numeric or associative).
1822
   *
1823
   * @param mixed $value
1824
   *
1825
   * @return Arrayy
1826
   */
1827 7
  public function removeValue($value)
1828
  {
1829 7
    $isNumericArray = true;
1830 7
    foreach ($this->array as $key => $item) {
1831 6
      if ($item === $value) {
1832 6
        if (!is_int($key)) {
1833
          $isNumericArray = false;
1834
        }
1835 6
        unset($this->array[$key]);
1836 6
      }
1837 7
    }
1838
1839 7
    if ($isNumericArray) {
1840 7
      $this->array = array_values($this->array);
1841 7
    }
1842
1843 7
    return static::create($this->array);
1844
  }
1845
1846
  /**
1847
   * Pad array to the specified size with a given value.
1848
   *
1849
   * @param int   $size  Size of the result array
1850
   * @param mixed $value Empty value by default
1851
   *
1852
   * @return Arrayy Arrayy object padded to $size with $value
1853
   */
1854 4
  public function pad($size, $value)
1855
  {
1856 4
    $result = array_pad($this->array, $size, $value);
1857
1858 4
    return static::create($result);
1859
  }
1860
1861
  /**
1862
   * Prepend a value to an array.
1863
   *
1864
   * @param mixed $value
1865
   *
1866
   * @return Arrayy
1867
   */
1868 7
  public function prepend($value)
1869
  {
1870 7
    array_unshift($this->array, $value);
1871
1872 7
    return static::create($this->array);
1873
  }
1874
1875
  /**
1876
   * alias: for "Arrayy->append()"
1877
   *
1878
   * @param $value
1879
   *
1880
   * @return $this
1881
   */
1882 1
  public function add($value)
1883
  {
1884 1
    $this->array[] = $value;
1885
1886 1
    return $this;
1887
  }
1888
1889
  /**
1890
   * Create a numerically re-indexed Arrayy object.
1891
   *
1892
   * @return Arrayy The new instance with re-indexed array-elements
1893
   */
1894 4
  public function reindex()
1895
  {
1896 4
    $this->array = array_values($this->array);
1897
1898 4
    return static::create($this->array);
1899
  }
1900
1901
  /**
1902
   * Return the array in the reverse order.
1903
   *
1904
   * @return Arrayy
1905
   */
1906 7
  public function reverse()
1907
  {
1908 7
    $this->array = array_reverse($this->array);
1909
1910 7
    return static::create($this->array);
1911
  }
1912
1913
  /**
1914
   * Custom sort by value via "usort"
1915
   *
1916
   * @link http://php.net/manual/en/function.usort.php
1917
   *
1918
   * @param callable $func
1919
   *
1920
   * @return $this
1921
   */
1922 4
  public function customSortValues(callable $func)
1923
  {
1924 4
    usort($this->array, $func);
1925
1926 4
    return $this;
1927
  }
1928
1929
  /**
1930
   * Custom sort by index via "uksort"
1931
   *
1932
   * @link http://php.net/manual/en/function.uksort.php
1933
   *
1934
   * @param callable $func
1935
   *
1936
   * @return $this
1937
   */
1938 4
  public function customSortKeys(callable $func)
1939
  {
1940 4
    uksort($this->array, $func);
1941
1942 4
    return $this;
1943
  }
1944
1945
  /**
1946
   * Sort the current array by key.
1947
   *
1948
   * @link http://php.net/manual/en/function.ksort.php
1949
   * @link http://php.net/manual/en/function.krsort.php
1950
   *
1951
   * @param int|string $direction use SORT_ASC or SORT_DESC
1952
   * @param int        $strategy  use e.g.: SORT_REGULAR or SORT_NATURAL
1953
   *
1954
   * @return $this
1955
   */
1956 18
  public function sortKeys($direction = SORT_ASC, $strategy = SORT_REGULAR)
1957
  {
1958 18
    $this->sorterKeys($this->array, $direction, $strategy);
1959
1960 18
    return $this;
1961
  }
1962
1963
  /**
1964
   * sorting keys
1965
   *
1966
   * @param array $elements
1967
   * @param int   $direction
1968
   * @param int   $strategy
1969
   */
1970 18
  protected function sorterKeys(array &$elements, $direction = SORT_ASC, $strategy = SORT_REGULAR)
1971
  {
1972 18
    $direction = $this->getDirection($direction);
1973
1974
    switch ($direction) {
1975 18
      case 'desc':
1976 18
      case SORT_DESC:
1977 6
        krsort($elements, $strategy);
1978 6
        break;
1979 13
      case 'asc':
1980 13
      case SORT_ASC:
1981 13
      default:
1982 13
        ksort($elements, $strategy);
1983 13
    }
1984 18
  }
1985
1986
  /**
1987
   * Get correct PHP constant for direction.
1988
   *
1989
   * @param int|string $direction
1990
   *
1991
   * @return int
1992
   */
1993 38
  protected function getDirection($direction)
1994
  {
1995 38
    if (is_string($direction)) {
1996 10
      $direction = strtolower($direction);
1997
1998 10
      if ($direction === 'desc') {
1999 2
        $direction = SORT_DESC;
2000 2
      } else {
2001 8
        $direction = SORT_ASC;
2002
      }
2003 10
    }
2004
2005
    if (
2006
        $direction !== SORT_DESC
2007 38
        &&
2008
        $direction !== SORT_ASC
2009 38
    ) {
2010
      $direction = SORT_ASC;
2011
    }
2012
2013 38
    return $direction;
2014
  }
2015
2016
  /**
2017
   * Sort the current array by value.
2018
   *
2019
   * @param int $direction use SORT_ASC or SORT_DESC
2020
   * @param int $strategy  use e.g.: SORT_REGULAR or SORT_NATURAL
2021
   *
2022
   * @return Arrayy
2023
   */
2024 1
  public function sortValueKeepIndex($direction = SORT_ASC, $strategy = SORT_REGULAR)
2025
  {
2026 1
    return $this->sort($direction, $strategy, true);
2027
  }
2028
2029
  /**
2030
   * Sort the current array and optional you can keep the keys.
2031
   *
2032
   * @param string|int $direction use SORT_ASC or SORT_DESC
2033
   * @param int|string $strategy
2034
   * @param bool       $keepKeys
2035
   *
2036
   * @return Arrayy
2037
   */
2038 19
  public function sort($direction = SORT_ASC, $strategy = SORT_REGULAR, $keepKeys = false)
2039
  {
2040 19
    $this->sorting($this->array, $direction, $strategy, $keepKeys);
2041
2042 19
    return $this;
2043
  }
2044
2045
  /**
2046
   * @param array      &$elements
2047
   * @param int|string $direction
2048
   * @param int        $strategy
2049
   * @param bool       $keepKeys
2050
   */
2051 19
  protected function sorting(array &$elements, $direction = SORT_ASC, $strategy = SORT_REGULAR, $keepKeys = false)
2052
  {
2053 19
    $direction = $this->getDirection($direction);
2054
2055 19
    if (!$strategy) {
2056 19
      $strategy = SORT_REGULAR;
2057 19
    }
2058
2059
    switch ($direction) {
2060 19
      case 'desc':
2061 19
      case SORT_DESC:
2062 9
        if ($keepKeys) {
2063 5
          arsort($elements, $strategy);
2064 5
        } else {
2065 4
          rsort($elements, $strategy);
2066
        }
2067 9
        break;
2068 10
      case 'asc':
2069 10
      case SORT_ASC:
2070 10
      default:
2071 10
        if ($keepKeys) {
2072 4
          asort($elements, $strategy);
2073 4
        } else {
2074 6
          sort($elements, $strategy);
2075
        }
2076 10
    }
2077 19
  }
2078
2079
  /**
2080
   * Sort the current array by value.
2081
   *
2082
   * @param int $direction use SORT_ASC or SORT_DESC
2083
   * @param int $strategy  use e.g.: SORT_REGULAR or SORT_NATURAL
2084
   *
2085
   * @return Arrayy
2086
   */
2087 1
  public function sortValueNewIndex($direction = SORT_ASC, $strategy = SORT_REGULAR)
2088
  {
2089 1
    return $this->sort($direction, $strategy, false);
2090
  }
2091
2092
  /**
2093
   * Sort a array by value, by a closure or by a property.
2094
   *
2095
   * - If the sorter is null, the array is sorted naturally.
2096
   * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
2097
   *
2098
   * @param null       $sorter
2099
   * @param string|int $direction
2100
   * @param int        $strategy
2101
   *
2102
   * @return Arrayy
2103
   */
2104 1
  public function sorter($sorter = null, $direction = SORT_ASC, $strategy = SORT_REGULAR)
2105
  {
2106 1
    $array = (array)$this->array;
2107 1
    $direction = $this->getDirection($direction);
2108
2109
    // Transform all values into their results.
2110 1
    if ($sorter) {
2111 1
      $arrayy = new self($array);
2112
2113 1
      $that = $this;
2114 1
      $results = $arrayy->each(
2115
          function ($value) use ($sorter, $that) {
2116 1
            return is_callable($sorter) ? $sorter($value) : $that->get($sorter, null, $value);
2117
          }
2118 1
      );
2119
2120 1
      $results = $results->getArray();
2121 1
    } else {
2122 1
      $results = $array;
2123
    }
2124
2125
    // Sort by the results and replace by original values
2126 1
    array_multisort($results, $direction, $strategy, $array);
2127
2128 1
    return static::create($array);
2129
  }
2130
2131
  /**
2132
   * Exchanges all keys with their associated values in an array.
2133
   *
2134
   * @return Arrayy
2135
   */
2136 1
  public function flip()
2137
  {
2138 1
    $this->array = array_flip($this->array);
2139
2140 1
    return static::create($this->array);
2141
  }
2142
2143
  /**
2144
   * Apply the given function to every element in the array,
2145
   * discarding the results.
2146
   *
2147
   * @param callable $callable
2148
   * @param bool     $recursive Whether array will be walked recursively or no
2149
   *
2150
   * @return Arrayy An Arrayy object with modified elements
2151
   */
2152 8
  public function walk($callable, $recursive = false)
2153
  {
2154 8
    if (true === $recursive) {
2155 4
      array_walk_recursive($this->array, $callable);
2156 4
    } else {
2157 4
      array_walk($this->array, $callable);
2158
    }
2159
2160 8
    return $this;
2161
  }
2162
2163
  /**
2164
   * Reduce the current array via callable e.g. anonymous-function.
2165
   *
2166
   * @param mixed $predicate
2167
   * @param array $init
2168
   *
2169
   * @return Arrayy
2170
   */
2171 2
  public function reduce($predicate, array $init = array())
2172
  {
2173 2
    $this->array = array_reduce($this->array, $predicate, $init);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_reduce($this->array, $predicate, $init) 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...
2174
2175 2
    return static::create($this->array);
2176
  }
2177
2178
  /**
2179
   * Return a duplicate free copy of the current array.
2180
   *
2181
   * @return Arrayy
2182
   */
2183 7
  public function unique()
2184
  {
2185 7
    $this->array = array_reduce(
0 ignored issues
show
Documentation Bug introduced by
It seems like array_reduce($this->arra...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...
2186 7
        $this->array,
2187 7
        function ($resultArray, $value) {
2188 6
          if (in_array($value, $resultArray, true) === false) {
2189 6
            $resultArray[] = $value;
2190 6
          }
2191
2192 6
          return $resultArray;
2193 7
        },
2194 7
        array()
2195 7
    );
2196
2197 7
    return static::create($this->array);
2198
  }
2199
2200
  /**
2201
   * Convert the current array to JSON.
2202
   *
2203
   * @param null $options e.g. JSON_PRETTY_PRINT
2204
   *
2205
   * @return string
2206
   */
2207 1
  public function toJson($options = null)
2208
  {
2209 1
    return UTF8::json_encode($this->array, $options);
2210
  }
2211
}
2212