This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php namespace nyx\utils; |
||
2 | |||
3 | // External dependencies |
||
4 | use nyx\diagnostics; |
||
5 | |||
6 | /** |
||
7 | * Arr |
||
8 | * |
||
9 | * The Arr class provides a few helper methods to make dealing with arrays easier. |
||
10 | * |
||
11 | * All methods which work with string delimited keys accept a string delimiter. If none is given (ie. null is passed), |
||
12 | * the default delimiter (dot) set statically in this class will be used. |
||
13 | * |
||
14 | * Some code in this class can be simplified and some duplication could be avoided but it is laid out so that the |
||
15 | * most common use cases are checked for first with performance being the priority. |
||
16 | * |
||
17 | * Some methods have aliases. To avoid the overhead please use the base methods, not the aliases. The methods which |
||
18 | * have aliases are documented as such and each alias directly points to the base method. |
||
19 | * |
||
20 | * Note: This class is based on Laravel, FuelPHP, Lo-dash and a few others, but certain methods which appear in |
||
21 | * those are not included since they would add overhead we consider 'not worth it' and don't want to encourage |
||
22 | * the use thereof: |
||
23 | * |
||
24 | * - Arr::each() -> use array_map() instead. |
||
25 | * - Arr::filter(), Arr::reject() -> use array_filter() instead. |
||
26 | * - Arr::range() -> use range() instead. |
||
27 | * - Arr::repeat() -> use array_fill() instead. |
||
28 | * - Arr::search() -> use array_search() instead. |
||
29 | * - Arr::shuffle() -> use shuffle() instead. |
||
30 | * - Arr::size() -> use count() instead. |
||
31 | * |
||
32 | * @package Nyx\Utils |
||
33 | * @version 0.1.0 |
||
34 | * @author Michal Chojnacki <[email protected]> |
||
35 | * @copyright 2012-2016 Nyx Dev Team |
||
36 | * @link http://docs.muyo.io/nyx/utils/index.html |
||
37 | * @todo Arr::sort() and Arr:sortBy() (add sortBy() to core\traits\Collection as well). |
||
38 | * @todo Add ArrayObject support? How? Just strip the array type hints so as to not add overhead with checks? |
||
39 | */ |
||
40 | class Arr |
||
41 | { |
||
42 | /** |
||
43 | * The traits of the Arr class. |
||
44 | */ |
||
45 | use traits\StaticallyExtendable; |
||
46 | |||
47 | /** |
||
48 | * @var string The default delimiter to use to separate array dimensions. |
||
49 | */ |
||
50 | public static $delimiter = '.'; |
||
51 | |||
52 | /** |
||
53 | * Adds an element to the given array but only if it does not yet exist. |
||
54 | * |
||
55 | * Note: Null as value of an item is considered a non-existing item for the purposes |
||
56 | * of this method. |
||
57 | * |
||
58 | * @param array $array The array to which the element should be added. |
||
59 | * @param string $key The key at which the value should be added. |
||
60 | * @param mixed $value The value of the element. |
||
61 | * @param string $delimiter The delimiter to use when exploding the key into parts. |
||
62 | */ |
||
63 | public static function add(array& $array, string $key, $value, string $delimiter = null) |
||
64 | { |
||
65 | if (null === static::get($array, $key, null, $delimiter)) { |
||
66 | static::set($array, $key, $value, $delimiter); |
||
67 | } |
||
68 | } |
||
69 | |||
70 | /** |
||
71 | * Checks whether all elements in the given array pass the given truth test. |
||
72 | * |
||
73 | * Aliases: |
||
74 | * - @see Arr::every() |
||
75 | * |
||
76 | * @param array $array The array to traverse. |
||
77 | * @param callable $callback The truth test the elements should pass. |
||
78 | * @return bool True when the elements passed the truth test, false otherwise. |
||
79 | */ |
||
80 | public static function all(array $array, callable $callback) : bool |
||
81 | { |
||
82 | // Map the array and then search for a 'false' boolean. If none is found, |
||
83 | // we assume all elements passed the test. |
||
84 | return !in_array(false, array_map($callback, $array), true); |
||
85 | } |
||
86 | |||
87 | /** |
||
88 | * Checks whether any of the elements in the given array passes the given truth test. |
||
89 | * |
||
90 | * Aliases: |
||
91 | * - @see Arr::some() |
||
92 | * |
||
93 | * @param array $array The array to traverse. |
||
94 | * @param callable $callback The truth test the elements should pass. |
||
95 | * @return bool True when at least on the the elements passed the truth test, false |
||
96 | * otherwise. |
||
97 | */ |
||
98 | public static function any(array $array, callable $callback) : bool |
||
99 | { |
||
100 | // Map the array and then search for a 'true' boolean. If at least one is found, |
||
101 | // we assume at least one element passed the test. |
||
102 | return in_array(true, array_map($callback, $array), true); |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * Returns the average value of the given array. |
||
107 | * |
||
108 | * @param array $array The array to traverse. |
||
109 | * @param int $decimals The number of decimals to return. |
||
110 | * @return float The average value. |
||
111 | */ |
||
112 | public static function average(array $array, int $decimals = 0) : float |
||
113 | { |
||
114 | return round((array_sum($array) / count($array)), $decimals); |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Removes all elements containing falsy values from the given array. The keys are preserved. |
||
119 | * |
||
120 | * See {@link http://php.net/manual/en/language.types.boolean.php} for information on which values evaluate |
||
121 | * to false. |
||
122 | * |
||
123 | * @param array $array The array to traverse. |
||
124 | * @return array The resulting array. |
||
125 | */ |
||
126 | public static function clean(array $array) : array |
||
127 | { |
||
128 | return array_filter($array, function($value) { |
||
129 | return (bool) $value; |
||
130 | }); |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * Collapses an array of arrays into a single array. Not recursive, ie. only the first dimension of arrays |
||
135 | * will be collapsed down. |
||
136 | * |
||
137 | * Standard array_merge() rules apply - @link http://php.net/manual/en/function.array-merge.php - |
||
138 | * non-array values with numeric keys will be appended to the resulting array in the order they are given, while |
||
139 | * non-array values with non-numeric keys will have their keys preserved but the values may be overwritten by |
||
140 | * the nested arrays being collapsed down if those contain values with the same non-numeric keys. Latter arrays |
||
141 | * overwrite previous arrays' keys on collisions. |
||
142 | * |
||
143 | * @param array $array The array to collapse. |
||
144 | * @return array The resulting array. |
||
145 | */ |
||
146 | public static function collapse(array $array) : array |
||
147 | { |
||
148 | $results = []; |
||
149 | |||
150 | foreach ($array as $key => $item) { |
||
151 | |||
152 | // Nested arrays will be merged in (non-recursively). |
||
153 | if (is_array($item)) { |
||
154 | $results = array_merge($results, $item); continue; |
||
155 | } |
||
156 | |||
157 | // Values with numeric keys will be appended in any case. |
||
158 | if (is_int($key)) { |
||
159 | $results[] = $item; continue; |
||
160 | } |
||
161 | |||
162 | // Non-numeric keys. If we've got the given key in $results already, it means it was merged |
||
163 | // in from one of the nested arrays and those are meant to overwrite the initial values on collisions - |
||
164 | // thus we're not going to do anything in that case. |
||
165 | if (!array_key_exists($key, $results)) { |
||
166 | $results[$key] = $item; |
||
167 | } |
||
168 | } |
||
169 | |||
170 | return $results; |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * Checks whether the given value is contained within the given array. Equivalent of a recursive in_array. |
||
175 | * When you are sure you are dealing with a 1-dimensional array, use in_array instead to avoid the overhead. |
||
176 | * |
||
177 | * @param array $haystack The array to search in. |
||
178 | * @param mixed $needle The value to search for. |
||
179 | * @param bool $strict Whether strict equality matches should be performed on the values. |
||
180 | * @return bool True when the value was found in the array, false otherwise. |
||
181 | */ |
||
182 | public static function contains(array $haystack, $needle, bool $strict = true) |
||
183 | { |
||
184 | foreach ($haystack as $value) { |
||
185 | if ((!$strict && $needle == $value) || $needle === $value) { |
||
186 | return true; |
||
187 | } |
||
188 | |||
189 | if (is_array($value) && static::contains($needle, $value, $strict)) { |
||
190 | return true; |
||
191 | } |
||
192 | } |
||
193 | |||
194 | return false; |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Flattens a multi-dimensional array using the given delimiter. |
||
199 | * |
||
200 | * @param array $array The initial array. |
||
201 | * @param string $prepend A string that should be prepended to the keys. |
||
202 | * @param string $delimiter The delimiter to use when exploding the key into parts. |
||
203 | * @return array The resulting array. |
||
204 | */ |
||
205 | public static function delimit(array $array, string $prepend = '', string $delimiter = null) |
||
206 | { |
||
207 | // Results holder. |
||
208 | $results = []; |
||
209 | |||
210 | // Which string delimiter should we use? |
||
211 | if (null === $delimiter) { |
||
212 | $delimiter = static::$delimiter; |
||
213 | } |
||
214 | |||
215 | foreach ($array as $key => $value) { |
||
216 | if (is_array($value) && !empty($value)) { |
||
217 | $results = array_merge($results, static::delimit($value, $prepend.$key.$delimiter)); |
||
218 | } else { |
||
219 | $results[$prepend.$key] = $value; |
||
220 | } |
||
221 | } |
||
222 | |||
223 | return $results; |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Alias for @see Arr::find() |
||
228 | */ |
||
229 | public static function detect(array $array, callable $callback, $default = null) |
||
230 | { |
||
231 | return static::find($array, $callback, $default); |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * Divides an array into two arrays - the first containing the keys, the second containing the values. |
||
236 | * |
||
237 | * @param array $array The initial array. |
||
238 | * @return array The resulting array. |
||
239 | */ |
||
240 | public static function divide(array $array) |
||
241 | { |
||
242 | return [array_keys($array), array_values($array)]; |
||
243 | } |
||
244 | |||
245 | /** |
||
246 | * Alias for @see Arr::all() |
||
247 | */ |
||
248 | public static function every(array $array, callable $callback) |
||
249 | { |
||
250 | return static::all($array, $callback); |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Returns a subset of the given array, containing all keys except for the ones specified. |
||
255 | * |
||
256 | * @param array $array The initial array. |
||
257 | * @param array $keys An array of keys to exclude from the initial array. |
||
258 | * @return array |
||
259 | */ |
||
260 | public static function except(array $array, array $keys) |
||
261 | { |
||
262 | return array_diff_key($array, array_flip($keys)); |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * Fetches a flattened array of an element nested in the initial array. |
||
267 | * |
||
268 | * @param array $array The initial array. |
||
269 | * @param string $key The string delimited key. |
||
270 | * @param string $delimiter The delimiter to use when exploding the key into parts. |
||
271 | * @return array The resulting array. |
||
272 | */ |
||
273 | public static function fetch(array $array, string $key, string $delimiter = null) : array |
||
274 | { |
||
275 | // Results holder. |
||
276 | $results = []; |
||
277 | |||
278 | // Which string delimiter should we use? |
||
279 | if (null === $delimiter) { |
||
280 | $delimiter = static::$delimiter; |
||
281 | } |
||
282 | |||
283 | foreach (explode($delimiter, $key) as $segment) { |
||
284 | $results = []; |
||
285 | |||
286 | foreach ($array as $value) { |
||
287 | $value = (array) $value; |
||
288 | |||
289 | $results[] = $value[$segment]; |
||
290 | } |
||
291 | |||
292 | $array = array_values($results); |
||
293 | } |
||
294 | |||
295 | return array_values($results); |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Returns the first value which passes the given truth test. |
||
300 | * |
||
301 | * Aliases: |
||
302 | * - @see Arr::detect() |
||
303 | * |
||
304 | * @param array $array The array to traverse. |
||
305 | * @param callable $callback The truth test the value should pass. |
||
306 | * @param mixed $default The default value to be returned if none of the elements passes the test. |
||
307 | * @return mixed |
||
308 | */ |
||
309 | public static function find(array $array, callable $callback, $default = null) |
||
310 | { |
||
311 | foreach ($array as $key => $value) { |
||
312 | if ($callback($value, $key)) { |
||
313 | return $value; |
||
314 | } |
||
315 | } |
||
316 | |||
317 | return $default; |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * Returns the first element of the array, |
||
322 | * OR the first $elements of the array when $elements is a positive integer, |
||
323 | * OR the first element which passes the given truth test when $elements is a callable. |
||
324 | * |
||
325 | * Aliases: @see \nyx\utils\Arr::head(), \nyx\utils\Arr::take() |
||
326 | * Opposite: @see \nyx\utils\Arr::last() |
||
327 | * |
||
328 | * @param array $array The array to traverse. |
||
329 | * @param callable|int $elements The truth test the value should pass or an integer denoting how many |
||
330 | * of the initial elements of the array should be returned. |
||
331 | * When not given, the method will return the first element of the array. |
||
332 | * @param mixed $default The default value to be returned if none of the elements passes |
||
333 | * the test or the array is empty. |
||
334 | * @throws \InvalidArgumentException When $elements is an integer smaller than 1. |
||
335 | * @throws \InvalidArgumentException When $elements is neither a valid integer nor a callable. |
||
336 | * @return mixed |
||
337 | */ |
||
338 | View Code Duplication | public static function first(array $array, $elements = null, $default = null) |
|
339 | { |
||
340 | if (empty($array)) { |
||
341 | return $default; |
||
342 | } |
||
343 | |||
344 | // Most common use case - simply return the first value of the array. |
||
345 | if (!isset($elements) || $elements === 1) { |
||
346 | return reset($array); |
||
347 | } |
||
348 | |||
349 | // With a integer given, return a slice containing the first $callback elements. |
||
350 | if (is_int($elements)) { |
||
351 | |||
352 | if ($elements < 1) { |
||
353 | throw new \InvalidArgumentException("At least 1 element must be requested, while [$elements] were requested."); |
||
354 | } |
||
355 | |||
356 | return array_slice($array, 0, $elements); |
||
357 | } |
||
358 | |||
359 | // With a callable given, return the first value which passes the given truth test. |
||
360 | if (is_callable($elements)) { |
||
361 | return static::find($array, $elements, $default); |
||
362 | } |
||
363 | |||
364 | throw new \InvalidArgumentException('Expected $callback to be a positive integer or a callable, got ['.diagnostics\Debug::getTypeName($elements).'] instead.'); |
||
365 | } |
||
366 | |||
367 | /** |
||
368 | * Flattens a multi-dimensional array. |
||
369 | * |
||
370 | * @param array $array The initial array. |
||
371 | * @return array The flattened array. |
||
372 | */ |
||
373 | public static function flatten(array $array) |
||
374 | { |
||
375 | $results = []; |
||
376 | |||
377 | array_walk_recursive($array, function($x) use (&$results) { |
||
378 | $results[] = $x; |
||
379 | }); |
||
380 | |||
381 | return $results; |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * Returns a string delimited key from an array, with a default value if the given key does not exist. If null |
||
386 | * is given instead of a key, the whole initial array will be returned. |
||
387 | * |
||
388 | * Note: Nested objects will be accessed as if they were arrays, eg. if "some.nested.object" is an object, |
||
389 | * then looking for "some.nested.object.property" will be handled just as a normal array would. |
||
390 | * |
||
391 | * @param array $array The array to search in. |
||
392 | * @param string|array $key The string delimited key or a chain (array) of nested keys pointing |
||
393 | * to the desired key. |
||
394 | * @param mixed $default The default value. |
||
395 | * @param string $delimiter The delimiter to use when exploding the key into parts. |
||
396 | * @return mixed |
||
397 | */ |
||
398 | public static function get(array $array, $key = null, $default = null, string $delimiter = null) |
||
399 | { |
||
400 | // Make loops easier for the end-user - return the initial array if the key is null instead of forcing |
||
401 | // a valid value. |
||
402 | if (!isset($key)) { |
||
403 | return $array; |
||
404 | } |
||
405 | |||
406 | // Which string delimiter should we use? |
||
407 | if (!isset($delimiter)) { |
||
408 | $delimiter = static::$delimiter; |
||
409 | } |
||
410 | |||
411 | // If the key is string delimited, we need to explode it into an array of segments. |
||
412 | $segments = is_array($key) ? $key : explode($delimiter, $key); |
||
413 | |||
414 | // One dimension at a time. |
||
415 | while ($segment = array_shift($segments)) { |
||
416 | |||
417 | // If the current segment is a wildcard, make sure the it points to an array |
||
418 | // and pluck the remaining segments from it. |
||
419 | if ($segment === '*') { |
||
420 | return is_array($array) ? static::pluck($array, $segments, $delimiter) : $default; |
||
421 | } |
||
422 | |||
423 | // Note: isset() is the cheapest condition to check for while being rather probable at the same time, |
||
424 | // thus the seemingly unintuitive condition ordering. |
||
425 | if (isset($array->{$segment})) { |
||
426 | $array = $array->{$segment}; |
||
427 | } elseif (is_array($array) && array_key_exists($segment, $array)) { |
||
428 | $array = $array[$segment]; |
||
429 | } else { |
||
430 | return $default; |
||
431 | } |
||
432 | } |
||
433 | |||
434 | return $array; |
||
435 | } |
||
436 | |||
437 | /** |
||
438 | * Checks whether the given string delimited key exists in the array. |
||
439 | * |
||
440 | * Note: Nested objects will not be accessed as if they were arrays, ie. if "some.nested.object" is an object, |
||
441 | * not an array, then looking for "some.nested.object.key" will always return false. |
||
442 | * |
||
443 | * @param array $array The array to search in. |
||
444 | * @param string $key The string delimited key. |
||
445 | * @param string $delimiter The delimiter to use when exploding the key into parts. |
||
446 | * @return bool True when the given key exists, false otherwise. |
||
447 | */ |
||
448 | public static function has(array $array, string $key, string $delimiter = null) |
||
449 | { |
||
450 | // Which string delimiter should we use? |
||
451 | if (null === $delimiter) { |
||
452 | $delimiter = static::$delimiter; |
||
453 | } |
||
454 | |||
455 | foreach (explode($delimiter, $key) as $segment) { |
||
456 | if (!is_array($array) || !array_key_exists($segment, $array)) { |
||
457 | return false; |
||
458 | } |
||
459 | |||
460 | $array = $array[$segment]; |
||
461 | } |
||
462 | |||
463 | return true; |
||
464 | } |
||
465 | |||
466 | /** |
||
467 | * @see \nyx\utils\Arr::first() |
||
468 | */ |
||
469 | public static function head(array $array, $callback = null, $default = null) |
||
470 | { |
||
471 | return static::first($array, $callback, $default); |
||
472 | } |
||
473 | |||
474 | /** |
||
475 | * Returns all but the last value(s) of the given array. |
||
476 | * |
||
477 | * If a callable is passed, elements at the end of the array are excluded from the result as long as the |
||
478 | * callback returns a truthy value. If a number is passed, the last n values are excluded from the result. |
||
479 | * |
||
480 | * @param array $array The array to traverse. |
||
481 | * @param callable|int $callback The truth test the value should pass or an integer denoting how many |
||
482 | * of the final elements of the array should be excluded. The count is |
||
483 | * 1-indexed, ie. if you want to exclude the last 2 elements, pass 2. |
||
484 | * @param mixed $default The default value to be returned if none of the elements passes the test. |
||
485 | * Only useful when $callback is a callable. |
||
486 | * @return mixed |
||
487 | */ |
||
488 | public static function initial(array $array, $callback, $default = null) |
||
489 | { |
||
490 | // When given a callable, keep counting as long as the callable returns a truthy value. |
||
491 | View Code Duplication | if (is_callable($callback)) { |
|
492 | $i = 0; |
||
493 | |||
494 | foreach (array_reverse($array) as $key => $value) { |
||
495 | if (!$callback($value, $key)) { |
||
496 | break; |
||
497 | } |
||
498 | |||
499 | $i++; |
||
500 | } |
||
501 | |||
502 | // If we didn't get at least a single truthy value, return the default. |
||
503 | if ($i === 0) { |
||
504 | return $default; |
||
505 | } |
||
506 | |||
507 | // Otherwise we're just gonna overwrite the $callback and proceed as if it were an integer in the |
||
508 | // first place. |
||
509 | $callback = $i; |
||
510 | } |
||
511 | |||
512 | // At this point we need a positive integer, 1 at minimum. |
||
513 | return array_slice($array, 0, -(int) (!$callback ? 1 : abs($callback))); |
||
514 | } |
||
515 | |||
516 | /** |
||
517 | * Checks whether the given array is an associative array. |
||
518 | * |
||
519 | * @param array $array The array to check. |
||
520 | * @return bool True when the array is associative, false otherwise. |
||
521 | */ |
||
522 | public static function isAssociative(array $array) : bool |
||
523 | { |
||
524 | return array_keys($array) !== range(0, count($array) - 1); |
||
525 | } |
||
526 | |||
527 | /** |
||
528 | * Checks whether the given array is a multidimensional array. |
||
529 | * |
||
530 | * @param array $array The array to check. |
||
531 | * @return bool True when the array has multiple dimensions, false otherwise. |
||
532 | */ |
||
533 | public static function isMultidimensional(array $array) : bool |
||
534 | { |
||
535 | return count($array) !== count($array, COUNT_RECURSIVE); |
||
536 | } |
||
537 | |||
538 | /** |
||
539 | * Returns the last element of the array, |
||
540 | * OR the last $elements of the array when $elements is a positive integer, |
||
541 | * OR the last element which passes the given truth test when $elements is a callable. |
||
542 | * |
||
543 | * Opposite: @see \nyx\utils\Arr::first() |
||
544 | * |
||
545 | * @param array $array The array to traverse. |
||
546 | * @param callable|int $elements The truth test the value should pass or a positive integer |
||
547 | * denoting how many of the final elements of the array should be returned. |
||
548 | * When not given, the method will return the first element of the array. |
||
549 | * @param mixed $default The default value to be returned if none of the elements passes |
||
550 | * the test or the array is empty. |
||
551 | * @throws \InvalidArgumentException When $elements is an integer smaller than 1. |
||
552 | * @throws \InvalidArgumentException When $elements is neither a valid integer nor a callable. |
||
553 | * @return mixed |
||
554 | */ |
||
555 | View Code Duplication | public static function last(array $array, $elements = null, $default = null) |
|
556 | { |
||
557 | if (empty($array)) { |
||
558 | return $default; |
||
559 | } |
||
560 | |||
561 | // Most common use case - simply return the last value of the array. |
||
562 | if (!isset($elements) || $elements === 1) { |
||
563 | return end($array); |
||
564 | } |
||
565 | |||
566 | // With a integer given, return a slice containing the last $elements elements. |
||
567 | if (is_int($elements)) { |
||
568 | |||
569 | if ($elements < 1) { |
||
570 | throw new \InvalidArgumentException("At least 1 element must be requested, while [$elements] were requested."); |
||
571 | } |
||
572 | |||
573 | return array_slice($array, -$elements); |
||
574 | } |
||
575 | |||
576 | // With a callable given, return the last value which passes the given truth test. |
||
577 | if (is_callable($elements)) { |
||
578 | return static::find(array_reverse($array), $elements, $default); |
||
579 | } |
||
580 | |||
581 | throw new \InvalidArgumentException('Expected $elements to be a positive integer or a callable, got ['.diagnostics\Debug::getTypeName($elements).'] instead.'); |
||
582 | } |
||
583 | |||
584 | /** |
||
585 | * Returns the biggest value from the given array. |
||
586 | * |
||
587 | * @param array $array The array to traverse. |
||
588 | * @return mixed The resulting value. |
||
589 | */ |
||
590 | public static function max(array $array) |
||
591 | { |
||
592 | // Avoid some overhead at this point already if possible. |
||
593 | if (empty($array)) { |
||
594 | return null; |
||
595 | } |
||
596 | |||
597 | // Sort in a descending order. |
||
598 | arsort($array); |
||
599 | |||
600 | // Return the first element of the sorted array. |
||
601 | return reset($array); |
||
602 | } |
||
603 | |||
604 | /** |
||
605 | * Merges 2 or more arrays recursively. Differs in two important aspects from array_merge_recursive(), to |
||
606 | * more closely resemble the standard behaviour of the non-recursive array_merge(): |
||
607 | * - In case of 2 different values, when they are not arrays, the latter one overwrites the earlier instead |
||
608 | * of merging them into an array; |
||
609 | * - Non-conflicting numeric keys are left unchanged. In case of a conflict, the new value will be appended |
||
610 | * to the resulting array (not preserving its key). |
||
611 | * |
||
612 | * @param array $array The initial array. |
||
613 | * @param array ...$with One or more (ie. separate arguments) arrays to merge in. |
||
614 | * @return array The resulting merged array. |
||
615 | */ |
||
616 | public static function merge(array $array, array ...$with) : array |
||
617 | { |
||
618 | foreach ($with as $arr) { |
||
619 | foreach ($arr as $key => $value) { |
||
620 | // Append numeric keys. |
||
621 | if (is_int($key)) { |
||
622 | array_key_exists($key, $array) ? $array[] = $value : $array[$key] = $value; |
||
623 | } |
||
624 | // Merge multi-dimensional arrays recursively. |
||
625 | elseif (is_array($value) && array_key_exists($key, $array) && is_array($array[$key])) { |
||
626 | $array[$key] = static::merge($array[$key], $value); |
||
627 | } else { |
||
628 | $array[$key] = $value; |
||
629 | } |
||
630 | } |
||
631 | } |
||
632 | |||
633 | return $array; |
||
634 | } |
||
635 | |||
636 | /** |
||
637 | * Returns the smallest value from the given array. |
||
638 | * |
||
639 | * @param array $array The array to traverse. |
||
640 | * @return mixed The resulting value. |
||
641 | */ |
||
642 | public static function min(array $array) |
||
643 | { |
||
644 | // Avoid some overhead at this point already if possible. |
||
645 | if (empty($array)) { |
||
646 | return null; |
||
647 | } |
||
648 | |||
649 | // Sort in an ascending order. |
||
650 | asort($array); |
||
651 | |||
652 | // Return the first element of the sorted array. |
||
653 | return reset($array); |
||
654 | } |
||
655 | |||
656 | /** |
||
657 | * Returns a subset of the given array, containing only the specified keys. |
||
658 | * |
||
659 | * @param array $array The initial array. |
||
660 | * @param array $keys An array of keys (the keys are expected to be values of this array). |
||
661 | * @return array |
||
662 | */ |
||
663 | public static function only(array $array, array $keys) : array |
||
664 | { |
||
665 | return array_intersect_key($array, array_values($keys)); |
||
666 | } |
||
667 | |||
668 | /** |
||
669 | * Returns a random value from the given array, or $elements random values when $elements is a positive integer, or |
||
670 | * the first random element that passes the given truth test when $elements is a callable. |
||
671 | * |
||
672 | * @param array $array The array to search in. |
||
673 | * @param callable|int $elements The number of random values to return or a callable to return the first |
||
674 | * randomly shuffled element that passes the given truth test. |
||
675 | * @param mixed $default The default value to be returned if none of the elements passes |
||
676 | * the test or the array is empty. |
||
677 | * @return mixed |
||
678 | */ |
||
679 | public static function pick(array $array, $elements = null, $default = null) |
||
680 | { |
||
681 | // There are *slightly* better performing alternatives, but simply shuffling and delegating |
||
682 | // the actual picking to static::first() allows us to avoid a fair amount of duplicated code. |
||
683 | shuffle($array); |
||
684 | |||
685 | return static::first($array, $elements, $default); |
||
686 | } |
||
687 | |||
688 | /** |
||
689 | * Given an array containing other arrays or objects, looks for the value with the given key/property |
||
690 | * of $key within them and returns a new array containing all values of said key from the initial array. |
||
691 | * Essentially like fetching a single column from a classic database table. |
||
692 | * |
||
693 | * When the optional $index parameter is given, the resulting array will be indexed by the values corresponding |
||
694 | * to the given $index. |
||
695 | * |
||
696 | * @see array_column() A faster and simpler alternative, if you do not need to pluck data with support |
||
697 | * for delimited keys or wildcards. |
||
698 | * |
||
699 | * @param array $array The array to search in. |
||
700 | * @param string|array $key The key of the value to look for. |
||
701 | * @param string|array $index The key of the value to index the resulting array by. |
||
702 | * @param string $delimiter The delimiter to use when exploding the key into parts. |
||
703 | * @return array |
||
704 | */ |
||
705 | public static function pluck(array $array, $key, $index = null, string $delimiter = null) : array |
||
706 | { |
||
707 | $results = []; |
||
708 | |||
709 | // Which string delimiter should we use? |
||
710 | if (!isset($delimiter)) { |
||
711 | $delimiter = static::$delimiter; |
||
712 | } |
||
713 | |||
714 | foreach ($array as $item) { |
||
715 | |||
716 | $itemValue = static::get($item, $key, $delimiter); |
||
717 | |||
718 | // If the key given is null, the resulting array will contain numerically indexed keys. |
||
719 | if (!isset($index)) { |
||
720 | $results[] = $itemValue; |
||
721 | } |
||
722 | // Otherwise we are going use the value of the given key and use it in the resulting array as key |
||
723 | // for the value determined earlier. |
||
724 | else { |
||
725 | $results[static::get($item, $index, $delimiter)] = $itemValue; |
||
726 | } |
||
727 | } |
||
728 | |||
729 | return $results; |
||
730 | } |
||
731 | |||
732 | /** |
||
733 | * Returns the value for a string delimited key from an array and then removes it. |
||
734 | * |
||
735 | * @param array $array The array to search in. |
||
736 | * @param string $key The string delimited key. |
||
737 | * @param string $delimiter The delimiter to use when exploding the key into parts. |
||
738 | * @return mixed |
||
739 | */ |
||
740 | public static function pull(&$array, string $key, string $delimiter = null) |
||
741 | { |
||
742 | $value = static::get($array, $key, null, $delimiter); |
||
743 | |||
744 | static::remove($array, $key, $delimiter); |
||
745 | |||
746 | return $value; |
||
747 | } |
||
748 | |||
749 | /** |
||
750 | * Removes a string delimited key from the given array. |
||
751 | * |
||
752 | * @param array& $array The array to search in. |
||
0 ignored issues
–
show
|
|||
753 | * @param string $key The string delimited key. |
||
754 | * @param string $delimiter The delimiter to use when exploding the key into parts. |
||
755 | */ |
||
756 | public static function remove(array& $array, string $key, string $delimiter = null) |
||
757 | { |
||
758 | // Which string delimiter should we use? |
||
759 | if (!isset($delimiter)) { |
||
760 | $delimiter = static::$delimiter; |
||
761 | } |
||
762 | |||
763 | // Explode the key according to that delimiter. |
||
764 | $keys = explode($delimiter, $key); |
||
765 | |||
766 | while ($key = array_shift($keys)) { |
||
767 | if (!isset($array[$key]) || !is_array($array[$key])) { |
||
768 | return; |
||
769 | } |
||
770 | |||
771 | $array =& $array[$key]; |
||
772 | } |
||
773 | |||
774 | unset($array[array_shift($keys)]); |
||
775 | } |
||
776 | |||
777 | /** |
||
778 | * Returns all but the first value of the given array, all but the first elements for which the $callback |
||
779 | * returns true if $callback is a callable, or all but the first $callback elements if $callback is a number. |
||
780 | * |
||
781 | * Aliases: |
||
782 | * - @see Arr::tail() |
||
783 | * |
||
784 | * @param array $array The array to traverse. |
||
785 | * @param callable|int|bool $callback The truth test the value should pass or an integer denoting how many |
||
786 | * of the initial elements of the array should be excluded. The count |
||
787 | * is 1-indexed, ie. if you want to exclude the first 2 elements, pass 2. |
||
788 | * When a falsy value is given, the method will return all but the first |
||
789 | * element of the array. |
||
790 | * @param mixed $default The default value to be returned if none of the elements passes the |
||
791 | * test or the array contains no more than one item. |
||
792 | * @return mixed |
||
793 | */ |
||
794 | public static function rest(array $array, $callback = false, $default = null) |
||
795 | { |
||
796 | // Avoid some overhead at this point already if possible. We need at least 2 elements in the array for |
||
797 | // this method to make any usage sense. |
||
798 | if (2 > count($array)) { |
||
799 | return $default; |
||
800 | } |
||
801 | |||
802 | // For a falsy callback, return all but the first element of the array. |
||
803 | if (!$callback) { |
||
804 | return array_slice($array, 1); |
||
805 | } |
||
806 | |||
807 | // With a callable given, keep counting as long as the callable returns a truthy value. |
||
808 | View Code Duplication | if (is_callable($callback)) { |
|
809 | $i = 0; |
||
810 | |||
811 | foreach ($array as $key => $value) { |
||
812 | if (!$callback($value, $key)) { |
||
813 | break; |
||
814 | } |
||
815 | |||
816 | $i++; |
||
817 | } |
||
818 | |||
819 | // If we didn't get at least a single truthy value, return the default. |
||
820 | if ($i === 0) { |
||
821 | return $default; |
||
822 | } |
||
823 | |||
824 | // Otherwise we're just gonna overwrite the $callback and proceed as if it were an integer in the |
||
825 | // first place. |
||
826 | $callback = $i; |
||
827 | } |
||
828 | |||
829 | // Return the final $callback elements. |
||
830 | return array_slice($array, abs((int) $callback)); |
||
831 | } |
||
832 | |||
833 | /** |
||
834 | * Sets the given value for a string delimited key within the given array. If null is given instead of a key, |
||
835 | * the whole initial array will be overwritten with the given value. |
||
836 | * |
||
837 | * @param array $array The array to set the value in. |
||
838 | * @param string $key The string delimited key. |
||
839 | * @param mixed $value The value to set. |
||
840 | * @param string $delimiter The delimiter to use when exploding the key into parts. |
||
841 | * @return mixed |
||
842 | */ |
||
843 | public static function set(array& $array, $key, $value, string $delimiter = null) |
||
844 | { |
||
845 | // Make loops easier for the end-user - overwrite the whole array if the key is null. |
||
846 | if (null === $key) { |
||
847 | return $array = $value; |
||
848 | } |
||
849 | |||
850 | // Which string delimiter should we use? |
||
851 | if (null === $delimiter) { |
||
852 | $delimiter = static::$delimiter; |
||
853 | } |
||
854 | |||
855 | // Explode the key according to that delimiter. |
||
856 | $keys = explode($delimiter, $key); |
||
857 | |||
858 | while (count($keys) > 1) { |
||
859 | $key = array_shift($keys); |
||
860 | |||
861 | if (!isset($array[$key]) || !is_array($array[$key])) { |
||
862 | $array[$key] = []; |
||
863 | } |
||
864 | |||
865 | $array =& $array[$key]; |
||
866 | } |
||
867 | |||
868 | return $array[array_shift($keys)] = $value; |
||
869 | } |
||
870 | |||
871 | /** |
||
872 | * Alias for {@see static::any()} |
||
873 | */ |
||
874 | public static function some(array $array, callable $callback) : bool |
||
875 | { |
||
876 | return static::any($array, $callback); |
||
877 | } |
||
878 | |||
879 | /** |
||
880 | * Alias for {@see static::rest()} |
||
881 | */ |
||
882 | public static function tail(array $array, $callback = false, $default = null) |
||
883 | { |
||
884 | return static::rest($array, $callback, $default); |
||
885 | } |
||
886 | |||
887 | /** |
||
888 | * @see \nyx\utils\Arr::first() |
||
889 | */ |
||
890 | public static function take(array $array, $callback = null, $default = null) |
||
891 | { |
||
892 | return static::first($array, $callback, $default); |
||
893 | } |
||
894 | |||
895 | /** |
||
896 | * Returns an array based on the initial array with all occurrences of the passed values removed. Uses strict |
||
897 | * equality comparisons. |
||
898 | * |
||
899 | * @param array $array The array to traverse. |
||
900 | * @param mixed ...$values The values which should get removed. |
||
901 | * @return array |
||
902 | */ |
||
903 | public static function without(array $array, ...$values) : array |
||
904 | { |
||
905 | return array_filter($array, function($value) use ($values) { |
||
906 | return !in_array($value, $values, true); |
||
907 | }); |
||
908 | } |
||
909 | } |
||
910 |
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.