Complex classes like Arrgh 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 Arrgh, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | class Arrgh implements \ArrayAccess, \Iterator |
||
31 | { |
||
32 | const PHP_SORT_DIRECTION_56 = 1; |
||
33 | const PHP_SORT_DIRECTION_7 = -1; |
||
34 | |||
35 | private $array; |
||
36 | private $array_position; |
||
37 | private $original_array; |
||
38 | private $terminate; |
||
39 | private $keep_once; |
||
40 | private $last_value; |
||
41 | |||
42 | private static $php_version; |
||
43 | private static $php_sort_direction; |
||
44 | |||
45 | /** |
||
46 | * Creates a new Arrgh object |
||
47 | * |
||
48 | * @method __construct |
||
49 | * @param mixed $array Optional parameter that can be either an array or another Arrgh object. |
||
50 | */ |
||
51 | public function __construct($array = []) |
||
61 | |||
62 | /** |
||
63 | * Invoke calls for Arrgh instances. |
||
64 | * @internal |
||
65 | */ |
||
66 | public function __call($method, $args) |
||
70 | |||
71 | /** |
||
72 | * Returns a native array. |
||
73 | * |
||
74 | * @method toArray |
||
75 | * @return array Returns an array. |
||
76 | */ |
||
77 | public function toArray() |
||
87 | |||
88 | /** |
||
89 | * Tells Arrgh to either return iteself or a value where it would otherwise |
||
90 | * had terminated the chain and return a value. |
||
91 | * |
||
92 | * The default behaviour is to break the chain on terminating methods like: |
||
93 | * |
||
94 | * - join |
||
95 | * - pop |
||
96 | * |
||
97 | * @method keepChain |
||
98 | * @param bool $value Set true to keep and false to terminate. |
||
99 | * @param bool $keep_once Set true to automatically switch of again |
||
100 | * after one call to a terminating method. |
||
101 | * @see keep |
||
102 | * @see keepOnce |
||
103 | * @see breakChain |
||
104 | * @return Arrgh\Arrgh self |
||
105 | */ |
||
106 | public function keepChain($value = true, $keep_once = false) |
||
112 | |||
113 | /** |
||
114 | * Tells Arrgh to return itself where it would otheriwse had terminated |
||
115 | * the chain and returned a value. |
||
116 | * |
||
117 | * @method keep |
||
118 | * @see keepChain |
||
119 | * @return Arrgh\Arrgh self |
||
120 | */ |
||
121 | public function keep() |
||
125 | |||
126 | /** |
||
127 | * Tells Arrgh to return iteself where it would otherwise had terminated |
||
128 | * the chain and return a value, but do it just once. |
||
129 | * |
||
130 | * @method keepOnce |
||
131 | * @see keepChain |
||
132 | * @return Arrgh\Arrgh self |
||
133 | */ |
||
134 | public function keepOnce() |
||
138 | |||
139 | /** |
||
140 | * Tells Arrgh to return to its normal behaviour and return values rather |
||
141 | * than itself when terminating methods are called. |
||
142 | * |
||
143 | * @method breakChain |
||
144 | * @see keepChain |
||
145 | * @return Arrgh\Arrgh self |
||
146 | */ |
||
147 | public function breakChain() |
||
151 | |||
152 | /** |
||
153 | * ArrayAccess |
||
154 | * @internal |
||
155 | */ |
||
156 | public function offsetExists($offset) |
||
160 | |||
161 | /** |
||
162 | * ArrayAccess |
||
163 | * @internal |
||
164 | */ |
||
165 | public function offsetGet($offset) |
||
169 | |||
170 | /** |
||
171 | * ArrayAccess |
||
172 | * @internal |
||
173 | */ |
||
174 | public function offsetSet($offset, $value) |
||
182 | |||
183 | /** |
||
184 | * ArrayAccess |
||
185 | * @internal |
||
186 | */ |
||
187 | public function offsetUnset($offset) |
||
191 | |||
192 | /** |
||
193 | * Iterator |
||
194 | * @internal |
||
195 | */ |
||
196 | public function current() |
||
204 | |||
205 | /** |
||
206 | * Iterator |
||
207 | * @internal |
||
208 | */ |
||
209 | public function key() |
||
213 | |||
214 | /** |
||
215 | * Iterator |
||
216 | * @internal |
||
217 | */ |
||
218 | public function next() |
||
222 | |||
223 | /** |
||
224 | * Iterator |
||
225 | * @internal |
||
226 | */ |
||
227 | public function rewind() |
||
231 | |||
232 | /** |
||
233 | * Iterator |
||
234 | * @internal |
||
235 | */ |
||
236 | public function valid() |
||
240 | |||
241 | /** |
||
242 | * Creates a new Arrgh object (chain) |
||
243 | * |
||
244 | * @param mixed $array Optional parameter that can be either an array or another Arrgh object. |
||
245 | * @see chain |
||
246 | * @return Arrgh\Arrgh A new Arrgh object |
||
247 | */ |
||
248 | public static function arr($array = []) |
||
252 | |||
253 | /** |
||
254 | * Creates a new Arrgh object (chain) |
||
255 | * |
||
256 | * @param mixed $array Optional parameter that can be either an array or another Arrgh object. |
||
257 | * @see arr |
||
258 | * @return Arrgh\Arrgh A new Arrgh object |
||
259 | */ |
||
260 | public static function chain($array = []) |
||
264 | |||
265 | /** |
||
266 | * Invoke calls for static Arrgh calls. |
||
267 | * @internal |
||
268 | */ |
||
269 | public static function __callStatic($method, $args) |
||
282 | |||
283 | /** |
||
284 | * Returns list of supported functions partitioned into types of functions. |
||
285 | * |
||
286 | * @method allFunctions |
||
287 | * @return array Associative array with function type as key point |
||
288 | * to a list of functions of that type |
||
289 | */ |
||
290 | public static function allFunctions() |
||
302 | |||
303 | /** |
||
304 | * Given the PHP version this functions returns the sort integer to use |
||
305 | * for equal values in functions like `usort`. Optionally you can pass in |
||
306 | * the existing value like so: |
||
307 | * |
||
308 | * usort($input, function ($a, $b) { |
||
309 | * return Arrgh::getSortDirection($a - $b); |
||
310 | * }); |
||
311 | * |
||
312 | * This will ensure that the custom sort function will work in both PHP |
||
313 | * version 5.6.x and 7.x |
||
314 | * |
||
315 | * @method getSortDirection |
||
316 | * @param integer $direction An integer like value |
||
317 | * @return integer Returns a sort integer for a sort or compare function |
||
318 | */ |
||
319 | public static function getSortDirection($direction = null) |
||
330 | |||
331 | /** |
||
332 | * Wraps a callable with the purpose of fixing bad PHP sort implementations. |
||
333 | * |
||
334 | * @internal |
||
335 | * |
||
336 | * @method wrapCallable |
||
337 | * @param \Closure $callable A sort function |
||
338 | * @return \Closure A new closeure |
||
339 | */ |
||
340 | private static function wrapCallable(Closure $callable) |
||
351 | |||
352 | |||
353 | /** |
||
354 | * Transforms the incoming calls to native calls. |
||
355 | * |
||
356 | * @internal |
||
357 | * |
||
358 | * @method invoke |
||
359 | * @param string $method Name of method to invoke. |
||
360 | * @param array $args Arguments for method. |
||
361 | * @param Arrgh $object Optionally invoke on $object. |
||
362 | * |
||
363 | * @return mixed Can return anything. |
||
364 | */ |
||
365 | private static function invoke($method, $args, Arrgh $object = null) |
||
437 | |||
438 | /** |
||
439 | * Based on input method finds handler, function and post handler. |
||
440 | * |
||
441 | * @internal |
||
442 | * |
||
443 | * @return array Returns a tuble of [handler, function, postHandler] |
||
444 | */ |
||
445 | private static function findFunction($method) |
||
472 | |||
473 | /** |
||
474 | * Handles special case: asort - In PHP5 reverses equals ("arsort" doen't mess up for some reason) |
||
475 | * |
||
476 | * @internal |
||
477 | */ |
||
478 | private static function handleCaseAsort(&$matching_function, &$args) |
||
483 | |||
484 | /** |
||
485 | * Handles special case: array_column - Native array_column filters away null values. |
||
486 | * |
||
487 | * That means you cannot use array_column for multisort since array size no longer matches. |
||
488 | * This version of array_column returns null if the column is missing. |
||
489 | * |
||
490 | * @internal |
||
491 | */ |
||
492 | private static function handleCaseArrayColumn(&$matching_handler, &$matching_function, &$post_handler, &$args) |
||
512 | |||
513 | /** |
||
514 | * Handler: _call |
||
515 | * |
||
516 | * Calls the native function directly. |
||
517 | * |
||
518 | * @internal |
||
519 | */ |
||
520 | private static function _call($function, $args) |
||
524 | |||
525 | /** |
||
526 | * Handler: _rotateRight |
||
527 | * |
||
528 | * Shifts of the first argument (callable) and pushes it to the end. |
||
529 | * |
||
530 | * @internal |
||
531 | */ |
||
532 | private static function _rotateRight($function, $args) |
||
538 | |||
539 | /** |
||
540 | * Handler: _swapTwoFirst |
||
541 | * |
||
542 | * Swaps the first two args. |
||
543 | * |
||
544 | * @internal |
||
545 | */ |
||
546 | private static function _swapTwoFirst($function, $args) |
||
554 | |||
555 | /** |
||
556 | * Handler: _copy |
||
557 | * |
||
558 | * Makes a copy of the array and returns it after invoking function. |
||
559 | * |
||
560 | * @internal |
||
561 | */ |
||
562 | private static function _copy($function, $args) |
||
568 | |||
569 | /** |
||
570 | * Handler: _copyMultiple |
||
571 | * |
||
572 | * If multiple arrays are passed as arguments mulitple will be returned. |
||
573 | * Otherwise _copy is used. |
||
574 | * |
||
575 | * @internal |
||
576 | */ |
||
577 | private static function _copyMultiple($function, $args) |
||
591 | |||
592 | /** |
||
593 | * Handler: _copyValue |
||
594 | * |
||
595 | * Makes a copy of the array and returns it after invoking function. |
||
596 | * |
||
597 | * @internal |
||
598 | */ |
||
599 | private static function _copyValue($function, $args, $object = null) |
||
608 | |||
609 | /** |
||
610 | * Handler: _arrgh |
||
611 | * |
||
612 | * The handler for non-native functions |
||
613 | * |
||
614 | * @internal |
||
615 | */ |
||
616 | private static function _arrgh($function, $args) |
||
621 | |||
622 | /** |
||
623 | * A mapping function for associative arrays (keeps keys). |
||
624 | * |
||
625 | * @method map_assoc |
||
626 | * |
||
627 | * @param array $array Array or array-like value. |
||
628 | * @param Closure $callable Mapping function |
||
629 | * |
||
630 | * @return array Returns mapped associative array. |
||
631 | */ |
||
632 | private static function arr_map_assoc($array, Closure $callable) |
||
637 | |||
638 | /** |
||
639 | * Sort an array of associative arrays by key. It checks the first two values for type |
||
640 | * either sorts by number or using strcmp. If a key is missing entries are moved to the top |
||
641 | * (or bottom depending on $direction) |
||
642 | */ |
||
643 | private static function arr_sort_by($array, $key, $direction = "ASC") |
||
661 | |||
662 | private static function arr_collapse($array) |
||
672 | |||
673 | private static function arr_contains($array, $search, $key = null) |
||
684 | |||
685 | private static function arr_except($array, $except) |
||
706 | |||
707 | private static function arr_only($array, $only) |
||
730 | |||
731 | /** |
||
732 | * Get for multi-dimensional arrays |
||
733 | * |
||
734 | * @param array An array to query on |
||
735 | * @param path|array A string representing the path to traverse. |
||
736 | * Optionally pass as [ $path, ...$functions ] if `!$` is used |
||
737 | * @param bool Collapse resulting data-set |
||
738 | * @throws InvalidArgumentException Thrown when a path cannot be reached in case $array does |
||
739 | * not correspond to path type. E.g. collection expected |
||
740 | * but a simple value was encountered. |
||
741 | */ |
||
742 | private static function arr_get($array, $path, $collapse = false) |
||
751 | |||
752 | /* arr_get: Traverses path to get value */ |
||
753 | private static function _arr_get_traverse($data, $path, $collapse = false, $functions = []) |
||
814 | |||
815 | private static function _arr_get_traverse_collection($path, $next_node, $collapse, $functions) |
||
850 | |||
851 | /* arr_get: Find next node by index */ |
||
852 | private static function _arr_get_traverse_next_node_index($data, $plug_index) |
||
866 | |||
867 | /* arr_get: Find next node by key */ |
||
868 | private static function _arr_get_traverse_next_node_key($data, $is_collection, $next_key) |
||
888 | |||
889 | /* arr_get: Invoke custom filter function on path */ |
||
890 | private static function _arr_get_traverse_apply_custom_function($data, $functions, $path) |
||
897 | |||
898 | private static function arr_is_collection($mixed) |
||
902 | |||
903 | /** |
||
904 | * Return the depth of a collection hiearchy. Zero based. |
||
905 | * |
||
906 | * @param array A collection |
||
907 | * @return int `null` if $array is not a collection. |
||
908 | */ |
||
909 | private static function arr_depth($array) |
||
929 | |||
930 | /** |
||
931 | * Partion the input based on the result of the callback function. |
||
932 | * |
||
933 | * @param array $array A collection |
||
934 | * @param \Closeure $callable A callable returning true or false depending on which way to partion the element—left or right. |
||
935 | * @return array An array with two arrays—left and right: [left, right] |
||
936 | */ |
||
937 | private static function arr_partition($array, Closure $callable) |
||
950 | |||
951 | private static function arr_even($array) |
||
955 | |||
956 | private static function arr_odd($array) |
||
960 | |||
961 | /* Synonym of shift */ |
||
962 | private static function arr_head($array) |
||
966 | |||
967 | private static function arr_first($array) |
||
974 | |||
975 | private static function arr_last($array) |
||
982 | |||
983 | private static function arr_tail($array) |
||
987 | |||
988 | // _arrgh |
||
989 | static private $arr_functions = [ |
||
990 | "collapse", |
||
991 | "contains", |
||
992 | "except", |
||
993 | "map_assoc", |
||
994 | "only", |
||
995 | "sort_by", |
||
996 | 'depth', |
||
997 | 'even', |
||
998 | 'first', |
||
999 | 'get', |
||
1000 | 'head', |
||
1001 | 'is_collection', |
||
1002 | 'last', |
||
1003 | 'odd', |
||
1004 | 'partition', |
||
1005 | 'tail', |
||
1006 | ]; |
||
1007 | |||
1008 | // _call |
||
1009 | static private $simple_functions = [ |
||
1010 | "array_change_key_case", |
||
1011 | "array_chunk", |
||
1012 | "array_column", |
||
1013 | "array_combine", |
||
1014 | "array_count_values", |
||
1015 | "array_diff", |
||
1016 | "array_diff_assoc", |
||
1017 | "array_diff_key", |
||
1018 | "array_diff_uassoc", |
||
1019 | "array_diff_ukey", |
||
1020 | "array_fill", |
||
1021 | "array_fill_keys", |
||
1022 | "array_filter", |
||
1023 | "array_flip", |
||
1024 | "array_intersect", |
||
1025 | "array_intersect_assoc", |
||
1026 | "array_intersect_key", |
||
1027 | "array_intersect_uassoc", |
||
1028 | "array_intersect_ukey", |
||
1029 | "array_keys", |
||
1030 | "array_merge", |
||
1031 | "array_merge_recursive", |
||
1032 | "array_pad", |
||
1033 | "array_product", |
||
1034 | "array_rand", |
||
1035 | "array_reduce", |
||
1036 | "array_replace", |
||
1037 | "array_replace_recursive", |
||
1038 | "array_reverse", |
||
1039 | "array_slice", |
||
1040 | "array_sum", |
||
1041 | "array_udiff", |
||
1042 | "array_udiff_assoc", |
||
1043 | "array_udiff_uassoc", |
||
1044 | "array_uintersect", |
||
1045 | "array_uintersect_assoc", |
||
1046 | "array_uintersect_uassoc", |
||
1047 | "array_unique", |
||
1048 | "array_values", |
||
1049 | "count", |
||
1050 | "max", |
||
1051 | "min", |
||
1052 | "range", |
||
1053 | "sizeof", |
||
1054 | ]; |
||
1055 | |||
1056 | // _copy |
||
1057 | static private $mutable_functions = [ |
||
1058 | "array_push", |
||
1059 | "array_splice", |
||
1060 | "array_unshift", |
||
1061 | "array_walk", |
||
1062 | "array_walk_recursive", |
||
1063 | "arsort", |
||
1064 | "asort", |
||
1065 | "krsort", |
||
1066 | "ksort", |
||
1067 | "natcasesort", |
||
1068 | "natsort", |
||
1069 | "rsort", |
||
1070 | "shuffle", |
||
1071 | "sort", |
||
1072 | "uasort", |
||
1073 | "uksort", |
||
1074 | "usort", |
||
1075 | ]; |
||
1076 | |||
1077 | // _copyMultiple |
||
1078 | static private $mutable_functions_multiple = [ |
||
1079 | "array_multisort", |
||
1080 | ]; |
||
1081 | |||
1082 | // _copyValue |
||
1083 | static private $mutable_value_functions = [ |
||
1084 | "array_pop", |
||
1085 | "array_shift", |
||
1086 | "end", |
||
1087 | ]; |
||
1088 | |||
1089 | // _rotateRight |
||
1090 | static private $reverse_functions = [ |
||
1091 | "array_map", |
||
1092 | ]; |
||
1093 | |||
1094 | // _swapTwoFirst |
||
1095 | static private $swapped_functions = [ |
||
1096 | "array_key_exists", |
||
1097 | "array_search", |
||
1098 | "implode", |
||
1099 | "in_array", |
||
1100 | "join", |
||
1101 | ]; |
||
1102 | |||
1103 | static private $starters = [ |
||
1104 | "array_fill", |
||
1105 | "array_fill_keys", |
||
1106 | "range", |
||
1107 | ]; |
||
1108 | |||
1109 | static private $terminators = [ |
||
1110 | "array_pop", |
||
1111 | "array_shift", |
||
1112 | "array_sum", |
||
1113 | "count", |
||
1114 | "first", |
||
1115 | "head", |
||
1116 | "join", |
||
1117 | "last", |
||
1118 | "max", |
||
1119 | "min", |
||
1120 | "sizeof", |
||
1121 | ]; |
||
1122 | |||
1123 | static private $reverse_result_functions = [ |
||
1124 | "uasort", |
||
1125 | "uksort", |
||
1126 | "usort", |
||
1127 | "asort", |
||
1128 | ]; |
||
1129 | } |
||
1130 |
If you implement
__call
and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.This is often the case, when
__call
is implemented by a parent class and only the child class knows which methods exist: