Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Arr often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Arr, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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 |
|
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) |
|
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 |
|
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) |
|
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 |
|
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 |
|
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 | 20 | public static function exists($array, $key): bool |
|
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 |
|
217 | |||
218 | /** |
||
219 | * Updates data at the given path. |
||
220 | * |
||
221 | * @param array $array |
||
222 | * @param sting $key |
||
223 | * @param callable $callback Callback to update the value. |
||
224 | * |
||
225 | * @return mixed Updated data. |
||
226 | */ |
||
227 | 1 | public static function update(array $array, string $key, callable $callback) |
|
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 | 13 | public static function forget(array &$array, $keys) |
|
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) |
|
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) |
|
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 |
|
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 |
|
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 |
|
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 |
|
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) |
|
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) |
|
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) |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
612 | |||
613 | /** |
||
614 | * Merges two or more arrays into one recursively. |
||
615 | * |
||
616 | * @return array |
||
617 | */ |
||
618 | 4 | public static function merge(): array |
|
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 |
|
668 | |||
669 | /** |
||
670 | * Extend one array with another. |
||
671 | * |
||
672 | * @return array |
||
673 | */ |
||
674 | 3 | public static function extend(): array |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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) |
|
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() |
|
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( |
|
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) |
|
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() |
|
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) |
|
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) |
|
1051 | |||
1052 | /** |
||
1053 | * Returns whether every element of the array satisfies the given predicate or not. |
||
1054 | * Works with Iterators too. |
||
1055 | * |
||
1056 | * @param array $array |
||
1057 | * @param callable $predicate |
||
1058 | * |
||
1059 | * @return bool |
||
1060 | */ |
||
1061 | 1 | public static function all(array $array, callable $predicate) |
|
1071 | |||
1072 | /** |
||
1073 | * The opposite of filter(). |
||
1074 | * |
||
1075 | * @param array $array |
||
1076 | * @param callable $callback Function to filter values. |
||
1077 | * |
||
1078 | * @return array filtered array. |
||
1079 | */ |
||
1080 | public static function reject(array $array, callable $callback): array |
||
1086 | |||
1087 | /** |
||
1088 | * Filter the array using the given Closure. |
||
1089 | * |
||
1090 | * @param array $array |
||
1091 | * @param callable $callback |
||
1092 | * |
||
1093 | * @return array |
||
1094 | */ |
||
1095 | 1 | public static function where(array $array, callable $callback): array |
|
1107 | |||
1108 | /** |
||
1109 | * Return the first element in an array passing a given truth test. |
||
1110 | * |
||
1111 | * @param array $array |
||
1112 | * @param callable|null $callback |
||
1113 | * @param mixed $default |
||
1114 | * |
||
1115 | * @return mixed |
||
1116 | */ |
||
1117 | 2 | public static function first(array $array, callable $callback = null, $default = null) |
|
1137 | |||
1138 | /** |
||
1139 | * Return the last element in an array passing a given truth test. |
||
1140 | * |
||
1141 | * @param array $array |
||
1142 | * @param callable|null $callback |
||
1143 | * @param mixed $default |
||
1144 | * |
||
1145 | * @return mixed |
||
1146 | */ |
||
1147 | 1 | public static function last(array $array, callable $callback = null, $default = null) |
|
1155 | |||
1156 | /** |
||
1157 | * Get all of the given array except for a specified array of items. |
||
1158 | * |
||
1159 | * @param array $array |
||
1160 | * @param array|string $keys |
||
1161 | * |
||
1162 | * @return array |
||
1163 | */ |
||
1164 | 1 | public static function except(array $array, $keys): array |
|
1170 | |||
1171 | /** |
||
1172 | * Return the default value of the given value. |
||
1173 | * |
||
1174 | * @param mixed $value |
||
1175 | * |
||
1176 | * @return mixed |
||
1177 | */ |
||
1178 | 10 | public static function value($value) |
|
1182 | |||
1183 | /** |
||
1184 | * Recurse through an array, add the leaf items to the $newArray var |
||
1185 | * |
||
1186 | * @param array $subject |
||
1187 | * @param array &$newArray |
||
1188 | * @param array $stack |
||
1189 | * |
||
1190 | * @return string[]|null |
||
1191 | */ |
||
1192 | 3 | private static function recurseCollapse(array $subject, array &$newArray, $stack = []) |
|
1206 | } |
||
1207 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.