Complex classes like ArrayUtil 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 ArrayUtil, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | class ArrayUtil |
||
22 | { |
||
23 | /** |
||
24 | * Reduce an array by a search value and keep the array structure. |
||
25 | * |
||
26 | * Comparison is type strict: |
||
27 | * - For a given needle of type string, integer, array or boolean, |
||
28 | * value and value type must match to occur in result array |
||
29 | * - For a given object, an object within the array must be a reference to |
||
30 | * the same object to match (not just different instance of same class) |
||
31 | * |
||
32 | * Example: |
||
33 | * - Needle: 'findMe' |
||
34 | * - Given array: |
||
35 | * array( |
||
36 | * 'foo' => 'noMatch', |
||
37 | * 'bar' => 'findMe', |
||
38 | * 'foobar => array( |
||
39 | * 'foo' => 'findMe', |
||
40 | * ), |
||
41 | * ); |
||
42 | * - Result: |
||
43 | * array( |
||
44 | * 'bar' => 'findMe', |
||
45 | * 'foobar' => array( |
||
46 | * 'foo' => findMe', |
||
47 | * ), |
||
48 | * ); |
||
49 | * |
||
50 | * See the unit tests for more examples and expected behaviour |
||
51 | * |
||
52 | * @param mixed $needle The value to search for |
||
53 | * @param array $haystack The array in which to search |
||
54 | * @return array $haystack array reduced matching $needle values |
||
55 | */ |
||
56 | public static function filterByValueRecursive($needle = '', array $haystack = []) |
||
57 | { |
||
58 | $resultArray = []; |
||
59 | // Define a lambda function to be applied to all members of this array dimension |
||
60 | // Call recursive if current value is of type array |
||
61 | // Write to $resultArray (by reference!) if types and value match |
||
62 | $callback = function (&$value, $key) use ($needle, &$resultArray) { |
||
63 | if ($value === $needle) { |
||
64 | ($resultArray[$key] = $value); |
||
65 | } elseif (is_array($value)) { |
||
66 | ($subArrayMatches = static::filterByValueRecursive($needle, $value)); |
||
67 | if (! empty($subArrayMatches)) { |
||
68 | ($resultArray[$key] = $subArrayMatches); |
||
69 | } |
||
70 | } |
||
71 | }; |
||
72 | // array_walk() is not affected by the internal pointers, no need to reset |
||
73 | array_walk($haystack, $callback); |
||
74 | |||
75 | // Pointers to result array are reset internally |
||
76 | return $resultArray; |
||
77 | } |
||
78 | |||
79 | /** |
||
80 | * Checks if a given path exists in array. |
||
81 | * |
||
82 | * Example: |
||
83 | * - array: |
||
84 | * array( |
||
85 | * 'foo' => array( |
||
86 | * 'bar' = 'test', |
||
87 | * ) |
||
88 | * ); |
||
89 | * - path: 'foo/bar' |
||
90 | * - return: TRUE |
||
91 | * |
||
92 | * @param array $array Given array |
||
93 | * @param string $path Path to test, 'foo/bar/foobar' |
||
94 | * @param string $delimiter Delimiter for path, default / |
||
95 | * @return bool TRUE if path exists in array |
||
96 | */ |
||
97 | public static function isValidPath(array $array, $path, $delimiter = '/') |
||
109 | |||
110 | /** |
||
111 | * Returns a value by given path. |
||
112 | * |
||
113 | * Example |
||
114 | * - array: |
||
115 | * array( |
||
116 | * 'foo' => array( |
||
117 | * 'bar' => array( |
||
118 | * 'baz' => 42 |
||
119 | * ) |
||
120 | * ) |
||
121 | * ); |
||
122 | * - path: foo/bar/baz |
||
123 | * - return: 42 |
||
124 | * |
||
125 | * If a path segments contains a delimiter character, the path segment |
||
126 | * must be enclosed by " (double quote), see unit tests for details |
||
127 | * |
||
128 | * @param array $array Input array |
||
129 | * @param string $path Path within the array |
||
130 | * @param string $delimiter Defined path delimiter, default / |
||
131 | * @return mixed |
||
132 | * @throws \RuntimeException |
||
133 | */ |
||
134 | public static function getValueByPath(array $array, $path, $delimiter = '/') |
||
158 | |||
159 | /** |
||
160 | * Modifies or sets a new value in an array by given path. |
||
161 | * |
||
162 | * Example: |
||
163 | * - array: |
||
164 | * array( |
||
165 | * 'foo' => array( |
||
166 | * 'bar' => 42, |
||
167 | * ), |
||
168 | * ); |
||
169 | * - path: foo/bar |
||
170 | * - value: 23 |
||
171 | * - return: |
||
172 | * array( |
||
173 | * 'foo' => array( |
||
174 | * 'bar' => 23, |
||
175 | * ), |
||
176 | * ); |
||
177 | * |
||
178 | * @param array $array Input array to manipulate |
||
179 | * @param string $path Path in array to search for |
||
180 | * @param mixed $value Value to set at path location in array |
||
181 | * @param string $delimiter Path delimiter |
||
182 | * @return array Modified array |
||
183 | * @throws \RuntimeException |
||
184 | */ |
||
185 | public static function setValueByPath(array $array, $path, $value, $delimiter = '/') |
||
215 | |||
216 | /** |
||
217 | * Remove a sub part from an array specified by path. |
||
218 | * |
||
219 | * @param array $array Input array to manipulate |
||
220 | * @param string $path Path to remove from array |
||
221 | * @param string $delimiter Path delimiter |
||
222 | * @return array Modified array |
||
223 | * @throws \RuntimeException |
||
224 | */ |
||
225 | public static function removeByPath(array $array, $path, $delimiter = '/') |
||
257 | |||
258 | /** |
||
259 | * Sorts an array recursively by key. |
||
260 | * |
||
261 | * @param array $array Array to sort recursively by key |
||
262 | * @return array Sorted array |
||
263 | */ |
||
264 | public static function sortByKeyRecursive(array $array) |
||
275 | |||
276 | /** |
||
277 | * Sort an array of arrays by a given key using uasort. |
||
278 | * |
||
279 | * @param array $arrays Array of arrays to sort |
||
280 | * @param string $key Key to sort after |
||
281 | * @param bool $ascending Set to TRUE for ascending order, FALSE for descending order |
||
282 | * @return array Array of sorted arrays |
||
283 | * @throws \RuntimeException |
||
284 | */ |
||
285 | public static function sortArraysByKey(array $arrays, $key, $ascending = true) |
||
286 | { |
||
287 | if (empty($arrays)) { |
||
288 | return $arrays; |
||
289 | } |
||
290 | $sortResult = uasort($arrays, function (array $a, array $b) use ($key, $ascending) { |
||
291 | if (! isset($a[$key]) || ! isset($b[$key])) { |
||
292 | throw new \RuntimeException('The specified sorting key "' . $key . '" is not available in the given array.', 1373727309); |
||
293 | } |
||
294 | |||
295 | return ($ascending) ? strcasecmp($a[$key], $b[$key]) : strcasecmp($b[$key], $a[$key]); |
||
296 | }); |
||
297 | if (! $sortResult) { |
||
298 | throw new \RuntimeException('The function uasort() failed for unknown reasons.', 1373727329); |
||
299 | } |
||
300 | |||
301 | return $arrays; |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * Exports an array as string. |
||
306 | * Similar to var_export(), but representation follows the PSR-2 and TYPO3 core CGL. |
||
307 | * |
||
308 | * See unit tests for detailed examples |
||
309 | * |
||
310 | * @param array $array Array to export |
||
311 | * @param int $level Internal level used for recursion, do *not* set from outside! |
||
312 | * @return string String representation of array |
||
313 | * @throws \RuntimeException |
||
314 | */ |
||
315 | public static function arrayExport(array $array = [], $level = 0) |
||
364 | |||
365 | /** |
||
366 | * Converts a multidimensional array to a flat representation. |
||
367 | * |
||
368 | * See unit tests for more details |
||
369 | * |
||
370 | * Example: |
||
371 | * - array: |
||
372 | * array( |
||
373 | * 'first.' => array( |
||
374 | * 'second' => 1 |
||
375 | * ) |
||
376 | * ) |
||
377 | * - result: |
||
378 | * array( |
||
379 | * 'first.second' => 1 |
||
380 | * ) |
||
381 | * |
||
382 | * Example: |
||
383 | * - array: |
||
384 | * array( |
||
385 | * 'first' => array( |
||
386 | * 'second' => 1 |
||
387 | * ) |
||
388 | * ) |
||
389 | * - result: |
||
390 | * array( |
||
391 | * 'first.second' => 1 |
||
392 | * ) |
||
393 | * |
||
394 | * @param array $array The (relative) array to be converted |
||
395 | * @param string $prefix The (relative) prefix to be used (e.g. 'section.') |
||
396 | * @return array |
||
397 | */ |
||
398 | public static function flatten(array $array, $prefix = '') |
||
413 | |||
414 | /** |
||
415 | * Determine the intersections between two arrays, recursively comparing keys |
||
416 | * A complete sub array of $source will be preserved, if the key exists in $mask. |
||
417 | * |
||
418 | * See unit tests for more examples and edge cases. |
||
419 | * |
||
420 | * Example: |
||
421 | * - source: |
||
422 | * array( |
||
423 | * 'key1' => 'bar', |
||
424 | * 'key2' => array( |
||
425 | * 'subkey1' => 'sub1', |
||
426 | * 'subkey2' => 'sub2', |
||
427 | * ), |
||
428 | * 'key3' => 'baz', |
||
429 | * ) |
||
430 | * - mask: |
||
431 | * array( |
||
432 | * 'key1' => NULL, |
||
433 | * 'key2' => array( |
||
434 | * 'subkey1' => exists', |
||
435 | * ), |
||
436 | * ) |
||
437 | * - return: |
||
438 | * array( |
||
439 | * 'key1' => 'bar', |
||
440 | * 'key2' => array( |
||
441 | * 'subkey1' => 'sub1', |
||
442 | * ), |
||
443 | * ) |
||
444 | * |
||
445 | * @param array $source Source array |
||
446 | * @param array $mask Array that has the keys which should be kept in the source array |
||
447 | * @return array Keys which are present in both arrays with values of the source array |
||
448 | */ |
||
449 | public static function intersectRecursive(array $source, array $mask = []) |
||
468 | |||
469 | /** |
||
470 | * Renumber the keys of an array to avoid leaps if keys are all numeric. |
||
471 | * |
||
472 | * Is called recursively for nested arrays. |
||
473 | * |
||
474 | * Example: |
||
475 | * |
||
476 | * Given |
||
477 | * array(0 => 'Zero' 1 => 'One', 2 => 'Two', 4 => 'Three') |
||
478 | * as input, it will return |
||
479 | * array(0 => 'Zero' 1 => 'One', 2 => 'Two', 3 => 'Three') |
||
480 | * |
||
481 | * Will treat keys string representations of number (ie. '1') equal to the |
||
482 | * numeric value (ie. 1). |
||
483 | * |
||
484 | * Example: |
||
485 | * Given |
||
486 | * array('0' => 'Zero', '1' => 'One' ) |
||
487 | * it will return |
||
488 | * array(0 => 'Zero', 1 => 'One') |
||
489 | * |
||
490 | * @param array $array Input array |
||
491 | * @param int $level Internal level used for recursion, do *not* set from outside! |
||
492 | * @return array |
||
493 | */ |
||
494 | public static function renumberKeysToAvoidLeapsIfKeysAreAllNumeric(array $array = [], $level = 0) |
||
516 | |||
517 | /** |
||
518 | * Merges two arrays recursively and "binary safe" (integer keys are |
||
519 | * overridden as well), overruling similar values in the original array |
||
520 | * with the values of the overrule array. |
||
521 | * In case of identical keys, ie. keeping the values of the overrule array. |
||
522 | * |
||
523 | * This method takes the original array by reference for speed optimization with large arrays |
||
524 | * |
||
525 | * The differences to the existing PHP function array_merge_recursive() are: |
||
526 | * * Keys of the original array can be unset via the overrule array. ($enableUnsetFeature) |
||
527 | * * Much more control over what is actually merged. ($addKeys, $includeEmptyValues) |
||
528 | * * Elements or the original array get overwritten if the same key is present in the overrule array. |
||
529 | * |
||
530 | * @param array $original Original array. It will be *modified* by this method and contains the result afterwards! |
||
531 | * @param array $overrule Overrule array, overruling the original array |
||
532 | * @param bool $addKeys If set to FALSE, keys that are NOT found in $original will not be set. Thus only existing value can/will be overruled from overrule array. |
||
533 | * @param bool $includeEmptyValues If set, values from $overrule will overrule if they are empty or zero. |
||
534 | * @param bool $enableUnsetFeature If set, special values "__UNSET" can be used in the overrule array in order to unset array keys in the original array. |
||
535 | * @return void |
||
536 | */ |
||
537 | public static function mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys = true, $includeEmptyValues = true, $enableUnsetFeature = true) |
||
558 | |||
559 | /** |
||
560 | * Check if an string item exists in an array. |
||
561 | * Please note that the order of function parameters is reverse compared to the PHP function in_array()!!! |
||
562 | * |
||
563 | * Comparison to PHP in_array(): |
||
564 | * -> $array = array(0, 1, 2, 3); |
||
565 | * -> variant_a := \TYPO3\CMS\Core\Utility\ArrayUtility::inArray($array, $needle) |
||
566 | * -> variant_b := in_array($needle, $array) |
||
567 | * -> variant_c := in_array($needle, $array, TRUE) |
||
568 | * +---------+-----------+-----------+-----------+ |
||
569 | * | $needle | variant_a | variant_b | variant_c | |
||
570 | * +---------+-----------+-----------+-----------+ |
||
571 | * | '1a' | FALSE | TRUE | FALSE | |
||
572 | * | '' | FALSE | TRUE | FALSE | |
||
573 | * | '0' | TRUE | TRUE | FALSE | |
||
574 | * | 0 | TRUE | TRUE | TRUE | |
||
575 | * +---------+-----------+-----------+-----------+ |
||
576 | * |
||
577 | * @param array $in_array One-dimensional array of items |
||
578 | * @param string $item Item to check for |
||
579 | * @return bool TRUE if $item is in the one-dimensional array $in_array |
||
580 | */ |
||
581 | public static function inArray(array $in_array, $item) |
||
591 | |||
592 | /** |
||
593 | * Removes the value $cmpValue from the $array if found there. Returns the modified array. |
||
594 | * |
||
595 | * @param array $array Array containing the values |
||
596 | * @param string $cmpValue Value to search for and if found remove array entry where found. |
||
597 | * @return array Output array with entries removed if search string is found |
||
598 | */ |
||
599 | public static function removeArrayEntryByValue(array $array, $cmpValue) |
||
611 | |||
612 | /** |
||
613 | * Filters an array to reduce its elements to match the condition. |
||
614 | * The values in $keepItems can be optionally evaluated by a custom callback function. |
||
615 | * |
||
616 | * Example (arguments used to call this function): |
||
617 | * $array = array( |
||
618 | * array('aa' => array('first', 'second'), |
||
619 | * array('bb' => array('third', 'fourth'), |
||
620 | * array('cc' => array('fifth', 'sixth'), |
||
621 | * ); |
||
622 | * $keepItems = array('third'); |
||
623 | * $getValueFunc = function($value) { return $value[0]; } |
||
624 | * |
||
625 | * Returns: |
||
626 | * array( |
||
627 | * array('bb' => array('third', 'fourth'), |
||
628 | * ) |
||
629 | * |
||
630 | * @param array $array The initial array to be filtered/reduced |
||
631 | * @param mixed $keepItems The items which are allowed/kept in the array - accepts array or csv string |
||
632 | * @param string $getValueFunc (optional) Callback function used to get the value to keep |
||
633 | * @return array The filtered/reduced array with the kept items |
||
634 | */ |
||
635 | public static function keepItemsInArray(array $array, $keepItems, $getValueFunc = null) |
||
660 | |||
661 | /** |
||
662 | * Rename Array keys with a given mapping table. |
||
663 | * |
||
664 | * @param array $array Array by reference which should be remapped |
||
665 | * @param array $mappingTable Array with remap information, array/$oldKey => $newKey) |
||
666 | */ |
||
667 | public static function remapArrayKeys(array &$array, array $mappingTable) |
||
676 | |||
677 | /** |
||
678 | * Filters keys off from first array that also exist in second array. Comparison is done by keys. |
||
679 | * This method is a recursive version of php array_diff_assoc(). |
||
680 | * |
||
681 | * @param array $array1 Source array |
||
682 | * @param array $array2 Reduce source array by this array |
||
683 | * @return array Source array reduced by keys also present in second array |
||
684 | */ |
||
685 | public static function arrayDiffAssocRecursive(array $array1, array $array2) |
||
700 | |||
701 | /** |
||
702 | * Sorts an array by key recursive - uses natural sort order (aAbB-zZ). |
||
703 | * |
||
704 | * @param array $array array to be sorted recursively, passed by reference |
||
705 | * @return bool always TRUE |
||
706 | */ |
||
707 | public static function naturalKeySortRecursive(array &$array) |
||
718 | } |
||
719 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.