Test Setup Failed
Branch develop (3bfe0b)
by Daniel
14:58
created

Arr::value()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
crap 2
1
<?php
2
declare(strict_types=1);
3
namespace Narrowspark\Arr;
4
5
use ArrayAccess;
6
use Closure;
7
8
class Arr
9
{
10
    /**
11
     * Dotted array cache.
12
     *
13
     * @var array
14
     */
15
    protected static $dotted = [];
16
17
    /**
18
     * Determine whether the given value is array accessible.
19
     *
20
     * @param mixed $value
21
     *
22
     * @return bool
23
     */
24 8
    public static function accessible($value)
25
    {
26 8
        return is_array($value) || $value instanceof ArrayAccess;
27
    }
28
29
    /**
30
     * Get a value from the array, and remove it.
31
     *
32
     * @param array  $array
33
     * @param string $key
34
     * @param mixed  $default
35
     *
36
     * @return mixed
37
     */
38 3
    public static function pull(array &$array, string $key, $default = null)
39
    {
40 3
        $value = static::get($array, $key, $default);
41
42 3
        static::forget($array, $key);
43
44 3
        return $value;
45
    }
46
47
    /**
48
     * Set an array item to a given value using "dot" notation.
49
     * If no key is given to the method, the entire array will be replaced.
50
     *
51
     * @param array       $array
52
     * @param string|null $key
53
     * @param mixed       $value
54
     *
55
     * @return array
56
     */
57 9
    public static function set(array $array, $key, $value): array
58
    {
59 9
        if ($key === null) {
60 1
            return $value;
61
        }
62
63 9
        $keys = explode('.', $key);
64 9
        $current = &$array;
65
66 9
        while (count($keys) > 1) {
67 7
            $key = array_shift($keys);
68
69
            // If the key doesn't exist at this depth, we will just create an empty array
70
            // to hold the next value, allowing us to create the arrays to hold final
71
            // values at the correct depth. Then we'll keep digging into the array.
72 7
            if (! isset($current[$key]) || ! is_array($current[$key])) {
73 4
                $current[$key] = [];
74
            }
75
76 7
            $current = &$current[$key];
77
        }
78
79 9
        $current[array_shift($keys)] = $value;
80
81 9
        return $array;
82
    }
83
84
    /**
85
     * Get an item from an array using "dot" notation.
86
     * If key dont exist, you get a default value back.
87
     *
88
     * @param array                  $array
89
     * @param string[]|callable|null $key
90
     * @param mixed                  $default
91
     *
92
     * @return mixed
93
     */
94 8
    public static function get(array $array, $key = null, $default = null)
95
    {
96 8
        if ($key === null) {
97 1
            return $array;
98
        }
99
100 8
        if (isset($array[$key])) {
101 5
            return static::value($array[$key]);
102
        }
103
104 4 View Code Duplication
        foreach (explode('.', $key) as $segment) {
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...
105 4
            if (! array_key_exists($segment, $array)) {
106 1
                return static::value($default);
107
            }
108
109 4
            $array = $array[$segment];
110
        }
111
112 3
        return $array;
113
    }
114
115
    /**
116
     * Add an element to the array at a specific location
117
     * using the "dot" notation.
118
     *
119
     * @param array                  $array
120
     * @param string[]|callable|null $key
121
     * @param mixed                  $value
122
     *
123
     * @return array
124
     */
125 4
    public static function add(array $array, $key, $value): array
126
    {
127 4
        $target = static::get($array, $key, []);
128
129 4
        if (! is_array($target)) {
130 2
            $target = [$target];
131
        }
132
133 4
        $target[] = $value;
134 4
        $array = static::set($array, $key, $target);
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 125 can also be of type callable; however, Narrowspark\Arr\Arr::set() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
135
136 4
        return $array;
137
    }
138
139
    /**
140
     * Check if any item or items exist in an array using "dot" notation.
141
     *
142
     * @param array        $array
143
     * @param string|array $keys
144
     *
145
     * @return bool
146
     */
147 1
    public static function any(array $array, $keys): bool
148
    {
149 1
        foreach ((array) $keys as $key) {
150 1
            if (static::has($array, $key)) {
151 1
                return true;
152
            }
153
        }
154
155 1
        return false;
156
    }
157
158
    /**
159
     * Determine if the given key exists in the provided array.
160
     *
161
     * @param \ArrayAccess|array $array
162
     * @param string|int         $key
163
     *
164
     * @return bool
165
     */
166 19
    public static function exists($array, $key): bool
167
    {
168 19
        if ($array instanceof ArrayAccess) {
169
            return $array->offsetExists($key);
170
        }
171
172 19
        return array_key_exists($key, $array);
173
    }
174
175
    /**
176
     * Check if an item exists in an array using "dot" notation.
177
     *
178
     * @param \ArrayAccess|array $array
179
     * @param string|int         $keys
180
     *
181
     * @return bool
182
     */
183 9
    public static function has($array, $keys): bool
184
    {
185 9
        if (is_null($keys)) {
186 2
            return false;
187
        }
188
189 9
        $keys = (array) $keys;
190
191 9
        if (! $array) {
192 4
            return false;
193
        }
194
195 9
        if ($keys === []) {
196
            return false;
197
        }
198
199 9
        foreach ($keys as $key) {
200 9
            $subKeyArray = $array;
201
202 9
            if (static::exists($array, $key)) {
203 7
                continue;
204
            }
205
206 8 View Code Duplication
            foreach (explode('.', (string) $key) as $segment) {
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...
207 8
                if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
208 2
                    $subKeyArray = $subKeyArray[$segment];
209
                } else {
210 8
                    return false;
211
                }
212
            }
213
        }
214
215 7
        return true;
216
    }
217
218
    /**
219
     * Updates data at the given path.
220
     *
221
     * @param array          $array
222
     * @param array|string[] $key
223
     * @param callable       $callback Callback to update the value.
224
     *
225
     * @return mixed Updated data.
226
     */
227 1
    public static function update(array $array, $key, callable $callback)
228
    {
229 1
        $keys = explode('.', $key);
230 1
        $current = &$array;
231
232 1
        foreach ($keys as $key) {
233 1
            if (! isset($current[$key])) {
234 1
                return $array;
235
            }
236
237 1
            $current = &$current[$key];
238
        }
239
240 1
        $current = call_user_func($callback, $current);
241
242 1
        return $array;
243
    }
244
245
    /**
246
     * Remove one or many array items from a given array using "dot" notation.
247
     *
248
     * @param array        $array
249
     * @param array|string $keys
250
     */
251 12
    public static function forget(array &$array, $keys)
252
    {
253 12
        $original = &$array;
254 12
        $keys = (array) $keys;
255
256 12
        if (count($keys) === 0) {
257 2
            return;
258
        }
259
260 10
        foreach ($keys as $key) {
261
            // if the exact key exists in the top-level, remove it
262 10
            if (static::exists($array, $key)) {
263 2
                unset($array[$key]);
264 2
                continue;
265
            }
266
267 8
            $parts = explode('.', $key);
268
            // clean up before each pass
269 8
            $array = &$original;
270
271 8
            while (count($parts) > 1) {
272 8
                $part = array_shift($parts);
273 8
                if (isset($array[$part]) && is_array($array[$part])) {
274 8
                    $array = &$array[$part];
275
                } else {
276 5
                    continue 2;
277
                }
278
            }
279
280 4
            unset($array[array_shift($parts)]);
281
        }
282 10
    }
283
284
    /**
285
     * Get a random element from the array supplied.
286
     *
287
     * @param array $array the source array
288
     *
289
     * @return mixed
290
     */
291 1
    public static function random(array $array)
292
    {
293 1
        if (! count($array)) {
294 1
            return;
295
        }
296
297 1
        $keys = array_rand($array, 1);
298
299 1
        return static::value($array[$keys]);
300
    }
301
302
    /**
303
     * Get a subset of the items from the given array.
304
     *
305
     * @param string[] $array
306
     * @param string[] $keys
307
     *
308
     * @return string[]
309
     */
310 1
    public static function only(array $array, array $keys)
311
    {
312 1
        return array_intersect_key($array, array_flip($keys));
313
    }
314
315
    /**
316
     * Determines if an array is associative.
317
     *
318
     * @param array $array
319
     *
320
     * @return bool
321
     */
322 10
    public static function isAssoc(array $array): bool
323
    {
324 10
        if ($array === []) {
325 1
            return true;
326
        }
327
328 9
        return array_keys($array) !== range(0, count($array) - 1);
329
    }
330
331
    /**
332
     * Split an array in the given amount of pieces.
333
     *
334
     * @param array $array
335
     * @param int   $numberOfPieces
336
     * @param bool  $preserveKeys
337
     *
338
     * @return array
339
     */
340 6
    public static function split(array $array, int $numberOfPieces = 2, bool $preserveKeys = false): array
341
    {
342 6
        if (count($array) === 0) {
343 1
            return [];
344
        }
345
346 5
        $splitSize = ceil(count($array) / $numberOfPieces);
347
348 5
        return array_chunk($array, (int) $splitSize, $preserveKeys);
349
    }
350
351
    /**
352
     * Check if an array has a numeric index.
353
     *
354
     * @param array $array
355
     *
356
     * @return bool
357
     */
358 1
    public static function isIndexed(array $array): bool
359
    {
360 1
        if ($array === []) {
361 1
            return true;
362
        }
363
364 1
        return ! static::isAssoc($array);
365
    }
366
367
    /**
368
     * Push an item onto the beginning of an array.
369
     *
370
     * @param array $array
371
     * @param mixed $value
372
     * @param mixed $key
373
     *
374
     * @return array
375
     */
376 1
    public static function prepend(array $array, $value, $key = null): array
377
    {
378 1
        if (is_null($key)) {
379 1
            array_unshift($array, $value);
380
        } else {
381 1
            $array = [$key => $value] + $array;
382
        }
383
384 1
        return $array;
385
    }
386
387
    /**
388
     * Return the closest found value from array.
389
     *
390
     * @param array $array
391
     * @param sting $value
0 ignored issues
show
Documentation introduced by
Should the type for parameter $value not be string|sting?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
392
     *
393
     * @return mixed
394
     */
395 1
    public static function closest(array $array, string $value)
396
    {
397 1
        sort($array);
398 1
        $closest = $array[0];
399
400 1
        for ($i = 1, $j = count($array), $k = 0; $i < $j; $i++, $k++) {
401 1
            $middleValue = ((int) $array[$i] - (int) $array[$k]) / 2 + (int) $array[$k];
402
403 1
            if ($value >= $middleValue) {
404 1
                $closest = $array[$i];
405
            }
406
        }
407
408 1
        return static::value($closest);
409
    }
410
411
    /**
412
     * Pop value from sub array.
413
     *
414
     * @param array  $array
415
     * @param string $key
416
     *
417
     * @return mixed
418
     */
419 1
    public static function pop(array $array, string $key)
420
    {
421 1
        $keys = explode('.', $key);
422
423 1
        foreach ($keys as $key) {
424 1
            if (! isset($array[$key])) {
425 1
                return;
426
            }
427
428 1
            $array = $array[$key];
429
        }
430
431 1
        if (! is_array($array)) {
432 1
            return;
433
        }
434
435 1
        return array_pop($array);
436
    }
437
438
    /**
439
     * Swap two elements between positions.
440
     *
441
     * @param array      $array array to swap
442
     * @param string<int $swapA
0 ignored issues
show
Documentation introduced by
The doc-type string<int could not be parsed: Expected "|" or "end of type", but got "<" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
443
     * @param string<int $swapB
0 ignored issues
show
Documentation introduced by
The doc-type string<int could not be parsed: Expected "|" or "end of type", but got "<" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
444
     *
445
     * @return array|null
446
     */
447 1
    public static function swap(array $array, $swapA, $swapB)
448
    {
449 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
450
451 1
        return $array;
452
    }
453
454
    /**
455
     * Create a new array consisting of every n-th element.
456
     *
457
     * @param array $array
458
     * @param int   $step
459
     * @param int   $offset
460
     *
461
     * @return array
462
     */
463 1
    public static function every(array $array, int $step, int $offset = 0): array
464
    {
465 1
        $new = [];
466
467 1
        $position = 0;
468
469 1
        foreach ($array as $key => $item) {
470 1
            if ($position % $step === $offset) {
471 1
                $new[] = $item;
472
            }
473
474 1
            ++$position;
475
        }
476
477 1
        return $new;
478
    }
479
480
    /**
481
     * Indexes an array depending on the values it contains.
482
     *
483
     * @param array    $array
484
     * @param callable $callback  Function to combine values.
485
     * @param bool     $overwrite Should duplicate keys be overwritten?
486
     *
487
     * @return array Indexed values.
488
     */
489 1
    public static function combine(array $array, callable $callback, bool $overwrite = true): array
490
    {
491 1
        $combined = [];
492
493 1
        foreach ($array as $key => $value) {
494 1
            $combinator = call_user_func($callback, $value, $key);
495
496
            // fix for hhvm #1871 bug
497 1
            if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.10.0', '<=')) {
498
                $combinator->next();
499
            }
500
501 1
            $index = $combinator->key();
502
503 1
            if ($overwrite || ! isset($combined[$index])) {
504 1
                $combined[$index] = $combinator->current();
505
            }
506
        }
507
508 1
        return $combined;
509
    }
510
511
    /**
512
     * Collapse a nested array down to an array of flat key=>value pairs.
513
     *
514
     * @param array $array
515
     *
516
     * @return array
517
     */
518 3
    public static function collapse(array $array): array
519
    {
520 3
        $newArray = [];
521
522 3
        foreach ($array as $key => $value) {
523 3
            if (is_array($value)) {
524
                // strip any manually added '.'
525 3
                if (preg_match('/\./', $key)) {
526 2
                    $key = substr($key, 0, -2);
527
                }
528
529 3
                static::recurseCollapse($value, $newArray, (array) $key);
0 ignored issues
show
Bug introduced by
Since recurseCollapse() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of recurseCollapse() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
530
            } else {
531 3
                $newArray[$key] = $value;
532
            }
533
        }
534
535 3
        return $newArray;
536
    }
537
538
    /**
539
     * Divide an array into two arrays. One with keys and the other with values.
540
     *
541
     * @param array $array
542
     *
543
     * @return array
544
     */
545 1
    public static function divide(array $array): array
546
    {
547 1
        return [array_keys($array), array_values($array)];
548
    }
549
550
    /**
551
     * Stripe all empty items.
552
     *
553
     * @param array $array
554
     *
555
     * @return array
556
     */
557 1
    public static function stripEmpty(array $array): array
558
    {
559
        return array_filter($array, function ($item) {
560 1
            if (is_null($item)) {
561 1
                return false;
562
            }
563
564 1
            if (! trim($item)) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return (bool) trim($item);.
Loading history...
565 1
                return false;
566
            }
567
568 1
            return true;
569 1
        });
570
    }
571
572
    /**
573
     * Remove all instances of $ignore found in $elements (=== is used).
574
     *
575
     * @param array $array
576
     * @param array $ignore
577
     *
578
     * @return array
579
     */
580 1
    public static function without(array $array, array $ignore): array
581
    {
582 1
        foreach ($array as $key => $node) {
583 1
            if (in_array($node, $ignore, true)) {
584 1
                unset($array[$key]);
585
            }
586
        }
587
588 1
        return array_values($array);
589
    }
590
591
    /**
592
     * Reindexes a list of values.
593
     *
594
     * @param array $array
595
     * @param array $map      An map of correspondances of the form
596
     *                        ['currentIndex' => 'newIndex'].
597
     * @param bool  $unmapped Whether or not to keep keys that are not
598
     *                        remapped.
599
     *
600
     * @return array
601
     */
602 1
    public static function reindex(array $array, array $map, bool $unmapped = true): array
603
    {
604 1
        $reindexed = $unmapped
605 1
            ? $array
606 1
            : [];
607
608 1
        foreach ($map as $from => $to) {
609 1
            if (isset($array[$from])) {
610 1
                $reindexed[$to] = $array[$from];
611
            }
612
        }
613
614 1
        return $reindexed;
615
    }
616
617
    /**
618
     * Merges two or more arrays into one recursively.
619
     *
620
     * @param array $arrays.
0 ignored issues
show
Documentation introduced by
There is no parameter named $arrays.. Did you maybe mean $arrays?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
621
     *
622
     * @return array
623
     */
624 4
    public static function merge(array $arrays): array
0 ignored issues
show
Unused Code introduced by
The parameter $arrays is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
625
    {
626 4
        $args = func_get_args();
627 4
        $array = array_shift($args);
628
629 4
        while (! empty($args)) {
630 4
            $next = array_shift($args);
631
632 4
            foreach ($next as $key => $value) {
633 4
                if (is_int($key)) {
634 3
                    if (isset($array[$key])) {
635 2
                        $array[] = $value;
636
                    } else {
637 3
                        $array[$key] = $value;
638
                    }
639 2
                } elseif (is_array($value) && isset($array[$key]) && is_array($array[$key])) {
640 1
                    $array[$key] = static::merge($array[$key], $value);
641
                } else {
642 4
                    $array[$key] = $value;
643
                }
644
            }
645
        }
646
647 4
        return $array;
648
    }
649
650
    /**
651
     *  Makes every value that is numerically indexed a key, given $default
652
     *  as value.
653
     *
654
     *  @param array $array
655
     *  @param mixed $default
656
     *
657
     *  @return array
658
     */
659 1
    public static function normalize(array $array, $default): array
660
    {
661 1
        $normalized = [];
662
663 1
        foreach ($array as $key => $value) {
664 1
            if (is_numeric($key)) {
665 1
                $key = $value;
666 1
                $value = $default;
667
            }
668
669 1
            $normalized[$key] = $value;
670
        }
671
672 1
        return $normalized;
673
    }
674
675
    /**
676
     * Extend one array with another.
677
     *
678
     * @param array $arrays
679
     *
680
     * @return array
681
     */
682 3
    public static function extend(array $arrays): array
0 ignored issues
show
Unused Code introduced by
The parameter $arrays is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
683
    {
684 3
        $merged = [];
685
686 3
        foreach (func_get_args() as $array) {
687 3
            foreach ($array as $key => $value) {
688 3
                if (is_array($value) && static::has($merged, $key) && is_array($merged[$key])) {
689 2
                    $merged[$key] = static::extend($merged[$key], $value);
690
                } else {
691 3
                    $merged[$key] = $value;
692
                }
693
            }
694
        }
695
696 3
        return $merged;
697
    }
698
699
    /**
700
     * Transforms a 1-dimensional array into a multi-dimensional one,
701
     * exploding keys according to a separator.
702
     *
703
     * @param array $array
704
     *
705
     * @return array
706
     */
707 1
    public static function asHierarchy(array $array): array
708
    {
709 1
        $hierarchy = [];
710
711 1
        foreach ($array as $key => $value) {
712 1
            $segments = explode('.', $key);
713 1
            $valueSegment = array_pop($segments);
714 1
            $branch = &$hierarchy;
715
716 1
            foreach ($segments as $segment) {
717 1
                if (! isset($branch[$segment])) {
718 1
                    $branch[$segment] = [];
719
                }
720
721 1
                $branch = &$branch[$segment];
722
            }
723
724 1
            $branch[$valueSegment] = $value;
725
        }
726
727 1
        return $hierarchy;
728
    }
729
730
    /**
731
     * Separates elements from an array into groups.
732
     * The function maps an element to the key that will be used for grouping.
733
     * If no function is passed, the element itself will be used as key.
734
     *
735
     * @param array         $array
736
     * @param callable|null $callback
737
     *
738
     * @return array
739
     */
740 1
    public static function groupBy(array $array, callable $callback = null): array
741
    {
742
        $callback = $callback ?: function ($value) {
743 1
            return $value;
744 1
        };
745
746 1
        return array_reduce(
747
            $array,
748
            function ($buckets, $value) use ($callback) {
749 1
                $key = call_user_func($callback, $value);
750
751 1
                if (! array_key_exists($key, $buckets)) {
752 1
                    $buckets[$key] = [];
753
                }
754
755 1
                $buckets[$key][] = $value;
756
757 1
                return $buckets;
758 1
            },
759 1
            []
760
        );
761
    }
762
763
    /**
764
     * Flatten a multi-dimensional associative array with dots.
765
     *
766
     * @param array  $array
767
     * @param string $prepend
768
     *
769
     * @return array
770
     */
771 4
    public static function dot(array $array, string $prepend = ''): array
772
    {
773 4
        $cache = serialize(['array' => $array, 'prepend' => $prepend]);
774
775 4
        if (array_key_exists($cache, self::$dotted)) {
776 1
            return self::$dotted[$cache];
777
        }
778
779 3
        $results = [];
780
781 3 View Code Duplication
        foreach ($array as $key => $value) {
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...
782 3
            if (is_array($value)) {
783 2
                $results = array_merge($results, static::dot($value, $prepend . $key . '.'));
784
            } else {
785 3
                $results[$prepend . $key] = $value;
786
            }
787
        }
788
789 3
        return self::$dotted[$cache] = $results;
790
    }
791
792
    /**
793
     * Expand a dotted array. Acts the opposite way of Arr::dot().
794
     *
795
     * @param array     $array
796
     * @param int|float $depth
797
     *
798
     * @return array
799
     */
800 10
    public static function unDot(array $array, $depth = INF): array
801
    {
802 10
        $results = [];
803
804 10
        foreach ($array as $key => $value) {
805 9
            if (count($dottedKeys = explode('.', $key, 2)) > 1) {
806 9
                $results[$dottedKeys[0]][$dottedKeys[1]] = $value;
807
            } else {
808 9
                $results[$key] = $value;
809
            }
810
        }
811
812 10
        foreach ($results as $key => $value) {
813 9
            if (is_array($value) && ! empty($value) && $depth > 1) {
814 9
                $results[$key] = static::unDot($value, $depth - 1);
815
            }
816
        }
817
818 10
        return $results;
819
    }
820
821
    /**
822
     * Flatten a nested array to a separated key.
823
     *
824
     * @param array       $array
825
     * @param string|null $separator
826
     * @param string      $prepend
827
     *
828
     * @return array
829
     */
830 1
    public static function flatten(array $array, string $separator = null, string $prepend = ''): array
831
    {
832 1
        $flattened = [];
833
834 1 View Code Duplication
        foreach ($array as $key => $value) {
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...
835 1
            if (is_array($value)) {
836 1
                $flattened = array_merge($flattened, static::flatten($value, $separator, $prepend . $key . $separator));
837
            } else {
838 1
                $flattened[$prepend . $key] = $value;
839
            }
840
        }
841
842 1
        return $flattened;
843
    }
844
845
    /**
846
     * Expand a flattened array with dots to a multi-dimensional associative array.
847
     *
848
     * @param array  $array
849
     * @param string $prepend
850
     *
851
     * @return array
852
     */
853 4
    public static function expand(array $array, string $prepend = ''): array
854
    {
855 4
        $results = [];
856
857 4
        if ($prepend) {
858 2
            $prepend .= '.';
859
        }
860
861 4
        foreach ($array as $key => $value) {
862 4
            if ($prepend) {
863 2
                $pos = strpos($key, $prepend);
864
865 2
                if ($pos === 0) {
866 1
                    $key = substr($key, strlen($prepend));
867
                }
868
            }
869
870 4
            $results = static::set($results, $key, $value);
871
        }
872
873 4
        return $results;
874
    }
875
876
    /**
877
     * Reset all numerical indexes of an array (start from zero).
878
     * Non-numerical indexes will stay untouched. Returns a new array.
879
     *
880
     * @param array      $array
881
     * @param bool|false $deep
882
     *
883
     * @return array
884
     */
885 3
    public static function reset(array $array, $deep = false)
886
    {
887 3
        $target = [];
888
889 3
        foreach ($array as $key => $value) {
890 3
            if ($deep && is_array($value)) {
891 1
                $value = static::reset($value);
892
            }
893
894 3
            if (is_numeric($key)) {
895 3
                $target[] = $value;
896
            } else {
897 3
                $target[$key] = $value;
898
            }
899
        }
900
901 3
        return $target;
902
    }
903
904
    /**
905
     * Extend one array with another. Non associative arrays will not be merged
906
     * but rather replaced.
907
     *
908
     * @param array $arrays
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $arrays a bit more specific; maybe use array[].
Loading history...
909
     *
910
     * @return array
911
     */
912 4
    public static function extendDistinct(...$arrays)
0 ignored issues
show
Unused Code introduced by
The parameter $arrays is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
913
    {
914 4
        $merged = [];
915
916 4
        foreach (func_get_args() as $array) {
917 4
            foreach ($array as $key => $value) {
918 4
                if (is_array($value) && static::has($merged, $key) && is_array($merged[$key])) {
919 3
                    if (static::isAssoc($value) && static::isAssoc($merged[$key])) {
920 2
                        $merged[$key] = static::extendDistinct($merged[$key], $value);
921
922 2
                        continue;
923
                    }
924
                }
925
926 4
                $merged[$key] = $value;
927
            }
928
        }
929
930 4
        return $merged;
931
    }
932
933
    /**
934
     * Sort the array using the given callback.
935
     *
936
     * @param array    $array
937
     * @param callable $callback
938
     * @param int      $options
939
     * @param bool     $descending
940
     *
941
     * @return array
942
     */
943 1
    public static function sort(array $array, callable $callback, int $options = SORT_REGULAR, bool $descending = false): array
944
    {
945 1
        $results = [];
946
947
        // First we will loop through the items and get the comparator from a callback
948
        // function which we were given. Then, we will sort the returned values and
949
        // and grab the corresponding values for the sorted keys from this array.
950 1
        foreach ($array as $key => $value) {
951 1
            $results[$key] = $callback($value, $key);
952
        }
953
954 1
        $descending ? arsort($results, $options) : asort($results, $options);
955
956
        // Once we have sorted all of the keys in the array, we will loop through them
957
        // and grab the corresponding model so we can set the underlying items list
958
        // to the sorted version. Then we'll just return the collection instance.
959 1
        foreach (array_keys($results) as $key) {
960 1
            $results[$key] = $array[$key];
961
        }
962
963 1
        return $results;
964
    }
965
966
    /**
967
     * Recursively sort an array by keys and values.
968
     *
969
     * @param array $array
970
     *
971
     * @return array
972
     */
973 1
    public static function sortRecursive(array $array)
974
    {
975 1
        foreach ($array as &$value) {
976 1
            if (is_array($value)) {
977 1
                $value = static::sortRecursive($value);
978
            }
979
        }
980
981
        // sort associative array
982 1
        if (static::isAssoc($array)) {
983 1
            ksort($array);
984
            // sort regular array
985
        } else {
986 1
            sort($array);
987
        }
988
989 1
        return $array;
990
    }
991
992
    /**
993
     * Will turn each element in $arr into an array then appending
994
     * the associated indexs from the other arrays into this array as well.
995
     *
996
     * @param array $array
997
     * @param array $arrays
998
     *
999
     * @return array
1000
     */
1001 2
    public static function zip(array $array, array $arrays)
0 ignored issues
show
Unused Code introduced by
The parameter $arrays is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1002
    {
1003 2
        $args = func_get_args();
1004 2
        array_shift($args);
1005
1006 2
        foreach ($array as $key => $value) {
1007 2
            $array[$key] = [$value];
1008
1009 2
            foreach ($args as $k => $v) {
1010 2
                $array[$key][] = current($args[$k]);
1011
1012 2
                if (next($args[$k]) === false && $args[$k] !== [null]) {
1013 2
                    $args[$k] = [null];
1014
                }
1015
            }
1016
        }
1017
1018 2
        return $array;
1019
    }
1020
1021
    /**
1022
     * Applies the callback to the elements of the given arrays
1023
     *
1024
     * @param array    $array
1025
     * @param callable $callback
1026
     *
1027
     * @return array
1028
     */
1029 1
    public static function map(array $array, callable $callback)
1030
    {
1031 1
        $newArray = [];
1032
1033 1
        foreach ($array as $key => $item) {
1034 1
            $result = call_user_func($callback, $item, $key);
1035
1036 1
            $newArray = is_array($result) ?
1037 1
                array_replace_recursive($array, $result) :
1038 1
                array_merge_recursive($array, (array) $result);
1039
        }
1040
1041 1
        return $newArray;
1042
    }
1043
1044
    /**
1045
     * Filters each of the given values through a function.
1046
     *
1047
     * @param array    $array
1048
     * @param callable $callback
1049
     *
1050
     * @return array
1051
     */
1052 2
    public static function filter(array $array, callable $callback)
1053
    {
1054 2
        $newArray = [];
1055
1056 2
        foreach ($array as $key => $item) {
1057 2
            if (call_user_func($callback, $item, $key)) {
1058 2
                $newArray[$key] = $item;
1059
            }
1060
        }
1061
1062 2
        return $newArray;
1063
    }
1064
1065
    /**
1066
     * Returns whether every element of the array satisfies the given predicate or not.
1067
     * Works with Iterators too.
1068
     *
1069
     * @param array    $array
1070
     * @param callable $predicate
1071
     *
1072
     * @return bool
1073
     */
1074 1
    public static function all(array $array, callable $predicate)
1075
    {
1076 1
        foreach ($array as $key => $value) {
1077 1
            if (! call_user_func($predicate, $value, $key, $array)) {
1078 1
                return false;
1079
            }
1080
        }
1081
1082 1
        return true;
1083
    }
1084
1085
    /**
1086
     *  The opposite of filter().
1087
     *
1088
     *  @param array    $array
1089
     *  @param callable $callback Function to filter values.
1090
     *
1091
     *  @return array filtered array.
1092
     */
1093
    public static function reject(array $array, callable $callback): array
1094
    {
1095 1
        return static::filter($array, function ($value, $key) use ($callback) {
1096 1
            return ! call_user_func($callback, $value, $key);
1097 1
        });
1098
    }
1099
1100
    /**
1101
     * Filter the array using the given Closure.
1102
     *
1103
     * @param array    $array
1104
     * @param callable $callback
1105
     *
1106
     * @return array
1107
     */
1108 1
    public static function where(array $array, callable $callback): array
1109
    {
1110 1
        $filtered = [];
1111
1112 1
        foreach ($array as $key => $value) {
1113 1
            if (call_user_func($callback, $key, $value)) {
1114 1
                $filtered[$key] = $value;
1115
            }
1116
        }
1117
1118 1
        return $filtered;
1119
    }
1120
1121
    /**
1122
     * Return the first element in an array passing a given truth test.
1123
     *
1124
     * @param array    $array
1125
     * @param callable $callback
1126
     * @param mixed    $default
1127
     *
1128
     * @return mixed
1129
     */
1130 2
    public static function first(array $array, callable $callback, $default = null)
1131
    {
1132 2
        if (is_null($callback)) {
1133
            if (empty($array)) {
1134
                return value($default);
1135
            }
1136
1137
            foreach ($array as $item) {
1138
                return $item;
1139
            }
1140
        }
1141
1142 2
        foreach ($array as $key => $value) {
1143 2
            if (call_user_func($callback, $key, $value)) {
1144 2
                return $value;
1145
            }
1146
        }
1147
1148 1
        return static::value($default);
1149
    }
1150
1151
    /**
1152
     * Return the last element in an array passing a given truth test.
1153
     *
1154
     * @param array    $array
1155
     * @param callable $callback
1156
     * @param mixed    $default
1157
     *
1158
     * @return mixed
1159
     */
1160 1
    public static function last(array $array, callable $callback, $default = null)
1161
    {
1162 1
        if (is_null($callback)) {
1163
            return empty($array) ? static::value($default) : end($array);
1164
        }
1165
1166 1
        return static::first(array_reverse($array), $callback, $default);
1167
    }
1168
1169
    /**
1170
     * Return the default value of the given value.
1171
     *
1172
     * @param mixed $value
1173
     *
1174
     * @return mixed
1175
     */
1176 10
    public static function value($value)
1177
    {
1178 10
        return $value instanceof Closure ? $value() : $value;
1179
    }
1180
1181
    /**
1182
     * Recurse through an array, add the leaf items to the $newArray var
1183
     *
1184
     * @param array $subject
1185
     * @param array &$newArray
1186
     * @param array $stack
1187
     *
1188
     * @return string[]|null
1189
     */
1190 3
    private static function recurseCollapse(array $subject, array &$newArray, $stack = [])
1191
    {
1192 3
        foreach ($subject as $key => $value) {
1193 3
            $fstack = array_merge($stack, [$key]);
1194
1195 3
            if (is_array($value)) {
1196 3
                static::recurseCollapse($value, $newArray, $fstack);
0 ignored issues
show
Bug introduced by
Since recurseCollapse() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of recurseCollapse() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
1197
            } else {
1198 3
                $top = array_shift($fstack);
1199 3
                $arrayPart = count($fstack) ? '.' . implode('.', $fstack) : '';
1200 3
                $newArray[$top . $arrayPart] = $value;
1201
            }
1202
        }
1203 3
    }
1204
}
1205