Complex classes like code_review 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 code_review, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
7 | class code_review { |
||
|
|||
8 | |||
9 | /** |
||
10 | * Config array to allow mocking of configuration. |
||
11 | * |
||
12 | * @var array |
||
13 | */ |
||
14 | protected static $config = array(); |
||
15 | |||
16 | /** |
||
17 | * @codeCoverageIgnore |
||
18 | */ |
||
19 | public static function boot() { |
||
36 | |||
37 | /** |
||
38 | * @return array |
||
39 | */ |
||
40 | 1 | public static function getConfig() { |
|
43 | |||
44 | /** |
||
45 | * @param array $options |
||
46 | * |
||
47 | * @todo Move into CodeReviewConfig instead |
||
48 | */ |
||
49 | 3 | public static function initConfig(array $options) { |
|
65 | |||
66 | /** |
||
67 | * @codeCoverageIgnore |
||
68 | */ |
||
69 | public static function init() { |
||
81 | |||
82 | /** |
||
83 | * @codeCoverageIgnore |
||
84 | */ |
||
85 | public static function pagesetup() { |
||
96 | |||
97 | /** |
||
98 | * @codeCoverageIgnore |
||
99 | */ |
||
100 | public static function menu_register() { |
||
126 | |||
127 | /** |
||
128 | * @param string $subPath |
||
129 | * @return RegexIterator |
||
130 | */ |
||
131 | 2 | public static function getPhpIterator($subPath = '/') { |
|
137 | |||
138 | public static function getVersionsList() { |
||
159 | |||
160 | /** |
||
161 | * @val string |
||
162 | */ |
||
163 | const DEPRECATED_TAG_PREFIX = 'deprecated'; |
||
164 | |||
165 | |||
166 | /** |
||
167 | * @val string |
||
168 | */ |
||
169 | const PRIVATE_TAG_PREFIX = 'private'; |
||
170 | |||
171 | /** |
||
172 | * Filtering predicate |
||
173 | * |
||
174 | * @param $e |
||
175 | * @return bool |
||
176 | */ |
||
177 | 1 | public static function filterTagsByDeprecatedPrefix($e) { |
|
180 | |||
181 | /** |
||
182 | * Filtering predicate |
||
183 | * |
||
184 | * @param $e |
||
185 | * @return bool |
||
186 | */ |
||
187 | public static function filterTagsByPrivatePrefix($e) { |
||
190 | |||
191 | 1 | private static function getDeprecatedInfoFromDocBlock($deprecatedInfo) { |
|
192 | 1 | if (strpos($deprecatedInfo, '@' . self::DEPRECATED_TAG_PREFIX) === false){ |
|
193 | 1 | return false; |
|
194 | } else { |
||
195 | 1 | $deprecatedInfo = explode('* @', $deprecatedInfo); |
|
196 | 1 | $deprecatedInfo = array_filter($deprecatedInfo, array(__CLASS__, 'filterTagsByDeprecatedPrefix')); |
|
197 | 1 | $deprecatedInfo = array_shift($deprecatedInfo); |
|
198 | 1 | $deprecatedInfo = substr($deprecatedInfo, strlen(self::DEPRECATED_TAG_PREFIX)); |
|
199 | |||
200 | //strip leading whitechars and stars and closing tags |
||
201 | 1 | $deprecatedInfo = preg_replace('#\n\s*(?:\*\/?\s*)+#', "\n", $deprecatedInfo); |
|
202 | //save and strip leading version info |
||
203 | 1 | $version = null; |
|
204 | 1 | preg_match('#^\s*([0-9]+\.[0-9]+)#', $deprecatedInfo, $matches); |
|
205 | 1 | if ($matches) { |
|
206 | 1 | $version = $matches[1]; |
|
207 | 1 | } |
|
208 | 1 | $deprecatedInfo = preg_replace('#\s*[0-9](?:\.[0-9]+){1,2}\.?\s*#', "", $deprecatedInfo); |
|
209 | //strip embedded @link docblock entries |
||
210 | 1 | $deprecatedInfo = preg_replace('#\{\@[a-z]+\s([^\}]+)\}#', '$1', $deprecatedInfo); |
|
211 | //trim possible whitechars at the end |
||
212 | 1 | $deprecatedInfo = trim($deprecatedInfo); |
|
213 | |||
214 | 1 | $shortDeprecatedInfo = $deprecatedInfo; |
|
215 | 1 | if (($pos = strpos($shortDeprecatedInfo, "\n")) !== false) { |
|
216 | $shortDeprecatedInfo = substr($shortDeprecatedInfo, 0, $pos); |
||
217 | } |
||
218 | |||
219 | $result = array( |
||
220 | 1 | 'deprecated' => true, |
|
221 | // 'fixinfo' => strlen($deprecatedInfo) > 0 ? $deprecatedInfo : false, |
||
222 | 1 | 'fixinfoshort' => strlen($shortDeprecatedInfo) > 0 ? $shortDeprecatedInfo : false, |
|
223 | 1 | ); |
|
224 | 1 | if ($version) { |
|
225 | 1 | $result['version'] = $version; |
|
226 | 1 | } |
|
227 | 1 | return $result; |
|
228 | } |
||
229 | } |
||
230 | |||
231 | private static function getPrivateInfoFromDocBlock($privateInfo) { |
||
241 | |||
242 | /** |
||
243 | * @param string $maxVersion |
||
244 | * @return array |
||
245 | */ |
||
246 | 1 | public static function getDeprecatedFunctionsList($maxVersion = '') { |
|
276 | |||
277 | /** |
||
278 | * @param string $maxVersion |
||
279 | * @return array |
||
280 | */ |
||
281 | 1 | public static function getPrivateFunctionsList() { |
|
300 | |||
301 | /** |
||
302 | * Redurns deprecated functions from particular file. |
||
303 | * |
||
304 | * @param PhpFileParser $tokens |
||
305 | * @param SplFileInfo $file |
||
306 | * @param $version |
||
307 | * @return array |
||
308 | */ |
||
309 | 1 | private static function getDeprecatedFunctionsFromTokens(PhpFileParser $tokens, SplFileInfo $file, $version) { |
|
310 | 1 | $namespace = ''; |
|
311 | 1 | $className = null; |
|
312 | 1 | $functs = array(); |
|
313 | 1 | foreach ($tokens as $key => $token) { |
|
314 | 1 | if ($tokens->isEqualToToken(T_INTERFACE, $key)) { |
|
315 | //we don't process interfaces for deprecated functions |
||
316 | break; |
||
317 | } |
||
318 | 1 | if ($tokens->isEqualToToken(T_NAMESPACE, $key)) { |
|
319 | 1 | $pos = $key+2; |
|
320 | 1 | $namespace = ''; |
|
321 | 1 | while (isset($tokens[$pos]) && $tokens[$pos] !== ';') { |
|
322 | 1 | $namespace .= $tokens[$pos][1]; |
|
323 | 1 | $pos++; |
|
324 | 1 | } |
|
325 | 1 | $namespace = '\\' . $namespace . '\\'; |
|
326 | 1 | } |
|
327 | 1 | if ($tokens->isEqualToToken(T_CLASS, $key)) { |
|
328 | //mark class name for all following functions |
||
329 | 1 | $className = $namespace . $tokens[$key+2][1]; |
|
330 | 1 | } |
|
331 | |||
332 | //TODO we need to filter out closures |
||
333 | |||
334 | 1 | if ($tokens->isEqualToToken(T_FUNCTION, $key)) { |
|
335 | 1 | if ($className) { |
|
336 | 1 | $functionName = $className . '::' . $tokens[$key+2][1]; |
|
337 | try { |
||
338 | 1 | $reflection = new ReflectionMethod($className, $tokens[$key+2][1]); |
|
339 | 1 | } catch (ReflectionException $e) { |
|
340 | 1 | break; |
|
341 | } |
||
342 | |||
343 | } else { |
||
344 | 1 | $functionName = $tokens[$key+2][1]; |
|
345 | try { |
||
346 | 1 | $reflection = new ReflectionFunction($functionName); |
|
347 | 1 | } catch (ReflectionException $e) { |
|
348 | break; |
||
349 | } |
||
350 | } |
||
351 | |||
352 | //check if non empty version and try go guess |
||
353 | $data = array( |
||
354 | 1 | 'name' => $functionName, |
|
355 | 1 | 'version' => $version, |
|
356 | 1 | 'file' => $file->getPathname(), |
|
357 | 1 | 'line' => $token[2], |
|
358 | 1 | ); |
|
359 | |||
360 | 1 | $docBlock = $reflection->getDocComment(); |
|
361 | 1 | if ($docBlock) { |
|
362 | 1 | $info = self::getDeprecatedInfoFromDocBlock($docBlock); |
|
363 | 1 | if (!$info) { |
|
364 | 1 | if ($version) { |
|
365 | // no details, but we have version, so everything is deprecated here |
||
366 | $info = array( |
||
367 | 1 | 'deprecated' => true, |
|
368 | 1 | 'version' => $version, |
|
369 | 1 | 'fixinfoshort' => false, |
|
370 | 1 | ); |
|
371 | 1 | } else { |
|
372 | //skipping - not deprecated |
||
373 | continue; |
||
374 | } |
||
375 | 1 | } |
|
376 | 1 | $data = array_merge($data, $info); |
|
377 | 1 | } |
|
378 | |||
379 | 1 | $functs[strtolower($functionName)] = new CodeReview_Issues_Deprecated($data); |
|
380 | 1 | } |
|
381 | 1 | } |
|
382 | 1 | return $functs; |
|
383 | } |
||
384 | |||
385 | /** |
||
386 | * Redurns deprecated functions from particular file. |
||
387 | * |
||
388 | * @param PhpFileParser $tokens |
||
389 | * @param SplFileInfo $file |
||
390 | * @param $version |
||
391 | * @return array |
||
392 | */ |
||
393 | 1 | private static function getPrivateFunctionsFromTokens(PhpFileParser $tokens, SplFileInfo $file) { |
|
394 | 1 | $namespace = ''; |
|
395 | 1 | $className = null; |
|
396 | 1 | $functs = array(); |
|
397 | 1 | foreach ($tokens as $key => $token) { |
|
398 | 1 | if ($tokens->isEqualToToken(T_INTERFACE, $key)) { |
|
399 | //we don't process interfaces for deprecated functions |
||
400 | break; |
||
401 | } |
||
402 | 1 | if ($tokens->isEqualToToken(T_NAMESPACE, $key)) { |
|
403 | 1 | $pos = $key+2; |
|
404 | 1 | $namespace = ''; |
|
405 | 1 | while (isset($tokens[$pos]) && $tokens[$pos] !== ';') { |
|
406 | 1 | $namespace .= $tokens[$pos][1]; |
|
407 | 1 | $pos++; |
|
408 | 1 | } |
|
409 | 1 | $namespace = '\\' . $namespace . '\\'; |
|
410 | 1 | } |
|
411 | 1 | if ($tokens->isEqualToToken(T_CLASS, $key)) { |
|
412 | //mark class name for all following functions |
||
413 | 1 | $className = $namespace . $tokens[$key+2][1]; |
|
414 | 1 | } |
|
415 | |||
416 | 1 | if ($tokens->isEqualToToken(T_FUNCTION, $key)) { |
|
417 | 1 | if ($className) { |
|
418 | 1 | $functionName = $className . '::' . $tokens[$key+2][1]; |
|
419 | try { |
||
420 | 1 | $reflection = new ReflectionMethod($className, $tokens[$key+2][1]); |
|
421 | 1 | } catch (ReflectionException $e) { |
|
422 | 1 | break; |
|
423 | } |
||
424 | } else { |
||
425 | 1 | $functionName = $tokens[$key+2][1]; |
|
426 | try { |
||
427 | 1 | $reflection = new ReflectionFunction($functionName); |
|
428 | 1 | } catch (ReflectionException $e) { |
|
429 | break; |
||
430 | } |
||
431 | } |
||
432 | |||
433 | //check if non empty version and try go guess |
||
434 | $data = array( |
||
435 | 1 | 'name' => $functionName, |
|
436 | 1 | 'file' => $file->getPathname(), |
|
437 | 1 | 'line' => $token[2], |
|
438 | 1 | ); |
|
439 | |||
440 | 1 | $docBlock = $reflection->getDocComment(); |
|
441 | 1 | if ($docBlock) { |
|
442 | 1 | if (preg_match('/@access\s+private/', $docBlock) < 1) { |
|
443 | //skipping - not private |
||
444 | 1 | continue; |
|
445 | } |
||
446 | 1 | $data = new CodeReview_Issues_Private($data); |
|
447 | 1 | } else { |
|
448 | //non documented means private |
||
449 | 1 | $data = new CodeReview_Issues_NotDocumented($data); |
|
450 | } |
||
451 | |||
452 | 1 | $functs[strtolower($functionName)] = $data; |
|
453 | 1 | } |
|
454 | 1 | } |
|
455 | 1 | return $functs; |
|
456 | } |
||
457 | |||
458 | /** |
||
459 | * Returns a list of plugin directory names from a base directory. |
||
460 | * Copied from 1.9 core due to elgg_get_plugin_ids_in_dir removal in 1.9 |
||
461 | * |
||
462 | * @param string $dir A dir to scan for plugins. Defaults to config's plugins_path. |
||
463 | * Must have a trailing slash. |
||
464 | * |
||
465 | * @return array Array of directory names (not full paths) |
||
466 | */ |
||
467 | 1 | public static function getPluginDirsInDir($dir = null) { |
|
488 | } |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.