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 Functions 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 Functions, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | class Functions |
||
31 | { |
||
32 | /** |
||
33 | * @var array This array contains the names of the loaded functions from directory /core/functions. |
||
34 | */ |
||
35 | public static $alreadyLoaded = []; |
||
36 | |||
37 | /** |
||
38 | * Recursive glob. |
||
39 | * |
||
40 | * @param string $pattern |
||
41 | * @param int $flags |
||
42 | * |
||
43 | * @return type |
||
|
|||
44 | */ |
||
45 | public static function globRecursive($pattern, $flags = 0) |
||
60 | |||
61 | public static function getServerLoad() |
||
62 | { |
||
63 | if (stristr(PHP_OS, 'win')) { |
||
64 | $wmi = new COM('Winmgmts://'); |
||
65 | $cpus = $wmi->execquery('SELECT LoadPercentage FROM Win32_Processor'); |
||
66 | |||
67 | $cpu_num = 0; |
||
68 | $load_total = 0; |
||
69 | |||
70 | foreach ($cpus as $cpu) { |
||
71 | ++$cpu_num; |
||
72 | $load_total += $cpu->loadpercentage; |
||
73 | } |
||
74 | |||
75 | $load = round($load_total / $cpu_num); |
||
76 | } else { |
||
77 | $sys_load = sys_getloadavg(); |
||
78 | $load = $sys_load[0]; |
||
79 | } |
||
80 | |||
81 | return (int) $load; |
||
82 | } |
||
83 | |||
84 | public static function inString($needle, $haystack, $insensitive = false) |
||
92 | |||
93 | /** |
||
94 | * Checks a string for a certain prefix or adds it, if missing. |
||
95 | * |
||
96 | * @param string $string |
||
97 | * @param string $prefix |
||
98 | * |
||
99 | * @return string prefixed classname |
||
100 | */ |
||
101 | public static function ensurePrefixedWith($string, $prefix) |
||
102 | { |
||
103 | $pos = null; |
||
104 | |||
105 | $pos = mb_strpos($string, $prefix); |
||
106 | |||
107 | if (is_int($pos) && ($pos === 0)) { |
||
108 | return $string; |
||
109 | } else { |
||
110 | return $prefix . $string; |
||
111 | } |
||
112 | } |
||
113 | |||
114 | public function dropNumericKeys(array $array) |
||
115 | { |
||
116 | foreach ($array as $key => $value) { |
||
117 | if (is_int($key)) { |
||
118 | unset($array[$key]); |
||
119 | } |
||
120 | } |
||
121 | |||
122 | return $array; |
||
123 | } |
||
124 | |||
125 | public function issetOrDefault($var, $defaultValue = null) |
||
126 | { |
||
127 | return isset($var) ? $var : $defaultValue; |
||
128 | } |
||
129 | |||
130 | public function issetArrayKeyOrDefault(array $array, $key, $defaultValue = null) |
||
131 | { |
||
132 | return isset($array[$key]) ? $array[$key] : $defaultValue; |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Transforms a string from underscored_lower_case to Underscored_Upper_Camel_Case. |
||
137 | * |
||
138 | * @param string $string String in underscored_lower_case format. |
||
139 | * |
||
140 | * @return $string String in Upper_Camel_Case. |
||
141 | */ |
||
142 | public static function toUnderscoredUpperCamelCase($string) |
||
143 | { |
||
144 | $upperCamelCase = str_replace(' ', '_', ucwords(str_replace('_', ' ', strtolower($string)))); |
||
145 | |||
146 | return $upperCamelCase; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * cut_string_backwards. |
||
151 | * |
||
152 | * haystack = abc_def |
||
153 | * needle = _def |
||
154 | * result = abc |
||
155 | * |
||
156 | * In PHP6 |
||
157 | * abc = $string = mb_strstr('abc_def', '_def'); |
||
158 | * |
||
159 | * @param $haystack string |
||
160 | * @param $needle string |
||
161 | * |
||
162 | * @return string |
||
163 | */ |
||
164 | public static function cutStringBackwards($haystack, $needle) |
||
165 | { |
||
166 | $needle_length = mb_strlen($needle); |
||
167 | |||
168 | if (($i = mb_strpos($haystack, $needle) !== false)) { |
||
169 | return mb_substr($haystack, 0, -$needle_length); |
||
170 | } |
||
171 | |||
172 | return $haystack; |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * @param string $haystack |
||
177 | * @param string $replace |
||
178 | * @param string $needle |
||
179 | * @param int $times |
||
180 | * |
||
181 | * @return $needle |
||
182 | */ |
||
183 | public static function strReplaceCount($haystack, $replace, $needle, $times) |
||
184 | { |
||
185 | $subject_original = $needle; |
||
186 | $length = mb_strlen($haystack); |
||
187 | $pos = 0; |
||
188 | |||
189 | for ($i = 1; $i <= $times; ++$i) { |
||
190 | $pos = mb_strpos($needle, $haystack, $pos); |
||
191 | |||
192 | if ($pos !== false) { |
||
193 | $needle = mb_substr($subject_original, 0, $pos); |
||
194 | $needle .= $replace; |
||
195 | $needle .= mb_substr($subject_original, $pos + $length); |
||
196 | $subject_original = $needle; |
||
197 | } else { |
||
198 | break; |
||
199 | } |
||
200 | } |
||
201 | |||
202 | return $needle; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Takes a needle and multi-dimensional haystack array and does a search on it's values. |
||
207 | * |
||
208 | * @param string $needle Needle to find |
||
209 | * @param array $haystack Haystack to look through |
||
210 | * @result array Returns the elements that the $string was found in |
||
211 | * |
||
212 | * array_values_recursive |
||
213 | */ |
||
214 | public static function findKeyInArray($needle, array $haystack) |
||
232 | |||
233 | /** |
||
234 | * array_compare. |
||
235 | * |
||
236 | * @author 55 dot php at imars dot com |
||
237 | * @author dwarven dot co dot uk |
||
238 | * |
||
239 | * @link http://www.php.net/manual/de/function.array-diff-assoc.php#89635 |
||
240 | * |
||
241 | * @param $array1 |
||
242 | * @param $array2 |
||
243 | */ |
||
244 | public static function arrayCompare($array1, $array2) |
||
245 | { |
||
246 | $diff = false; |
||
247 | |||
248 | // Left-to-right |
||
249 | foreach ($array1 as $key => $value) { |
||
250 | if (array_key_exists($key, $array2) === false) { |
||
251 | $diff[0][$key] = $value; |
||
252 | } elseif (is_array($value)) { |
||
253 | if (is_array($array2[$key]) === false) { |
||
254 | $diff[0][$key] = $value; |
||
255 | $diff[1][$key] = $array2[$key]; |
||
256 | } else { |
||
257 | $new = self::array_compare($value, $array2[$key]); |
||
258 | |||
259 | if ($new !== false) { |
||
260 | View Code Duplication | if ($new[0] !== null) { |
|
261 | $diff[0][$key] = $new[0]; |
||
262 | } |
||
263 | View Code Duplication | if ($new[1] !== null) { |
|
264 | $diff[1][$key] = $new[1]; |
||
265 | } |
||
266 | } |
||
267 | } |
||
268 | } elseif ($array2[$key] !== $value) { |
||
269 | $diff[0][$key] = $value; |
||
270 | $diff[1][$key] = $array2[$key]; |
||
271 | } |
||
272 | } |
||
273 | |||
274 | // Right-to-left |
||
275 | foreach ($array2 as $key => $value) { |
||
276 | if (array_key_exists($key, $array1) === false) { |
||
277 | $diff[1][$key] = $value; |
||
278 | } |
||
279 | |||
280 | /* |
||
281 | * No direct comparsion because matching keys were compared in the |
||
282 | * left-to-right loop earlier, recursively. |
||
283 | */ |
||
284 | } |
||
285 | |||
286 | return $diff; |
||
287 | } |
||
288 | |||
289 | /** |
||
290 | * Combines two arrays by using $keyArray as key providing array |
||
291 | * an $valueArray as value providing array. |
||
292 | * In case the valueArray is greater than the keyArray, |
||
293 | * the keyArray determines the maximum number of values returned. |
||
294 | * In case the valueArray is smaller than the keyArray, |
||
295 | * those keys are returned for which values exist. |
||
296 | * |
||
297 | * @example |
||
298 | * $keys = array('mod', 'sub', 'action', 'id'); |
||
299 | * $values = array('news', 'admin'); |
||
300 | * $combined = self::array_unequal_combine($keys, $values); |
||
301 | * Results in: array('mod'=>'news', 'sub'=>'admin'); |
||
302 | * |
||
303 | * @param array $keyArray |
||
304 | * @param array $valueArray |
||
305 | * |
||
306 | * @return array Combined Array |
||
307 | */ |
||
308 | public static function arrayUnequalCombine($keyArray, $valueArray) |
||
309 | { |
||
310 | $returnArray = []; |
||
311 | $key = ''; |
||
312 | $index = 0; |
||
313 | |||
314 | // more keys than values, reduce keys array |
||
315 | while (count($keyArray) > count($valueArray)) { |
||
316 | array_pop($keyArray); |
||
317 | } |
||
318 | |||
319 | // @todo more values than keys ? |
||
320 | // add pseudo keys a la "key-0" |
||
321 | |||
322 | foreach ($keyArray as $key) { |
||
323 | if ($valueArray[$index] !== null) { |
||
324 | // index is used, then incremented for the next turn in foreach (post-increment-operator) |
||
325 | $returnArray[$key] = $valueArray[$index++]; |
||
326 | } |
||
327 | } |
||
328 | |||
329 | return $returnArray; |
||
330 | } |
||
331 | |||
332 | /** |
||
333 | * Remaps multi-dim array (k1=>v1, k2=>v2, k(n) => v(n)) to the values of key1=>key2 (v1 => v2). |
||
334 | * The array might have several keys, so you might map value of key2 to value of key5 ;) |
||
335 | * Simple, but impressive! |
||
336 | * |
||
337 | * @param type $array |
||
338 | * @param type $map_value_of_key1 |
||
339 | * @param type $to_value_of_key2 |
||
340 | * |
||
341 | * @return type array |
||
342 | */ |
||
343 | public static function mapArrayKeysToValues($array, $map_value_of_key1, $to_value_of_key2) |
||
344 | { |
||
345 | $new_array = []; |
||
346 | foreach ($array as $inner_array) { |
||
347 | $new_array[$inner_array[$map_value_of_key1]] = $inner_array[$to_value_of_key2]; |
||
348 | } |
||
349 | |||
350 | return $new_array; |
||
351 | } |
||
352 | |||
353 | /** |
||
354 | * flatten multi-dimensional array. |
||
355 | * |
||
356 | * @param array $array |
||
357 | * |
||
358 | * @return array |
||
359 | */ |
||
360 | public static function arrayFlatten(array $array) |
||
369 | |||
370 | /** |
||
371 | * distanceOfTimeInWords. |
||
372 | * |
||
373 | * @author: anon |
||
374 | * @link: http://www.php.net/manual/de/function.time.php#85481 |
||
375 | * |
||
376 | * @param int $fromTime starttime |
||
377 | * @param $toTime endtime |
||
378 | * @param $showLessThanAMinute boolean |
||
379 | * |
||
380 | * @return string |
||
381 | */ |
||
382 | public static function distanceOfTimeInWords($fromTime, $toTime = 0, $showLessThanAMinute = false) |
||
383 | { |
||
384 | $distanceInSeconds = round(abs($toTime - $fromTime)); |
||
385 | $distanceInMinutes = round($distanceInSeconds / 60); |
||
386 | |||
387 | if ($distanceInMinutes <= 1) { |
||
388 | if ($showLessThanAMinute === false) { |
||
389 | return ($distanceInMinutes === 0) ? 'less than a minute' : '1 minute'; |
||
390 | } else { |
||
391 | if ($distanceInSeconds < 5) { |
||
392 | return 'less than 5 seconds'; |
||
393 | } |
||
394 | if ($distanceInSeconds < 10) { |
||
395 | return 'less than 10 seconds'; |
||
396 | } |
||
397 | if ($distanceInSeconds < 20) { |
||
398 | return 'less than 20 seconds'; |
||
399 | } |
||
400 | if ($distanceInSeconds < 40) { |
||
401 | return 'about half a minute'; |
||
402 | } |
||
403 | if ($distanceInSeconds < 60) { |
||
404 | return 'less than a minute'; |
||
405 | } |
||
406 | |||
407 | return '1 minute'; |
||
408 | } |
||
409 | } |
||
410 | if ($distanceInMinutes < 45) { |
||
411 | return $distanceInMinutes . ' minutes'; |
||
412 | } |
||
413 | if ($distanceInMinutes < 90) { |
||
414 | return 'about 1 hour'; |
||
415 | } |
||
416 | View Code Duplication | if ($distanceInMinutes < 1440) { |
|
417 | return 'about ' . round(floatval($distanceInMinutes) / 60.0) . ' hours'; |
||
418 | } |
||
419 | if ($distanceInMinutes < 2880) { |
||
420 | return '1 day'; |
||
421 | } |
||
422 | View Code Duplication | if ($distanceInMinutes < 43200) { |
|
423 | return 'about ' . round(floatval($distanceInMinutes) / 1440) . ' days'; |
||
424 | } |
||
425 | if ($distanceInMinutes < 86400) { |
||
426 | return 'about 1 month'; |
||
427 | } |
||
428 | if ($distanceInMinutes < 525600) { |
||
429 | return round(floatval($distanceInMinutes) / 43200) . ' months'; |
||
430 | } |
||
431 | if ($distanceInMinutes < 1051199) { |
||
432 | return 'about 1 year'; |
||
433 | } |
||
434 | |||
435 | return 'over ' . round(floatval($distanceInMinutes) / 525600) . ' years'; |
||
436 | } |
||
437 | |||
438 | /** |
||
439 | * Performs a dateToWord transformation via gettext. |
||
440 | * uses idate() to format a local time/date as integer and gettext functions _n(), _t(). |
||
441 | * |
||
442 | * @see http://www.php.net/idate |
||
443 | * |
||
444 | * @param string $from |
||
445 | * @param string $now |
||
446 | * |
||
447 | * @return string Word representation of |
||
448 | */ |
||
449 | public static function dateToWord($from, $now = null) |
||
450 | { |
||
451 | if ($now === null) { |
||
452 | $now = time(); |
||
453 | } |
||
454 | |||
455 | $between = $now - $from; |
||
456 | |||
457 | if ($between < 86400 and idate('d', $from) === idate('d', $now)) { |
||
458 | if ($between < 3600 and idate('H', $from) === idate('H', $now)) { |
||
459 | View Code Duplication | if ($between < 60 and idate('i', $from) === idate('i', $now)) { |
|
460 | $second = idate('s', $now) - idate('s', $from); |
||
461 | |||
462 | return sprintf(_n('%d', '%d', $second), $second); |
||
463 | } |
||
464 | |||
465 | $min = idate('i', $now) - idate('i', $from); |
||
466 | |||
467 | return sprintf(_n('%d', '%d', $min), $min); |
||
468 | } |
||
469 | |||
470 | $hour = idate('H', $now) - idate('H', $from); |
||
471 | |||
472 | return sprintf(_n('%d', '%d', $hour), $hour); |
||
473 | } |
||
474 | |||
475 | if ($between < 172800 && (idate('z', $from) + 1 === idate('z', $now) or idate('z', $from) > 2 + idate('z', $now))) { |
||
476 | return _t('.. %s', date('H:i', $from)); |
||
477 | } |
||
478 | |||
479 | View Code Duplication | if ($between < 604800 and idate('W', $from) === idate('W', $now)) { |
|
480 | $day = intval($between / (3600 * 24)); |
||
481 | |||
482 | return sprintf(_n('...', '...', $day), $day); |
||
483 | } |
||
484 | |||
485 | if ($between < 31622400 and idate('Y', $from) === idate('Y', $now)) { |
||
486 | return date(_t('...'), $from); |
||
487 | } |
||
488 | |||
489 | return date(_t('...'), $from); |
||
490 | } |
||
491 | |||
492 | /** |
||
493 | * Get the variable name as string. |
||
494 | * |
||
495 | * @author http://us2.php.net/manual/en/language.variables.php#76245 |
||
496 | * |
||
497 | * @param $var variable as reference |
||
498 | * @param $scope scope |
||
499 | * |
||
500 | * @return string |
||
501 | */ |
||
502 | public static function vname($var, $scope = false, $prefix = 'unique', $suffix = 'value') |
||
503 | { |
||
504 | $values = ''; |
||
505 | |||
506 | if ($scope === true) { |
||
507 | $values = $scope; |
||
508 | } |
||
509 | |||
510 | $old = $var; |
||
511 | $var = $new = $prefix . rand() . $suffix; |
||
512 | $vname = false; |
||
513 | |||
514 | foreach ($values as $key => $val) { |
||
515 | if ($val === $new) { |
||
516 | $vname = $key; |
||
517 | } |
||
518 | } |
||
519 | $var = $old; |
||
520 | |||
521 | return $vname; |
||
522 | } |
||
523 | |||
524 | /** |
||
525 | * format_seconds_to_shortstring. |
||
526 | * |
||
527 | * @param $seconds int |
||
528 | * |
||
529 | * @return string Ouput: 4D 10:12:20 |
||
530 | */ |
||
531 | public static function formatSecondsToShortstring($seconds = 0) |
||
542 | |||
543 | /** |
||
544 | * Remove comments prefilter. |
||
545 | * |
||
546 | * @param $html A String with HTML Comments. |
||
547 | * |
||
548 | * @return string $html String without Comments. |
||
549 | */ |
||
550 | public function removeCommentsFromTemplate($html) |
||
551 | { |
||
552 | return preg_replace('/<!--.*-->/U', '', $html); |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * @param string $string |
||
557 | */ |
||
558 | public static function shortenString($string, $maxlength = 50, $append_string = '[...]') |
||
579 | |||
580 | /** |
||
581 | * Converts a UTF8-string into HTML entities. |
||
582 | * |
||
583 | * When using UTF-8 as a charset you want to convert multi-byte characters. |
||
584 | * This function takes multi-byte characters up to level 4 into account. |
||
585 | * Htmlentities will only convert 1-byte and 2-byte characters. |
||
586 | * Use this function if you want to convert 3-byte and 4-byte characters also. |
||
587 | * |
||
588 | * @author silverbeat gmx at |
||
589 | * |
||
590 | * @link http://www.php.net/manual/de/function.htmlentities.php#96648 |
||
591 | * |
||
592 | * @param $utf8 string The UTF8-string to convert |
||
593 | * @param $encodeTags booloean TRUE will convert "<" to "<", Default = false |
||
594 | * |
||
595 | * @return string the converted HTML-string |
||
596 | */ |
||
597 | public static function utf8ToHtml($utf8, $encodeTags = false) |
||
604 | |||
605 | /** |
||
606 | * The Magic Call __callStatic() is triggered when invoking inaccessible methods in a static context. |
||
607 | * Method overloading. |
||
608 | * Available from PHP 5.3 onwards. |
||
609 | * |
||
610 | * @param $name string The $name argument is the name of the method being called. |
||
611 | * @param $arguments arra The $arguments argument is an enumerated array containing the parameters passed to the $name'ed method. |
||
612 | */ |
||
613 | View Code Duplication | public static function __callStatic($method, $arguments) |
|
633 | |||
634 | /** |
||
635 | * The Magic Call __call() is triggered when invoking inaccessible methods in an object context. |
||
636 | * Method overloading. |
||
637 | * |
||
638 | * This method takes care of loading the function command files. |
||
639 | * |
||
640 | * This means that a currently non-existing methods or properties of this class are dynamically "created". |
||
641 | * Overloading methods are always in the "public" scope. |
||
642 | * |
||
643 | * @param $name string The $name argument is the name of the method being called. |
||
644 | * @param $arguments array The $arguments argument is an enumerated array containing the parameters passed to the $name'ed method. |
||
645 | */ |
||
646 | View Code Duplication | public function __call($method, $arguments) |
|
669 | } |
||
670 |
This check compares the return type specified in the
@return
annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.If the return type contains the type array, this check recommends the use of a more specific type like
String[]
orarray<String>
.