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 App 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 App, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | class App implements AppInterface |
||
35 | { |
||
36 | /** |
||
37 | * Version string. |
||
38 | * |
||
39 | * @var string |
||
40 | */ |
||
41 | const VERSION = '2.2.0-DEV'; |
||
42 | |||
43 | /** |
||
44 | * @var bool |
||
45 | */ |
||
46 | protected $booted = false; |
||
47 | |||
48 | /** |
||
49 | * @var bool |
||
50 | */ |
||
51 | protected $debug; |
||
52 | |||
53 | /** |
||
54 | * Application environment: "dev|development" vs "prod|production". |
||
55 | * |
||
56 | * @var string |
||
57 | */ |
||
58 | protected $environment; |
||
59 | |||
60 | /** |
||
61 | * @var \Psr\Log\LoggerInterface |
||
62 | */ |
||
63 | protected $logger; |
||
64 | |||
65 | /** |
||
66 | * Unix timestamp with microseconds. |
||
67 | * |
||
68 | * @var float |
||
69 | */ |
||
70 | protected $startTime; |
||
71 | |||
72 | /** |
||
73 | * Configuration loader. |
||
74 | * |
||
75 | * @var \PPI\Framework\Config\ConfigManager |
||
76 | */ |
||
77 | protected $configManager; |
||
78 | |||
79 | /** |
||
80 | * The Module Manager. |
||
81 | * |
||
82 | * @var \Zend\ModuleManager\ModuleManager |
||
83 | */ |
||
84 | protected $moduleManager; |
||
85 | |||
86 | /** |
||
87 | * @param int $errorReportingLevel The level of error reporting you want |
||
88 | */ |
||
89 | protected $errorReportingLevel; |
||
90 | |||
91 | /** |
||
92 | * @var null|array |
||
93 | */ |
||
94 | protected $matchedRoute; |
||
95 | |||
96 | /** |
||
97 | * @var \PPI\Framework\Module\Controller\ControllerResolver |
||
98 | */ |
||
99 | protected $resolver; |
||
100 | |||
101 | /** |
||
102 | * @var string |
||
103 | */ |
||
104 | protected $name; |
||
105 | |||
106 | /** |
||
107 | * Path to the application root dir aka the "app" directory. |
||
108 | * |
||
109 | * @var null|string |
||
110 | */ |
||
111 | protected $rootDir; |
||
112 | |||
113 | /** |
||
114 | * Service Manager. |
||
115 | * |
||
116 | * @var \PPI\Framework\ServiceManager\ServiceManager |
||
117 | */ |
||
118 | protected $serviceManager; |
||
119 | |||
120 | /** |
||
121 | * @var ChainRouter |
||
122 | */ |
||
123 | private $router; |
||
124 | |||
125 | /** |
||
126 | * @var KernelInterface |
||
127 | */ |
||
128 | private $symfonyKernel; |
||
129 | |||
130 | /** |
||
131 | * App constructor. |
||
132 | * |
||
133 | * @param array $options |
||
134 | */ |
||
135 | public function __construct(array $options = array()) |
||
150 | |||
151 | public function __clone() |
||
160 | |||
161 | /** |
||
162 | * Run the boot process, load our modules and their dependencies. |
||
163 | * |
||
164 | * This method is automatically called by dispatch(), but you can use it |
||
165 | * to build all services when not handling a request. |
||
166 | * |
||
167 | * @return $this |
||
168 | */ |
||
169 | public function boot() |
||
202 | |||
203 | /** |
||
204 | * Run the application and send the response. |
||
205 | * |
||
206 | * @param HttpRequest|null $request |
||
207 | * @param HttpResponse|null $response |
||
208 | * |
||
209 | * @throws \Exception |
||
210 | * |
||
211 | * @return HttpResponse |
||
212 | */ |
||
213 | public function run(HttpRequest $request = null, HttpResponse $response = null) |
||
244 | |||
245 | /** |
||
246 | * Decide on a route to use and dispatch our module's controller action. |
||
247 | * |
||
248 | * @param HttpRequest $request |
||
249 | * @param HttpResponse $response |
||
250 | * |
||
251 | * @throws \Exception |
||
252 | * |
||
253 | * @return HttpResponse |
||
254 | */ |
||
255 | public function dispatch(HttpRequest $request, HttpResponse $response) |
||
300 | |||
301 | /** |
||
302 | * Gets the name of the application. |
||
303 | * |
||
304 | * @return string The application name |
||
305 | * |
||
306 | * @api |
||
307 | */ |
||
308 | public function getName() |
||
316 | |||
317 | /** |
||
318 | * Gets the version of the application. |
||
319 | * |
||
320 | * @return string The application version |
||
321 | * |
||
322 | * @api |
||
323 | */ |
||
324 | public function getVersion() |
||
328 | |||
329 | /** |
||
330 | * Get the environment mode the application is in. |
||
331 | * |
||
332 | * @return string The current environment |
||
333 | * |
||
334 | * @api |
||
335 | */ |
||
336 | public function getEnvironment() |
||
340 | |||
341 | /** |
||
342 | * @param $env |
||
343 | * |
||
344 | * @return bool |
||
345 | */ |
||
346 | public function isEnvironment($env) |
||
356 | |||
357 | /** |
||
358 | * Checks if debug mode is enabled. |
||
359 | * |
||
360 | * @return bool true if debug mode is enabled, false otherwise |
||
361 | * |
||
362 | * @api |
||
363 | */ |
||
364 | public function isDebug() |
||
368 | |||
369 | /** |
||
370 | * Gets the application root dir. |
||
371 | * |
||
372 | * @return string The application root dir |
||
373 | * |
||
374 | * @api |
||
375 | */ |
||
376 | public function getRootDir() |
||
384 | |||
385 | /** |
||
386 | * Get the service manager. |
||
387 | * |
||
388 | * @return ServiceManager |
||
389 | */ |
||
390 | public function getServiceManager() |
||
394 | |||
395 | /** |
||
396 | * @note Added for compatibility with Symfony's HttpKernel\Kernel. |
||
397 | * |
||
398 | * @return null|ServiceManager |
||
399 | */ |
||
400 | public function getContainer() |
||
404 | |||
405 | /** |
||
406 | * Returns the Module Manager. |
||
407 | * |
||
408 | * @return \Zend\ModuleManager\ModuleManager |
||
409 | */ |
||
410 | public function getModuleManager() |
||
418 | |||
419 | /** |
||
420 | * Get an array of the loaded modules. |
||
421 | * |
||
422 | * @return array An array of Module objects, keyed by module name |
||
423 | */ |
||
424 | public function getModules() |
||
428 | |||
429 | /** |
||
430 | * @see PPI\Framework\Module\ModuleManager::locateResource() |
||
431 | * |
||
432 | * @param string $name A resource name to locate |
||
433 | * @param string $dir A directory where to look for the resource first |
||
434 | * @param bool $first Whether to return the first path or paths for all matching bundles |
||
435 | * |
||
436 | * @throws \InvalidArgumentException if the file cannot be found or the name is not valid |
||
437 | * @throws \RuntimeException if the name contains invalid/unsafe |
||
438 | * @throws \RuntimeException if a custom resource is hidden by a resource in a derived bundle |
||
439 | * |
||
440 | * @return string|array The absolute path of the resource or an array if $first is false |
||
441 | */ |
||
442 | public function locateResource($name, $dir = null, $first = true) |
||
446 | |||
447 | /** |
||
448 | * Gets the request start time (not available if debug is disabled). |
||
449 | * |
||
450 | * @return int The request start timestamp |
||
451 | * |
||
452 | * @api |
||
453 | */ |
||
454 | public function getStartTime() |
||
458 | |||
459 | /** |
||
460 | * Gets the cache directory. |
||
461 | * |
||
462 | * @return string The cache directory |
||
463 | * |
||
464 | * @api |
||
465 | */ |
||
466 | public function getCacheDir() |
||
470 | |||
471 | /** |
||
472 | * Gets the log directory. |
||
473 | * |
||
474 | * @return string The log directory |
||
475 | * |
||
476 | * @api |
||
477 | */ |
||
478 | public function getLogDir() |
||
482 | |||
483 | /** |
||
484 | * Gets the charset of the application. |
||
485 | * |
||
486 | * @return string The charset |
||
487 | * |
||
488 | * @api |
||
489 | */ |
||
490 | public function getCharset() |
||
494 | |||
495 | /** |
||
496 | * Returns a ConfigManager instance. |
||
497 | * |
||
498 | * @return \PPI\Framework\Config\ConfigManager |
||
499 | */ |
||
500 | public function getConfigManager() |
||
509 | |||
510 | /** |
||
511 | * Loads a configuration file or PHP array. |
||
512 | * |
||
513 | * @param $resource |
||
514 | * @param null $type |
||
515 | * |
||
516 | * @return App The current instance |
||
517 | */ |
||
518 | public function loadConfig($resource, $type = null) |
||
524 | |||
525 | /** |
||
526 | * Returns the application configuration. |
||
527 | * |
||
528 | * @throws \RuntimeException |
||
529 | * |
||
530 | * @return array|object |
||
531 | */ |
||
532 | public function getConfig() |
||
540 | |||
541 | /** |
||
542 | * @return string |
||
543 | */ |
||
544 | public function serialize() |
||
548 | |||
549 | /** |
||
550 | * @param KernelInterface $kernel |
||
551 | */ |
||
552 | public function setSymfonyKernel(KernelInterface $kernel) |
||
556 | |||
557 | public function unserialize($data) |
||
563 | |||
564 | /** |
||
565 | * Returns the application parameters. |
||
566 | * |
||
567 | * @return array An array of application parameters |
||
568 | */ |
||
569 | protected function getAppParameters() |
||
584 | |||
585 | /** |
||
586 | * Gets the environment parameters. |
||
587 | * |
||
588 | * Only the parameters starting with "PPI__" are considered. |
||
589 | * |
||
590 | * @return array An array of parameters |
||
591 | */ |
||
592 | protected function getEnvParameters() |
||
603 | |||
604 | /** |
||
605 | * Creates and initializes a ServiceManager instance. |
||
606 | * |
||
607 | * @return ServiceManager The compiled service manager |
||
608 | */ |
||
609 | protected function buildServiceManager() |
||
618 | |||
619 | /** |
||
620 | * Perform the matching of a route and return a set of routing parameters if a valid one is found. |
||
621 | * Otherwise exceptions get thrown. |
||
622 | * |
||
623 | * @param HttpRequest $request |
||
624 | * |
||
625 | * @throws \Exception |
||
626 | * |
||
627 | * @return array |
||
628 | */ |
||
629 | protected function handleRouting(HttpRequest $request) |
||
653 | |||
654 | /** |
||
655 | * Logs with an arbitrary level. |
||
656 | * |
||
657 | * @param mixed $level |
||
658 | * @param string $message |
||
659 | * @param array $context |
||
660 | */ |
||
661 | protected function log($level, $message, array $context = array()) |
||
671 | } |
||
672 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.