PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 1.0.98
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v1.0.98
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
fluent-community / vendor / wpfluent / framework / src / WPFluent / Support / Arr.php

Arr.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 1.0.98, at vendor/wpfluent/framework/src/WPFluent/Support/Arr.php

1,680 lines 42.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\Framework\Support;
4
5 use Closure;
6 use ArrayAccess;
7 use InvalidArgumentException;
8 use FluentCommunity\Framework\Support\Helper;
9 use FluentCommunity\Framework\Support\Collection;
10 use FluentCommunity\Framework\Support\MacroableTrait;
11
12 class Arr
13 {
14 use MacroableTrait;
15
16 /**
17 * Makes a collection from array
18 *
19 * @param array $array
20 * @return FluentCommunity\Framework\Support\Collection
21 */
22 public static function of(array $array)
23 {
24 return Helper::collect($array);
25 }
26
27 /**
28 * Determine whether the given value is array accessible.
29 *
30 * @param mixed $value
31 * @return bool
32 */
33 public static function accessible($value)
34 {
35 return is_array($value) || $value instanceof ArrayAccess;
36 }
37
38 /**
39 * Add an element to an array using "dot" notation if it doesn't exist.
40 *
41 * @param array $array
42 * @param string $key
43 * @param mixed $value
44 * @return array
45 */
46 public static function add($array, $key, $value)
47 {
48 if (is_null(static::get($array, $key))) {
49 static::set($array, $key, $value);
50 }
51
52 return $array;
53 }
54
55 /**
56 * Collapse an array of arrays into a single array.
57 *
58 * @param iterable $array
59 * @return array
60 */
61 public static function collapse($array)
62 {
63 $results = [];
64
65 foreach ($array as $values) {
66 if ($values instanceof Collection) {
67 $values = $values->all();
68 } elseif (! is_array($values)) {
69 continue;
70 }
71
72 $results[] = $values;
73 }
74
75 return array_merge([], ...$results);
76 }
77
78 /**
79 * Cross join the given arrays, returning all possible permutations.
80 *
81 * @param iterable ...$arrays
82 * @return array
83 */
84 public static function crossJoin(...$arrays)
85 {
86 $results = [[]];
87
88 foreach ($arrays as $index => $array) {
89 $append = [];
90
91 foreach ($results as $product) {
92 foreach ($array as $item) {
93 $product[$index] = $item;
94
95 $append[] = $product;
96 }
97 }
98
99 $results = $append;
100 }
101
102 return $results;
103 }
104
105 /**
106 * Divide an array into two arrays. One with keys and the other with values.
107 *
108 * @param array $array
109 * @return array
110 */
111 public static function divide($array)
112 {
113 return [array_keys($array), array_values($array)];
114 }
115
116 /**
117 * Flatten a multi-dimensional associative array with dots.
118 *
119 * @param iterable $array
120 * @param string $prepend
121 * @return array
122 */
123 public static function dot($array, $prepend = '')
124 {
125 $results = [];
126
127 foreach ($array as $key => $value) {
128 if (is_array($value) && ! empty($value)) {
129 $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
130 } else {
131 $results[$prepend.$key] = $value;
132 }
133 }
134
135 return $results;
136 }
137
138 /**
139 * Convert a flatten "dot" notation array into an expanded array.
140 *
141 * @param iterable $array
142 * @return array
143 */
144 public static function undot($array)
145 {
146 $results = [];
147
148 foreach ($array as $key => $value) {
149 static::set($results, $key, $value);
150 }
151
152 return $results;
153 }
154
155 /**
156 * Get all of the given array except for a specified array of keys.
157 *
158 * @param array $array
159 * @param array|string $keys
160 * @return array
161 */
162 public static function except($array, $keys)
163 {
164 static::forget($array, $keys);
165
166 return $array;
167 }
168
169 /**
170 * Determine if the given key exists in the provided array.
171 *
172 * @param \ArrayAccess|array $array
173 * @param string|int $key
174 * @return bool
175 */
176 public static function exists($array, $key)
177 {
178 if ($array instanceof Enumerable) {
179 return $array->has($key);
180 }
181
182 if ($array instanceof ArrayAccess) {
183 return $array->offsetExists($key);
184 }
185
186 return array_key_exists($key, $array);
187 }
188
189 /**
190 * Alias of exists.
191 * @param \ArrayAccess|array $array
192 * @param string|int $key
193 * @return bool
194 */
195 public static function keyExists($array, $key)
196 {
197 return static::exists($array, $key);
198 }
199
200 /**
201 * Alias of exists.
202 * @param \ArrayAccess|array $array
203 * @param string|int $key
204 * @return bool
205 */
206 public static function arrayKeyExists($array, $key)
207 {
208 return static::exists($array, $key);
209 }
210
211 /**
212 * Return the first element in an array passing a given truth test.
213 *
214 * @param iterable $array
215 * @param callable|null $callback
216 * @param mixed $default
217 * @return mixed
218 */
219 public static function first($array, ?callable $callback = null, $default = null)
220 {
221 if (is_null($callback)) {
222 if (empty($array)) {
223 return Helper::value($default);
224 }
225
226 foreach ($array as $item) {
227 return $item;
228 }
229 }
230
231 foreach ($array as $key => $value) {
232 if ($callback($value, $key)) {
233 return $value;
234 }
235 }
236
237 return Helper::value($default);
238 }
239
240 /**
241 * Returns the key of the first item (matching the specified
242 * callback if given) or null if there is no such item.
243 *
244 * @param array $array
245 * @param callable|null $callback
246 * @return mixed
247 */
248 public static function firstKey($array, ?callable $callback = null)
249 {
250 if (!$callback) {
251 return array_key_first($array);
252 }
253
254 foreach ($array as $k => $v) {
255 if ($callback($v, $k, $array)) {
256 return $k;
257 }
258 }
259
260 return null;
261 }
262
263 /**
264 * Recursively filter an array like array_filter.
265 *
266 * @param array $array
267 * @param callable|null $cb
268 * @param integer $mode (ARRAY_FILTER_USE_BOTH = 1 | ARRAY_FILTER_USE_KEY = 2)
269 * @return array
270 */
271 public static function filterRecursive($array, ?callable $cb = null, $mode = 0)
272 {
273 $result = [];
274
275 foreach ($array as $key => $value) {
276 if (is_array($value)) {
277 if (is_int($key)) {
278 $result[] = static::filterRecursive($value, $cb, $mode);
279 } else {
280 $result[$key] = static::filterRecursive($value, $cb, $mode);
281 }
282 } else {
283 if (is_null($cb)) {
284 if ($value) {
285 if (is_int($key)) {
286 $result[] = $value;
287 } else {
288 $result[$key] = $value;
289 }
290 }
291 } else {
292 if ($mode && call_user_func($cb, $value, $key)) {
293 if (is_int($key)) {
294 $result[] = $value;
295 } else {
296 $result[$key] = $value;
297 }
298 } elseif (!$mode && call_user_func($cb, $value)) {
299 if (is_int($key)) {
300 $result[] = $value;
301 } else {
302 $result[$key] = $value;
303 }
304 }
305 }
306 }
307 }
308
309 return $result;
310 }
311
312 /**
313 * Recursively search the value and return the path of first match.
314 *
315 * @param array $array
316 * @param mixed $value
317 * @param bool $ci (false for case insensitive search, true otherwise)
318 * @return array|null
319 */
320 public static function findPath($array, $value, $ci = false)
321 {
322 if (!$ci) {
323 $value = strtolower($value);
324 $array = static::map($array, 'strtolower');
325 }
326
327 foreach ($array as $key => $val) {
328 if ($val === $value) {
329 return $key;
330 } elseif (is_array($val) && $path = static::findPath($val, $value, $ci)) {
331 return $key.'.'.$path;
332 }
333 }
334 }
335
336 /**
337 * Return the last element in an array passing a given truth test.
338 *
339 * @param array $array
340 * @param callable|null $callback
341 * @param mixed $default
342 * @return mixed
343 */
344 public static function last($array, ?callable $callback = null, $default = null)
345 {
346 if (is_null($callback)) {
347 return empty($array) ? Helper::value($default) : end($array);
348 }
349
350 return static::first(array_reverse($array, true), $callback, $default);
351 }
352
353 /**
354 * Returns the key of the last item (matching the specified
355 * callback if given) or null if there is no such item.
356 *
357 * @param array $array
358 * @param callable|null $callback
359 * @return mixed
360 */
361 public static function lastKey($array, ?callable $callback = null)
362 {
363 if (!$callback) {
364 return array_key_last($array);
365 }
366
367 $lastKey = null;
368
369 foreach ($array as $k => $v) {
370 if ($callback($v, $k, $array)) {
371 $lastKey = $k;
372 }
373 }
374
375 return $lastKey;
376 }
377
378 /**
379 * Flatten a multi-dimensional array into a single level.
380 *
381 * @param iterable $array
382 * @param int $depth
383 * @return array
384 */
385 public static function flatten($array, $depth = INF)
386 {
387 $result = [];
388
389 foreach ($array as $item) {
390 $item = $item instanceof Collection ? $item->all() : $item;
391
392 if (! is_array($item)) {
393 $result[] = $item;
394 } else {
395 $values = $depth === 1
396 ? array_values($item)
397 : static::flatten($item, $depth - 1);
398
399 foreach ($values as $value) {
400 $result[] = $value;
401 }
402 }
403 }
404
405 return $result;
406 }
407
408 /**
409 * Remove one or many array items from a given array using "dot" notation.
410 *
411 * @param array $array
412 * @param array|string $keys
413 * @return void
414 */
415 public static function forget(&$array, $keys)
416 {
417 $original = &$array;
418
419 $keys = (array) $keys;
420
421 if (count($keys) === 0) {
422 return;
423 }
424
425 foreach ($keys as $key) {
426 // if the exact key exists in the top-level, remove it
427 if (static::exists($array, $key)) {
428 unset($array[$key]);
429
430 continue;
431 }
432
433 $parts = explode('.', $key);
434
435 // clean up before each pass
436 $array = &$original;
437
438 while (count($parts) > 1) {
439 $part = array_shift($parts);
440
441 if (isset($array[$part]) && is_array($array[$part])) {
442 $array = &$array[$part];
443 } else {
444 continue 2;
445 }
446 }
447
448 unset($array[array_shift($parts)]);
449 }
450 }
451
452 /**
453 * Get an item from an array using "dot" notation.
454 *
455 * @param \ArrayAccess|array $array
456 * @param string|int|null $key
457 * @param mixed $default
458 * @return mixed
459 */
460 public static function get($array, $key, $default = null)
461 {
462 if (! static::accessible($array)) {
463 return Helper::value($default);
464 }
465
466 if (is_null($key)) {
467 return $array;
468 }
469
470 if (static::exists($array, $key)) {
471 return $array[$key];
472 }
473
474 if (strpos($key, '.') === false) {
475 return $array[$key] ?? Helper::value($default);
476 }
477
478 foreach (explode('.', $key) as $segment) {
479 if (static::accessible($array) && static::exists($array, $segment)) {
480 $array = $array[$segment];
481 } else {
482 return Helper::value($default);
483 }
484 }
485
486 return $array;
487 }
488
489 /**
490 * Check if an item or items (using key) exist in an array using "dot" notation.
491 *
492 * @param \ArrayAccess|array $array
493 * @param string|array $keys
494 * @return bool
495 */
496 public static function has($array, $keys)
497 {
498 $keys = (array) $keys;
499
500 if (! $array || $keys === []) {
501 return false;
502 }
503
504 foreach ($keys as $key) {
505 $subKeyArray = $array;
506
507 if (static::exists($array, $key)) {
508 continue;
509 }
510
511 foreach (explode('.', $key) as $segment) {
512 if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
513 $subKeyArray = $subKeyArray[$segment];
514 } else {
515 return false;
516 }
517 }
518 }
519
520 return true;
521 }
522
523 /**
524 * Determine if any of the keys exist in an array using "dot" notation.
525 *
526 * @param \ArrayAccess|array $array
527 * @param string|array $keys
528 * @return bool
529 */
530 public static function hasAny($array, $keys)
531 {
532 if (is_null($keys)) {
533 return false;
534 }
535
536 $keys = (array) $keys;
537
538 if (! $array) {
539 return false;
540 }
541
542 if ($keys === []) {
543 return false;
544 }
545
546 foreach ($keys as $key) {
547 if (static::has($array, $key)) {
548 return true;
549 }
550 }
551
552 return false;
553 }
554
555 /**
556 * Alias of contains.
557 *
558 * @param array $array
559 * @param string|array $values
560 * @return bool
561 */
562 public static function inArray($array, $value)
563 {
564 return static::contains($array, $value);
565 }
566
567 /**
568 * Determines if an array is associative.
569 *
570 * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
571 *
572 * @param array $array
573 * @return bool
574 */
575 public static function isAssoc(array $array)
576 {
577 $keys = array_keys($array);
578
579 return array_keys($keys) !== $keys;
580 }
581
582 /**
583 * Determines if an array is a list.
584 *
585 * An array is a "list" if all array keys are sequential integers starting from 0 with no gaps in between.
586 *
587 * @param array $array
588 * @return bool
589 */
590 public static function isList($array)
591 {
592 return ! self::isAssoc($array);
593 }
594
595 /**
596 * Determines if the given key contains a boolean value.
597 *
598 * Returns true for true, 1, "1", "true", "on" and "yes"
599 * Returns false for false, "0", "false", "off", "no", and ""
600 * Returns for all non-boolean values.
601 *
602 * @param array $array
603 * @param string $key
604 *
605 * @return bool|null
606 * @see https://www.php.net/manual/en/filter.filters.validate.php
607 */
608 public static function isTrue($array, $key)
609 {
610 return filter_var(
611 static::get($array, $key),
612 FILTER_VALIDATE_BOOLEAN,
613 FILTER_NULL_ON_FAILURE
614 );
615 }
616
617 /**
618 * Get a subset of the items from the given array.
619 *
620 * @param array $array
621 * @param array|string $keys
622 * @return array
623 */
624 public static function only($array, $keys)
625 {
626 return array_intersect_key($array, array_flip((array) $keys));
627 }
628
629 /**
630 * Pluck an array of values from an array.
631 *
632 * @param iterable $array
633 * @param string|array|int|null $value
634 * @param string|array|null $key
635 * @return array
636 */
637 public static function pluck($array, $value, $key = null)
638 {
639 $results = [];
640
641 [$value, $key] = static::explodePluckParameters($value, $key);
642
643 foreach ($array as $item) {
644 $itemValue = Helper::dataGet($item, $value);
645
646 // If the key is "null", we will just append the value to the array and keep
647 // looping. Otherwise we will key the array using the value of the key we
648 // received from the developer. Then we'll return the final array form.
649 if (is_null($key)) {
650 $results[] = $itemValue;
651 } else {
652 $itemKey = Helper::dataGet($item, $key);
653
654 if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
655 $itemKey = (string) $itemKey;
656 }
657
658 $results[$itemKey] = $itemValue;
659 }
660 }
661
662 return $results;
663 }
664
665 /**
666 * Explode the "value" and "key" arguments passed to "pluck".
667 *
668 * @param string|array $value
669 * @param string|array|null $key
670 * @return array
671 */
672 protected static function explodePluckParameters($value, $key)
673 {
674 $value = is_string($value) ? explode('.', $value) : $value;
675
676 $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
677
678 return [$value, $key];
679 }
680
681 /**
682 * Push an item onto the beginning of an array.
683 *
684 * @param array $array
685 * @param mixed $value
686 * @param mixed $key
687 * @return array
688 */
689 public static function prepend($array, $value, $key = null)
690 {
691 if (func_num_args() == 2) {
692 array_unshift($array, $value);
693 } else {
694 $array = [$key => $value] + $array;
695 }
696
697 return $array;
698 }
699
700 /**
701 * Get a value from the array, and remove it.
702 *
703 * @param array $array
704 * @param string|int $key
705 * @param mixed $default
706 * @return mixed
707 */
708 public static function pull(&$array, $key, $default = null)
709 {
710 $value = static::get($array, $key, $default);
711
712 static::forget($array, $key);
713
714 return $value;
715 }
716
717 /**
718 * Convert the array into a query string.
719 *
720 * @param array $array
721 * @return string
722 */
723 public static function query($array)
724 {
725 return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
726 }
727
728 /**
729 * Get one or a specified number of random values from an array.
730 *
731 * @param array $array
732 * @param int|null $number
733 * @param bool|false $preserveKeys
734 * @return mixed
735 *
736 * @throws \InvalidArgumentException
737 */
738 public static function random($array, $number = null, $preserveKeys = false)
739 {
740 $requested = is_null($number) ? 1 : $number;
741
742 $count = count($array);
743
744 if ($requested > $count) {
745 throw new InvalidArgumentException(
746 "You requested {$requested} items, but there are only {$count} items available."
747 );
748 }
749
750 if (is_null($number)) {
751 return $array[array_rand($array)];
752 }
753
754 if ((int) $number === 0) {
755 return [];
756 }
757
758 $keys = array_rand($array, $number);
759
760 $results = [];
761
762 if ($preserveKeys) {
763 foreach ((array) $keys as $key) {
764 $results[$key] = $array[$key];
765 }
766 } else {
767 foreach ((array) $keys as $key) {
768 $results[] = $array[$key];
769 }
770 }
771
772 return $results;
773 }
774
775 /**
776 * Set an array item to a given value using "dot" notation.
777 *
778 * If no key is given to the method, the entire array will be replaced.
779 *
780 * @param array $array
781 * @param string|null $key
782 * @param mixed $value
783 * @return array
784 */
785 public static function set(&$array, $key, $value)
786 {
787 if (is_null($key)) {
788 return $array = $value;
789 }
790
791 $keys = explode('.', $key);
792
793 foreach ($keys as $i => $key) {
794 if (count($keys) === 1) {
795 break;
796 }
797
798 unset($keys[$i]);
799
800 // If the key doesn't exist at this depth, we will just create an empty array
801 // to hold the next value, allowing us to create the arrays to hold final
802 // values at the correct depth. Then we'll keep digging into the array.
803 if (! isset($array[$key]) || ! is_array($array[$key])) {
804 $array[$key] = [];
805 }
806
807 $array = &$array[$key];
808 }
809
810 $array[array_shift($keys)] = $value;
811
812 return $array;
813 }
814
815 /**
816 * Shuffle the given array and return the result.
817 *
818 * @param array $array
819 * @param int|null $seed
820 * @return array
821 */
822 public static function shuffle($array, $seed = null)
823 {
824 if (is_null($seed)) {
825 shuffle($array);
826 } else {
827 mt_srand($seed);
828 shuffle($array);
829 mt_srand();
830 }
831
832 return $array;
833 }
834
835 /**
836 * Sort the array using the given callback or "dot" notation.
837 *
838 * @param array $array
839 * @param callable|array|string|null $callback
840 * @return array
841 */
842 public static function sort($array, $callback = null)
843 {
844 return Collection::make($array)->sortBy($callback)->all();
845 }
846
847 /**
848 * Sort an array in descending order.
849 *
850 * @param array $array
851 * @param Flags
852 * @return array
853 * @see https://www.php.net/manual/en/function.rsort.php
854 */
855 public static function rsort($array, $flags = SORT_REGULAR)
856 {
857 rsort($array, $flags);
858 return $array;
859 }
860
861 /**
862 * Sort an array in ascending order and maintain index association.
863 *
864 * @param array $array
865 * @param Flags
866 * @return array
867 * @see https://www.php.net/manual/en/function.asort.php
868 */
869 public static function asort($array, $flags = SORT_REGULAR)
870 {
871 asort($array, $flags);
872 return $array;
873 }
874
875 /**
876 * Sort an array in descending order and maintain index association.
877 *
878 * @param array $array
879 * @param Flags
880 * @return array
881 * @see https://www.php.net/manual/en/function.arsort.php
882 */
883 public static function arsort($array, $flags = SORT_REGULAR)
884 {
885 arsort($array, $flags);
886 return $array;
887 }
888
889 /**
890 * Sort an array by key in ascending order.
891 *
892 * @param array $array
893 * @param Flags
894 * @return array
895 * @see https://www.php.net/manual/en/function.ksort.php
896 */
897 public static function ksort($array, $flags = SORT_REGULAR)
898 {
899 ksort($array, $flags);
900 return $array;
901 }
902
903 /**
904 * Sort an array by key in descending order.
905 *
906 * @param array $array
907 * @param Flags
908 * @return array
909 * @see https://www.php.net/manual/en/function.krsort.php
910 */
911 public static function krsort($array, $flags = SORT_REGULAR)
912 {
913 krsort($array, $flags);
914 return $array;
915 }
916
917 /**
918 * Sort an array using a "natural order" algorithm.
919 *
920 * @param array $array
921 * @return array
922 * @see https://www.php.net/manual/en/function.natsort.php
923 */
924 public static function natsort($array)
925 {
926 natsort($array);
927 return $array;
928 }
929
930 /**
931 * Sort an array using a case insensitive "natural order" algorithm.
932 *
933 * @param array $array
934 * @return array
935 * @see https://www.php.net/manual/en/function.natcasesort.php
936 */
937 public static function natcasesort($array)
938 {
939 natcasesort($array);
940 return $array;
941 }
942
943 /**
944 * Sort an array by values using a user-defined comparison function.
945 *
946 * @param array $array
947 * @return array
948 * @see https://www.php.net/manual/en/function.usort.php
949 */
950 public static function usort($array, callable $callback)
951 {
952 usort($array, $callback);
953 return $array;
954 }
955
956 /**
957 * Sort an array with a user-defined comparison
958 * function and maintain index association.
959 *
960 * @param array $array
961 * @return array
962 * @see https://www.php.net/manual/en/function.uasort.php
963 */
964 public static function uasort($array, callable $callback)
965 {
966 uasort($array, $callback);
967 return $array;
968 }
969
970 /**
971 * Sort an array by keys using a user-defined comparison function.
972 *
973 * @param array $array
974 * @return array
975 * @see https://www.php.net/manual/en/function.uksort.php
976 */
977 public static function uksort($array, callable $callback)
978 {
979 uksort($array, $callback);
980 return $array;
981 }
982
983 /**
984 * Recursively sort an array by keys and values.
985 *
986 * @param array $array
987 * @param int $options
988 * @param bool $desc
989 * @return array
990 */
991 public static function sortRecursive($array, $options = SORT_REGULAR, $desc = false)
992 {
993 foreach ($array as &$value) {
994 if (is_array($value)) {
995 $value = static::sortRecursive($value, $options, $desc);
996 }
997 }
998
999 if (static::isAssoc($array)) {
1000 $desc
1001 ? krsort($array, $options)
1002 : ksort($array, $options);
1003 } else {
1004 $desc
1005 ? rsort($array, $options)
1006 : sort($array, $options);
1007 }
1008
1009 return $array;
1010 }
1011
1012 /**
1013 * Conditionally compile classes from an array into a CSS class list.
1014 *
1015 * @param array $array
1016 * @return string
1017 */
1018 public static function toCssClasses($array)
1019 {
1020 $classList = static::wrap($array);
1021
1022 $classes = [];
1023
1024 foreach ($classList as $class => $constraint) {
1025 if (is_numeric($class)) {
1026 $classes[] = $constraint;
1027 } elseif ($constraint) {
1028 $classes[] = $class;
1029 }
1030 }
1031
1032 return implode(' ', $classes);
1033 }
1034
1035 /**
1036 * Transforms an array to \stdClass
1037 * @param array $array
1038 * @return \stdClass
1039 */
1040 public static function toObject($array)
1041 {
1042 return StdObject::create($array);
1043 }
1044
1045 /**
1046 * Filter the array using the given callback.
1047 *
1048 * @param array $array
1049 * @param callable $callback
1050 * @return array
1051 */
1052 public static function where($array, callable $callback)
1053 {
1054 return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
1055 }
1056
1057 /**
1058 * Filter items where the value is not null.
1059 *
1060 * @param array $array
1061 * @return array
1062 */
1063 public static function whereNotNull($array)
1064 {
1065 return static::where($array, function ($value) {
1066 return ! is_null($value);
1067 });
1068 }
1069
1070 /**
1071 * Filter items where the value is not null.
1072 *
1073 * @param array $array
1074 * @return array
1075 */
1076 public static function whereNotTrue($array, $strict = false)
1077 {
1078 return static::where($array, function ($value) use ($strict) {
1079 return $strict ? $value === false : !$value;
1080 });
1081 }
1082
1083 /**
1084 * If the given value is not an array and not null, wrap it in one.
1085 *
1086 * @param mixed $value
1087 * @return array
1088 */
1089 public static function wrap($value)
1090 {
1091 if (is_null($value)) {
1092 return [];
1093 }
1094
1095 return is_array($value) ? $value : [$value];
1096 }
1097
1098 /**
1099 * Maps a function to all non-iterable elements of an array or an object.
1100 *
1101 * This is similar to `array_walk_recursive()` but acts upon objects too.
1102 *
1103 * @param mixed $value The array, object, or scalar.
1104 * @param callable $callback The function to map onto $value.
1105 * @see https://developer.wordpress.org/reference/functions/map_deep/
1106 *
1107 * @return mixed The value with the callback applied to all non-arrays and non-objects inside it.
1108 */
1109 public static function map($value, $callback)
1110 {
1111 return map_deep($value, $callback);
1112 }
1113
1114 /**
1115 * Check if the value(s) exist in an array using "dot" notation.
1116 *
1117 * @param array $array
1118 * @param string|array $values
1119 * @return bool
1120 */
1121 public static function contains(array $array, $values)
1122 {
1123 $result = [];
1124
1125 $values = is_array($values) ? $values : [$values];
1126
1127 foreach ($values as $value) {
1128
1129 if (in_array($value, $array)) {
1130 $result[] = $value;
1131 continue;
1132 }
1133
1134 $segments = explode('.', $value);
1135
1136 $value = array_pop($segments);
1137
1138 $nested = (array) static::get($array, implode('.', $segments));
1139
1140 if ($nested && in_array($value, $nested)) {
1141 $result[] = $value;
1142 }
1143 }
1144
1145 return count($result) === count($values);
1146 }
1147
1148 /**
1149 * Check if the any value exist in an array using "dot" notation.
1150 *
1151 * @param array $array
1152 * @param string|array $values
1153 * @return bool
1154 */
1155 public static function containsAny(array $array, $values)
1156 {
1157 $result = [];
1158
1159 $values = is_array($values) ? $values : [$values];
1160
1161 foreach ($values as $value) {
1162
1163 if (in_array($value, $array)) {
1164 return true;
1165 }
1166
1167 $segments = explode('.', $value);
1168
1169 $value = array_pop($segments);
1170
1171 $nested = (array) static::get($array, implode('.', $segments));
1172
1173 if ($nested && in_array($value, $nested)) {
1174 return true;
1175 }
1176 }
1177
1178 return false;
1179 }
1180
1181 /**
1182 * Compare two nested arrays side by side
1183 * @param array $array1
1184 * @param array $array2
1185 * @param array $path
1186 * @return array
1187 */
1188 public static function compare($array1, $array2, $path = [])
1189 {
1190 $differences = [];
1191
1192 foreach ($array1 as $key => $value1) {
1193 // Check if the key exists in the second array
1194 if (!array_key_exists($key, $array2)) {
1195 $differences[implode('.', array_merge($path, [$key]))] = [
1196 'array_1' => $value1,
1197 'array_2' => null,
1198 ];
1199 } else {
1200 // If the value is an array, recursively compare
1201 if (is_array($value1) && is_array($array2[$key])) {
1202 $differences = array_merge($differences, static::compare(
1203 $value1, $array2[$key], array_merge($path, [$key])
1204 ));
1205 } else {
1206 // Compare values
1207 if ($value1 !== $array2[$key]) {
1208 $differences[implode('.', array_merge($path, [$key]))] = [
1209 'array_1' => $value1,
1210 'array_2' => $array2[$key],
1211 ];
1212 }
1213 }
1214 }
1215 }
1216
1217 // Check for keys in the second array that are not in the first array
1218 foreach ($array2 as $key => $value2) {
1219 if (!array_key_exists($key, $array1)) {
1220 $differences[implode('.', array_merge($path, [$key]))] = [
1221 'array_1' => null,
1222 'array_2' => $value2,
1223 ];
1224 }
1225 }
1226
1227 return $differences;
1228 }
1229
1230 /**
1231 * Merge the items from the first array into the
1232 * second array if the second array is missing it.
1233 *
1234 * @param array &$array1
1235 * @param array &$array2
1236 * @return array
1237 */
1238 public static function mergeMissing(&$array1, &$array2)
1239 {
1240 foreach ($array1 as $key => $value1) {
1241 // If the key exists in the second array
1242 if (array_key_exists($key, $array2)) {
1243 // If the value is an array, recursively add missing items
1244 if (is_array($value1) && is_array($array2[$key])) {
1245 static::mergeMissing($value1, $array2[$key]);
1246 }
1247 } else {
1248 // If the key doesn't exist in the second array,
1249 // then add it with the corresponding value
1250 $array2[$key] = $value1;
1251 }
1252 }
1253
1254 return $array2;
1255 }
1256
1257 /**
1258 * Recursively merge the given array with defaults.
1259 * Overwrite $array with $defaults if only $array
1260 * contains null or empty values or doesn't exist.
1261 *
1262 * @param array $array
1263 * @param array $defaults
1264 * @return array
1265 */
1266 public static function mergeMissingValues(array $array, array $defaults)
1267 {
1268 $merged = array_merge($defaults, $array);
1269
1270 foreach ($merged as $key => $value) {
1271 if (
1272 is_array($value) &&
1273 isset($defaults[$key]) &&
1274 is_array($defaults[$key])
1275 ) {
1276 // Recursively merge arrays
1277 $merged[$key] = static::mergeMissingValues(
1278 $value, $defaults[$key]
1279 );
1280 } elseif (
1281 isset($defaults[$key]) &&
1282 (is_null($value) || $value === '')
1283 ) {
1284 // Replace null or empty values
1285 $merged[$key] = $defaults[$key];
1286 }
1287 }
1288
1289 return $merged;
1290 }
1291
1292 /**
1293 * Return matching items from array (similar to mysql's %LIKE%)
1294 *
1295 * @param string|regex $pattern
1296 * @param array $array
1297 * @return array|false
1298 */
1299 public static function like($array, $pattern)
1300 {
1301 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1302 $pattern = '~'. preg_quote($pattern, '~') . '~i';
1303 }
1304
1305 return preg_grep($pattern, $array);
1306 }
1307
1308 /**
1309 * Return non-matching items from array (similar to mysql's NOT %LIKE%)
1310 *
1311 * @param string|regex $pattern
1312 * @param array $array
1313 * @return array|false
1314 */
1315 public static function notLike($array, $pattern)
1316 {
1317 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1318 $pattern = '~'. preg_quote($pattern, '~') . '~i';
1319 }
1320
1321 return preg_grep($pattern, $array, PREG_GREP_INVERT);
1322 }
1323
1324 /**
1325 * Return matching starting of items from array (similar to mysql's %LIKE)
1326 *
1327 * @param string|regex $pattern
1328 * @param array $array
1329 * @return array|false
1330 */
1331 public static function startsLike($array, $pattern)
1332 {
1333 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1334 $pattern = '~^'. preg_quote($pattern, '~') . '~i';
1335 }
1336
1337 return preg_grep($pattern, $array);
1338 }
1339
1340 /**
1341 * Return non-matching starting of items from array (similar to mysql's NOT %LIKE)
1342 *
1343 * @param string|regex $pattern
1344 * @param array $array
1345 * @return array|false
1346 */
1347 public static function DoesNotStartLike($array, $pattern)
1348 {
1349 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1350 $pattern = '~^(?!' . preg_quote($pattern, '~') . ')~i';
1351 }
1352
1353 return preg_grep($pattern, $array);
1354 }
1355
1356 /**
1357 * Return matching ending of items from array (similar to mysql's LIKE%)
1358 *
1359 * @param string|regex $pattern
1360 * @param array $array
1361 * @return array|false
1362 */
1363 public static function endsLike($array, $pattern)
1364 {
1365 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1366 $pattern = '~'. preg_quote($pattern, '~') . '$~i';
1367 }
1368
1369 return preg_grep($pattern, $array);
1370 }
1371
1372 /**
1373 * Return non-matching ending of items from array (similar to mysql's NOT LIKE%)
1374 *
1375 * @param string|regex $pattern
1376 * @param array $array
1377 * @return array|false
1378 */
1379 public static function DoesNotEndLike($array, $pattern)
1380 {
1381 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1382 $pattern = '~'. preg_quote($pattern, '~') . '$~i';
1383 }
1384
1385 return preg_grep($pattern, $array, PREG_GREP_INVERT);
1386 }
1387
1388 /**
1389 * Return matching items from array by keys
1390 *
1391 * @param string|regex $pattern
1392 * @param array $array
1393 * @return array|false
1394 */
1395 public static function keysLike($array, $pattern)
1396 {
1397 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1398 $pattern = '~'. preg_quote($pattern, '~') . '~i';
1399 }
1400
1401 $values = [];
1402
1403 $keys = preg_grep($pattern, array_keys($array));
1404
1405 foreach ($keys as $key) {
1406 $values[$key] = $array[$key];
1407 }
1408
1409 return $values;
1410 }
1411
1412 /**
1413 * Return non-matching items from array by keys
1414 *
1415 * @param string|regex $pattern
1416 * @param array $array
1417 * @return array|false
1418 */
1419 public static function keysNotLike($array, $pattern)
1420 {
1421 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1422 $pattern = '~'. preg_quote($pattern, '~') . '~i';
1423 }
1424
1425 $values = [];
1426
1427 $keys = preg_grep($pattern, array_keys($array), 1);
1428
1429 foreach ($keys as $key) {
1430 $values[$key] = $array[$key];
1431 }
1432
1433 return $values;
1434 }
1435
1436 /**
1437 * Return matching starting of items from array by keys
1438 *
1439 * @param string|regex $pattern
1440 * @param array $array
1441 * @return array|false
1442 */
1443 public static function keysStartLike($array, $pattern)
1444 {
1445 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1446 $pattern = '~^'. preg_quote($pattern, '~') . '~i';
1447 }
1448
1449 $values = [];
1450
1451 $keys = preg_grep($pattern, array_keys($array));
1452
1453 foreach ($keys as $key) {
1454 $values[$key] = $array[$key];
1455 }
1456
1457 return $values;
1458 }
1459
1460 /**
1461 * Return non-matching starting of items from array by keys
1462 *
1463 * @param string|regex $pattern
1464 * @param array $array
1465 * @return array|false
1466 */
1467 public static function keysDoesNotStartLike($array, $pattern)
1468 {
1469 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1470 $pattern = '~^(?!' . preg_quote($pattern, '~') . ')~i';
1471 }
1472
1473 $values = [];
1474
1475 $keys = preg_grep($pattern, array_keys($array));
1476
1477 foreach ($keys as $key) {
1478 $values[$key] = $array[$key];
1479 }
1480
1481 return $values;
1482 }
1483
1484 /**
1485 * Return matching ending of items from array by keys
1486 *
1487 * @param string|regex $pattern
1488 * @param array $array
1489 * @return array|false
1490 */
1491 public static function keysEndLike($array, $pattern)
1492 {
1493 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1494 $pattern = '~'. preg_quote($pattern, '~') . '$~i';
1495 }
1496
1497 $values = [];
1498
1499 $keys = preg_grep($pattern, array_keys($array));
1500
1501 foreach ($keys as $key) {
1502 $values[$key] = $array[$key];
1503 }
1504
1505 return $values;
1506 }
1507
1508 /**
1509 * Return non-matching ending of items from array by keys
1510 *
1511 * @param string|regex $pattern
1512 * @param array $array
1513 * @return array|false
1514 */
1515 public static function keysDoesNotEndLike($array, $pattern)
1516 {
1517 if (!preg_match('/^([\/#~]).*\1$/', $pattern)) {
1518 $pattern = '~'. preg_quote($pattern, '~') . '$~i';
1519 }
1520
1521 $values = [];
1522
1523 $keys = preg_grep($pattern, array_keys($array), PREG_GREP_INVERT);
1524
1525 foreach ($keys as $key) {
1526 $values[$key] = $array[$key];
1527 }
1528
1529 return $values;
1530 }
1531
1532 /**
1533 * Insert a new item in the array at the given position.
1534 *
1535 * @param array $array
1536 * @param int $pos
1537 * @param mixed $newItem
1538 * @return array
1539 */
1540 public static function insertAt($array, $pos, $newItem)
1541 {
1542 if (!isset($array[$pos])) {
1543 $array[] = $newItem;
1544 } else {
1545 $array = array_splice($array, $pos, 0, $newItem);
1546 }
1547
1548 return $array;
1549 }
1550
1551 /**
1552 * Inserts an item before the specified key in the given array. If the
1553 * key is not found, inserts the item at the beginning of the array.
1554 *
1555 * @param array $array
1556 * @param mixed $key
1557 * @param mixed $newKey
1558 * @param mixed $newValue
1559 * @return array $newArray
1560 */
1561 public static function insertBefore($array, $key, $newKey, $newValue)
1562 {
1563 $newArray = [];
1564 $keyFound = false;
1565
1566 foreach ($array as $k => $v) {
1567 if ($k === $key) {
1568 $newArray[$newKey] = $newValue;
1569 $keyFound = true;
1570 }
1571 $newArray[$k] = $v;
1572 }
1573
1574 if (!$keyFound) {
1575 $newArray = [$newKey => $newValue] + $newArray;
1576 }
1577
1578 return $newArray;
1579 }
1580
1581 /**
1582 * Inserts an item after the specified key in the given array. If the
1583 * key is not found, inserts the item at the end of the array.
1584 *
1585 * @param array $array
1586 * @param mixed $key
1587 * @param mixed $newKey
1588 * @param mixed $newValue
1589 * @return array $newArray
1590 */
1591 public static function insertAfter($array, $key, $newKey, $newValue): array {
1592 $newArray = [];
1593 $keyFound = false;
1594
1595 foreach ($array as $k => $v) {
1596 $newArray[$k] = $v;
1597 if ($k === $key) {
1598 $newArray[$newKey] = $newValue;
1599 $keyFound = true;
1600 }
1601 }
1602
1603 if (!$keyFound) {
1604 $newArray[$newKey] = $newValue;
1605 }
1606
1607 return $newArray;
1608 }
1609
1610 /**
1611 * Tests whether at least one element in the array passes
1612 * the test implemented by the provided callback.
1613 *
1614 * @param array $array
1615 * @param callable $callback
1616 * @return bool
1617 */
1618 public static function some($array, callable $callback)
1619 {
1620 foreach ($array as $k => $v) {
1621 if ($callback($v, $k, $array)) {
1622 return true;
1623 }
1624 }
1625
1626 return false;
1627 }
1628
1629 /**
1630 * Tests whether all elements in the array pass the
1631 * test implemented by the provided callback.
1632 *
1633 * @param array $array
1634 * @param callable $callback
1635 * @return bool
1636 */
1637 public static function every($array, callable $callback)
1638 {
1639 foreach ($array as $k => $v) {
1640 if (!$callback($v, $k, $array)) {
1641 return false;
1642 }
1643 }
1644
1645 return true;
1646 }
1647
1648 /**
1649 * Finds the first element in the array that satisfies the
1650 * condition implemented by the callback function.
1651 *
1652 * @param array $array
1653 * @param callable $callback
1654 * @return mixed
1655 */
1656 public static function find($array, callable $callback, $findKey = false)
1657 {
1658 foreach ($array as $k => $v) {
1659 if ($callback($v, $k, $array)) {
1660 return $findKey ? $k : $v;
1661 }
1662 }
1663
1664 return null;
1665 }
1666
1667 /**
1668 * Finds the first key in the array that satisfies the
1669 * condition implemented by the callback function.
1670 *
1671 * @param array $array
1672 * @param callable $callback
1673 * @return mixed
1674 */
1675 public static function findKey($array, callable $callback)
1676 {
1677 return static::find($array, $callback, true);
1678 }
1679 }
1680