These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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): bool |
|
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|int $key |
||
34 | * @param mixed $default |
||
35 | * |
||
36 | * @return mixed |
||
37 | */ |
||
38 | 3 | public static function pull(array &$array, $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('.', (string) $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|int|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('.', (string) $key) as $segment) { |
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); |
|
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) { |
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 sting $key |
||
0 ignored issues
–
show
|
|||
223 | * @param callable $callback Callback to update the value. |
||
224 | * |
||
225 | * @return mixed Updated data. |
||
226 | */ |
||
227 | 1 | public static function update(array $array, string $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('.', (string) $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 string $value |
||
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 |
||
443 | * @param string|int $swapB |
||
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 | self::recurseCollapse($value, $newArray, (array) $key); |
|
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 | return (bool) trim($item); |
|
565 | 1 | }); |
|
566 | } |
||
567 | |||
568 | /** |
||
569 | * Remove all instances of $ignore found in $elements (=== is used). |
||
570 | * |
||
571 | * @param array $array |
||
572 | * @param array $ignore |
||
573 | * |
||
574 | * @return array |
||
575 | */ |
||
576 | 1 | public static function without(array $array, array $ignore): array |
|
577 | { |
||
578 | 1 | foreach ($array as $key => $node) { |
|
579 | 1 | if (in_array($node, $ignore, true)) { |
|
580 | 1 | unset($array[$key]); |
|
581 | } |
||
582 | } |
||
583 | |||
584 | 1 | return array_values($array); |
|
585 | } |
||
586 | |||
587 | /** |
||
588 | * Reindexes a list of values. |
||
589 | * |
||
590 | * @param array $array |
||
591 | * @param array $map An map of correspondances of the form |
||
592 | * ['currentIndex' => 'newIndex']. |
||
593 | * @param bool $unmapped Whether or not to keep keys that are not |
||
594 | * remapped. |
||
595 | * |
||
596 | * @return array |
||
597 | */ |
||
598 | 1 | public static function reindex(array $array, array $map, bool $unmapped = true): array |
|
599 | { |
||
600 | 1 | $reindexed = $unmapped |
|
601 | 1 | ? $array |
|
602 | 1 | : []; |
|
603 | |||
604 | 1 | foreach ($map as $from => $to) { |
|
605 | 1 | if (isset($array[$from])) { |
|
606 | 1 | $reindexed[$to] = $array[$from]; |
|
607 | } |
||
608 | } |
||
609 | |||
610 | 1 | return $reindexed; |
|
611 | } |
||
612 | |||
613 | /** |
||
614 | * Merges two or more arrays into one recursively. |
||
615 | * |
||
616 | * @return array |
||
617 | */ |
||
618 | 4 | public static function merge(): array |
|
619 | { |
||
620 | 4 | $args = func_get_args(); |
|
621 | 4 | $array = array_shift($args); |
|
622 | |||
623 | 4 | while (! empty($args)) { |
|
624 | 4 | $next = array_shift($args); |
|
625 | |||
626 | 4 | foreach ($next as $key => $value) { |
|
627 | 4 | if (is_int($key)) { |
|
628 | 3 | if (isset($array[$key])) { |
|
629 | 2 | $array[] = $value; |
|
630 | } else { |
||
631 | 3 | $array[$key] = $value; |
|
632 | } |
||
633 | 2 | } elseif (is_array($value) && isset($array[$key]) && is_array($array[$key])) { |
|
634 | 1 | $array[$key] = static::merge($array[$key], $value); |
|
635 | } else { |
||
636 | 4 | $array[$key] = $value; |
|
637 | } |
||
638 | } |
||
639 | } |
||
640 | |||
641 | 4 | return $array; |
|
642 | } |
||
643 | |||
644 | /** |
||
645 | * Makes every value that is numerically indexed a key, given $default |
||
646 | * as value. |
||
647 | * |
||
648 | * @param array $array |
||
649 | * @param mixed $default |
||
650 | * |
||
651 | * @return array |
||
652 | */ |
||
653 | 1 | public static function normalize(array $array, $default): array |
|
654 | { |
||
655 | 1 | $normalized = []; |
|
656 | |||
657 | 1 | foreach ($array as $key => $value) { |
|
658 | 1 | if (is_numeric($key)) { |
|
659 | 1 | $key = $value; |
|
660 | 1 | $value = $default; |
|
661 | } |
||
662 | |||
663 | 1 | $normalized[$key] = $value; |
|
664 | } |
||
665 | |||
666 | 1 | return $normalized; |
|
667 | } |
||
668 | |||
669 | /** |
||
670 | * Extend one array with another. |
||
671 | * |
||
672 | * @return array |
||
673 | */ |
||
674 | 3 | public static function extend(): array |
|
675 | { |
||
676 | 3 | $merged = []; |
|
677 | |||
678 | 3 | foreach (func_get_args() as $array) { |
|
679 | 3 | foreach ($array as $key => $value) { |
|
680 | 3 | if (is_array($value) && static::has($merged, $key) && is_array($merged[$key])) { |
|
681 | 2 | $merged[$key] = static::extend($merged[$key], $value); |
|
682 | } else { |
||
683 | 3 | $merged[$key] = $value; |
|
684 | } |
||
685 | } |
||
686 | } |
||
687 | |||
688 | 3 | return $merged; |
|
689 | } |
||
690 | |||
691 | /** |
||
692 | * Transforms a 1-dimensional array into a multi-dimensional one, |
||
693 | * exploding keys according to a separator. |
||
694 | * |
||
695 | * @param array $array |
||
696 | * |
||
697 | * @return array |
||
698 | */ |
||
699 | 1 | public static function asHierarchy(array $array): array |
|
700 | { |
||
701 | 1 | $hierarchy = []; |
|
702 | |||
703 | 1 | foreach ($array as $key => $value) { |
|
704 | 1 | $segments = explode('.', (string) $key); |
|
705 | 1 | $valueSegment = array_pop($segments); |
|
706 | 1 | $branch = &$hierarchy; |
|
707 | |||
708 | 1 | foreach ($segments as $segment) { |
|
709 | 1 | if (! isset($branch[$segment])) { |
|
710 | 1 | $branch[$segment] = []; |
|
711 | } |
||
712 | |||
713 | 1 | $branch = &$branch[$segment]; |
|
714 | } |
||
715 | |||
716 | 1 | $branch[$valueSegment] = $value; |
|
717 | } |
||
718 | |||
719 | 1 | return $hierarchy; |
|
720 | } |
||
721 | |||
722 | /** |
||
723 | * Separates elements from an array into groups. |
||
724 | * The function maps an element to the key that will be used for grouping. |
||
725 | * If no function is passed, the element itself will be used as key. |
||
726 | * |
||
727 | * @param array $array |
||
728 | * @param callable|null $callback |
||
729 | * |
||
730 | * @return array |
||
731 | */ |
||
732 | 1 | public static function groupBy(array $array, callable $callback = null): array |
|
733 | { |
||
734 | $callback = $callback ?: function ($value) { |
||
735 | 1 | return $value; |
|
736 | 1 | }; |
|
737 | |||
738 | 1 | return array_reduce( |
|
739 | $array, |
||
740 | function ($buckets, $value) use ($callback) { |
||
741 | 1 | $key = call_user_func($callback, $value); |
|
742 | |||
743 | 1 | if (! array_key_exists($key, $buckets)) { |
|
744 | 1 | $buckets[$key] = []; |
|
745 | } |
||
746 | |||
747 | 1 | $buckets[$key][] = $value; |
|
748 | |||
749 | 1 | return $buckets; |
|
750 | 1 | }, |
|
751 | 1 | [] |
|
752 | ); |
||
753 | } |
||
754 | |||
755 | /** |
||
756 | * Flatten a multi-dimensional associative array with dots. |
||
757 | * |
||
758 | * @param array $array |
||
759 | * @param string $prepend |
||
760 | * |
||
761 | * @return array |
||
762 | */ |
||
763 | 4 | public static function dot(array $array, string $prepend = ''): array |
|
764 | { |
||
765 | 4 | $cache = serialize(['array' => $array, 'prepend' => $prepend]); |
|
766 | |||
767 | 4 | if (array_key_exists($cache, self::$dotted)) { |
|
768 | 1 | return self::$dotted[$cache]; |
|
769 | } |
||
770 | |||
771 | 3 | $results = []; |
|
772 | |||
773 | 3 | View Code Duplication | foreach ($array as $key => $value) { |
774 | 3 | if (is_array($value)) { |
|
775 | 2 | $results = array_merge($results, static::dot($value, $prepend . $key . '.')); |
|
776 | } else { |
||
777 | 3 | $results[$prepend . $key] = $value; |
|
778 | } |
||
779 | } |
||
780 | |||
781 | 3 | return self::$dotted[$cache] = $results; |
|
782 | } |
||
783 | |||
784 | /** |
||
785 | * Expand a dotted array. Acts the opposite way of Arr::dot(). |
||
786 | * |
||
787 | * @param array $array |
||
788 | * @param int|float $depth |
||
789 | * |
||
790 | * @return array |
||
791 | */ |
||
792 | 10 | public static function unDot(array $array, $depth = INF): array |
|
793 | { |
||
794 | 10 | $results = []; |
|
795 | |||
796 | 10 | foreach ($array as $key => $value) { |
|
797 | 9 | if (count($dottedKeys = explode('.', (string) $key, 2)) > 1) { |
|
798 | 9 | $results[$dottedKeys[0]][$dottedKeys[1]] = $value; |
|
799 | } else { |
||
800 | 9 | $results[$key] = $value; |
|
801 | } |
||
802 | } |
||
803 | |||
804 | 10 | foreach ($results as $key => $value) { |
|
805 | 9 | if (is_array($value) && ! empty($value) && $depth > 1) { |
|
806 | 9 | $results[$key] = static::unDot($value, $depth - 1); |
|
807 | } |
||
808 | } |
||
809 | |||
810 | 10 | return $results; |
|
811 | } |
||
812 | |||
813 | /** |
||
814 | * Flatten a nested array to a separated key. |
||
815 | * |
||
816 | * @param array $array |
||
817 | * @param string|null $separator |
||
818 | * @param string $prepend |
||
819 | * |
||
820 | * @return array |
||
821 | */ |
||
822 | 1 | public static function flatten(array $array, string $separator = null, string $prepend = ''): array |
|
823 | { |
||
824 | 1 | $flattened = []; |
|
825 | |||
826 | 1 | View Code Duplication | foreach ($array as $key => $value) { |
827 | 1 | if (is_array($value)) { |
|
828 | 1 | $flattened = array_merge($flattened, static::flatten($value, $separator, $prepend . $key . $separator)); |
|
829 | } else { |
||
830 | 1 | $flattened[$prepend . $key] = $value; |
|
831 | } |
||
832 | } |
||
833 | |||
834 | 1 | return $flattened; |
|
835 | } |
||
836 | |||
837 | /** |
||
838 | * Expand a flattened array with dots to a multi-dimensional associative array. |
||
839 | * |
||
840 | * @param array $array |
||
841 | * @param string $prepend |
||
842 | * |
||
843 | * @return array |
||
844 | */ |
||
845 | 4 | public static function expand(array $array, string $prepend = ''): array |
|
846 | { |
||
847 | 4 | $results = []; |
|
848 | |||
849 | 4 | if ($prepend) { |
|
850 | 2 | $prepend .= '.'; |
|
851 | } |
||
852 | |||
853 | 4 | foreach ($array as $key => $value) { |
|
854 | 4 | if ($prepend) { |
|
855 | 2 | $pos = strpos($key, $prepend); |
|
856 | |||
857 | 2 | if ($pos === 0) { |
|
858 | 1 | $key = substr($key, strlen($prepend)); |
|
859 | } |
||
860 | } |
||
861 | |||
862 | 4 | $results = static::set($results, $key, $value); |
|
863 | } |
||
864 | |||
865 | 4 | return $results; |
|
866 | } |
||
867 | |||
868 | /** |
||
869 | * Reset all numerical indexes of an array (start from zero). |
||
870 | * Non-numerical indexes will stay untouched. Returns a new array. |
||
871 | * |
||
872 | * @param array $array |
||
873 | * @param bool|false $deep |
||
874 | * |
||
875 | * @return array |
||
876 | */ |
||
877 | 3 | public static function reset(array $array, $deep = false) |
|
878 | { |
||
879 | 3 | $target = []; |
|
880 | |||
881 | 3 | foreach ($array as $key => $value) { |
|
882 | 3 | if ($deep && is_array($value)) { |
|
883 | 1 | $value = static::reset($value); |
|
884 | } |
||
885 | |||
886 | 3 | if (is_numeric($key)) { |
|
887 | 3 | $target[] = $value; |
|
888 | } else { |
||
889 | 3 | $target[$key] = $value; |
|
890 | } |
||
891 | } |
||
892 | |||
893 | 3 | return $target; |
|
894 | } |
||
895 | |||
896 | /** |
||
897 | * Extend one array with another. Non associative arrays will not be merged |
||
898 | * but rather replaced. |
||
899 | * |
||
900 | * @return array |
||
901 | */ |
||
902 | 4 | public static function extendDistinct() |
|
903 | { |
||
904 | 4 | $merged = []; |
|
905 | |||
906 | 4 | foreach (func_get_args() as $array) { |
|
907 | 4 | foreach ($array as $key => $value) { |
|
908 | 4 | if (is_array($value) && static::has($merged, $key) && is_array($merged[$key])) { |
|
909 | 3 | if (static::isAssoc($value) && static::isAssoc($merged[$key])) { |
|
910 | 2 | $merged[$key] = static::extendDistinct($merged[$key], $value); |
|
911 | |||
912 | 2 | continue; |
|
913 | } |
||
914 | } |
||
915 | |||
916 | 4 | $merged[$key] = $value; |
|
917 | } |
||
918 | } |
||
919 | |||
920 | 4 | return $merged; |
|
921 | } |
||
922 | |||
923 | /** |
||
924 | * Sort the array using the given callback. |
||
925 | * |
||
926 | * @param array $array |
||
927 | * @param callable $callback |
||
928 | * @param int $options |
||
929 | * @param bool $descending |
||
930 | * |
||
931 | * @return array |
||
932 | */ |
||
933 | 1 | public static function sort( |
|
934 | array $array, |
||
935 | callable $callback, |
||
936 | int $options = SORT_REGULAR, |
||
937 | bool $descending = false |
||
938 | ): array { |
||
939 | 1 | $results = []; |
|
940 | |||
941 | // First we will loop through the items and get the comparator from a callback |
||
942 | // function which we were given. Then, we will sort the returned values and |
||
943 | // and grab the corresponding values for the sorted keys from this array. |
||
944 | 1 | foreach ($array as $key => $value) { |
|
945 | 1 | $results[$key] = $callback($value, $key); |
|
946 | } |
||
947 | |||
948 | 1 | $descending ? arsort($results, $options) : asort($results, $options); |
|
949 | |||
950 | // Once we have sorted all of the keys in the array, we will loop through them |
||
951 | // and grab the corresponding model so we can set the underlying items list |
||
952 | // to the sorted version. Then we'll just return the collection instance. |
||
953 | 1 | foreach (array_keys($results) as $key) { |
|
954 | 1 | $results[$key] = $array[$key]; |
|
955 | } |
||
956 | |||
957 | 1 | return $results; |
|
958 | } |
||
959 | |||
960 | /** |
||
961 | * Recursively sort an array by keys and values. |
||
962 | * |
||
963 | * @param array $array |
||
964 | * |
||
965 | * @return array |
||
966 | */ |
||
967 | 1 | public static function sortRecursive(array $array) |
|
968 | { |
||
969 | 1 | foreach ($array as &$value) { |
|
970 | 1 | if (is_array($value)) { |
|
971 | 1 | $value = static::sortRecursive($value); |
|
972 | } |
||
973 | } |
||
974 | |||
975 | // sort associative array |
||
976 | 1 | if (static::isAssoc($array)) { |
|
977 | 1 | ksort($array); |
|
978 | // sort regular array |
||
979 | } else { |
||
980 | 1 | sort($array); |
|
981 | } |
||
982 | |||
983 | 1 | return $array; |
|
984 | } |
||
985 | |||
986 | /** |
||
987 | * Will turn each element in $arr into an array then appending |
||
988 | * the associated indexs from the other arrays into this array as well. |
||
989 | * |
||
990 | * @return array<*,array> |
||
991 | */ |
||
992 | 2 | public static function zip() |
|
993 | { |
||
994 | 2 | $args = func_get_args(); |
|
995 | 2 | $originalArr = $args[0]; |
|
996 | |||
997 | 2 | array_shift($args); |
|
998 | |||
999 | 2 | $array = []; |
|
1000 | |||
1001 | 2 | foreach ($originalArr as $key => $value) { |
|
1002 | 2 | $array[$key] = [$value]; |
|
1003 | |||
1004 | 2 | foreach ($args as $k => $v) { |
|
1005 | 2 | $array[$key][] = current($args[$k]); |
|
1006 | |||
1007 | 2 | if (next($args[$k]) === false && $args[$k] !== [null]) { |
|
1008 | 2 | $args[$k] = [null]; |
|
1009 | } |
||
1010 | } |
||
1011 | } |
||
1012 | |||
1013 | 2 | return $array; |
|
1014 | } |
||
1015 | |||
1016 | /** |
||
1017 | * Applies the callback to the elements of the given arrays |
||
1018 | * |
||
1019 | * @param array $array |
||
1020 | * @param callable $callback |
||
1021 | * |
||
1022 | * @return array |
||
1023 | */ |
||
1024 | 1 | public static function map(array $array, callable $callback) |
|
1025 | { |
||
1026 | 1 | $newArray = []; |
|
1027 | |||
1028 | 1 | foreach ($array as $key => $item) { |
|
1029 | 1 | $result = call_user_func($callback, $item, $key); |
|
1030 | |||
1031 | 1 | $newArray = is_array($result) ? |
|
1032 | 1 | array_replace_recursive($array, $result) : |
|
1033 | 1 | array_merge_recursive($array, (array) $result); |
|
1034 | } |
||
1035 | |||
1036 | 1 | return $newArray; |
|
1037 | } |
||
1038 | |||
1039 | /** |
||
1040 | * Filters each of the given values through a function. |
||
1041 | * |
||
1042 | * @param array $array |
||
1043 | * @param callable $callback |
||
1044 | * |
||
1045 | * @return array |
||
1046 | */ |
||
1047 | 2 | public static function filter(array $array, callable $callback) |
|
1048 | { |
||
1049 | 2 | $newArray = []; |
|
1050 | |||
1051 | 2 | foreach ($array as $key => $item) { |
|
1052 | 2 | if (call_user_func($callback, $item, $key)) { |
|
1053 | 2 | $newArray[$key] = $item; |
|
1054 | } |
||
1055 | } |
||
1056 | |||
1057 | 2 | return $newArray; |
|
1058 | } |
||
1059 | |||
1060 | /** |
||
1061 | * Returns whether every element of the array satisfies the given predicate or not. |
||
1062 | * Works with Iterators too. |
||
1063 | * |
||
1064 | * @param array $array |
||
1065 | * @param callable $predicate |
||
1066 | * |
||
1067 | * @return bool |
||
1068 | */ |
||
1069 | 1 | public static function all(array $array, callable $predicate) |
|
1070 | { |
||
1071 | 1 | foreach ($array as $key => $value) { |
|
1072 | 1 | if (! call_user_func($predicate, $value, $key, $array)) { |
|
1073 | 1 | return false; |
|
1074 | } |
||
1075 | } |
||
1076 | |||
1077 | 1 | return true; |
|
1078 | } |
||
1079 | |||
1080 | /** |
||
1081 | * The opposite of filter(). |
||
1082 | * |
||
1083 | * @param array $array |
||
1084 | * @param callable $callback Function to filter values. |
||
1085 | * |
||
1086 | * @return array filtered array. |
||
1087 | */ |
||
1088 | public static function reject(array $array, callable $callback): array |
||
1089 | { |
||
1090 | 1 | return static::filter($array, function ($value, $key) use ($callback) { |
|
1091 | 1 | return ! call_user_func($callback, $value, $key); |
|
1092 | 1 | }); |
|
1093 | } |
||
1094 | |||
1095 | /** |
||
1096 | * Filter the array using the given Closure. |
||
1097 | * |
||
1098 | * @param array $array |
||
1099 | * @param callable $callback |
||
1100 | * |
||
1101 | * @return array |
||
1102 | */ |
||
1103 | 1 | public static function where(array $array, callable $callback): array |
|
1104 | { |
||
1105 | 1 | $filtered = []; |
|
1106 | |||
1107 | 1 | foreach ($array as $key => $value) { |
|
1108 | 1 | if (call_user_func($callback, $key, $value)) { |
|
1109 | 1 | $filtered[$key] = $value; |
|
1110 | } |
||
1111 | } |
||
1112 | |||
1113 | 1 | return $filtered; |
|
1114 | } |
||
1115 | |||
1116 | /** |
||
1117 | * Return the first element in an array passing a given truth test. |
||
1118 | * |
||
1119 | * @param array $array |
||
1120 | * @param callable $callback |
||
1121 | * @param mixed $default |
||
1122 | * |
||
1123 | * @return mixed |
||
1124 | */ |
||
1125 | 2 | public static function first(array $array, callable $callback, $default = null) |
|
1126 | { |
||
1127 | 2 | if (is_null($callback)) { |
|
1128 | if (empty($array)) { |
||
1129 | return value($default); |
||
1130 | } |
||
1131 | |||
1132 | foreach ($array as $item) { |
||
1133 | return $item; |
||
1134 | } |
||
1135 | } |
||
1136 | |||
1137 | 2 | foreach ($array as $key => $value) { |
|
1138 | 2 | if (call_user_func($callback, $key, $value)) { |
|
1139 | 2 | return $value; |
|
1140 | } |
||
1141 | } |
||
1142 | |||
1143 | 1 | return static::value($default); |
|
1144 | } |
||
1145 | |||
1146 | /** |
||
1147 | * Return the last element in an array passing a given truth test. |
||
1148 | * |
||
1149 | * @param array $array |
||
1150 | * @param callable $callback |
||
1151 | * @param mixed $default |
||
1152 | * |
||
1153 | * @return mixed |
||
1154 | */ |
||
1155 | 1 | public static function last(array $array, callable $callback, $default = null) |
|
1156 | { |
||
1157 | 1 | if (is_null($callback)) { |
|
1158 | return empty($array) ? static::value($default) : end($array); |
||
1159 | } |
||
1160 | |||
1161 | 1 | return static::first(array_reverse($array), $callback, $default); |
|
1162 | } |
||
1163 | |||
1164 | /** |
||
1165 | * Return the default value of the given value. |
||
1166 | * |
||
1167 | * @param mixed $value |
||
1168 | * |
||
1169 | * @return mixed |
||
1170 | */ |
||
1171 | 10 | public static function value($value) |
|
1172 | { |
||
1173 | 10 | return $value instanceof Closure ? $value() : $value; |
|
1174 | } |
||
1175 | |||
1176 | /** |
||
1177 | * Recurse through an array, add the leaf items to the $newArray var |
||
1178 | * |
||
1179 | * @param array $subject |
||
1180 | * @param array &$newArray |
||
1181 | * @param array $stack |
||
1182 | * |
||
1183 | * @return string[]|null |
||
1184 | */ |
||
1185 | 3 | private static function recurseCollapse(array $subject, array &$newArray, $stack = []) |
|
1186 | { |
||
1187 | 3 | foreach ($subject as $key => $value) { |
|
1188 | 3 | $fstack = array_merge($stack, [$key]); |
|
1189 | |||
1190 | 3 | if (is_array($value)) { |
|
1191 | 3 | self::recurseCollapse($value, $newArray, $fstack); |
|
1192 | } else { |
||
1193 | 3 | $top = array_shift($fstack); |
|
1194 | 3 | $arrayPart = count($fstack) ? '.' . implode('.', $fstack) : ''; |
|
1195 | 3 | $newArray[$top . $arrayPart] = $value; |
|
1196 | } |
||
1197 | } |
||
1198 | 3 | } |
|
1199 | } |
||
1200 |
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.