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 Dispatcher 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 Dispatcher, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
51 | class Dispatcher extends ParameterHolder |
||
52 | { |
||
53 | /** |
||
54 | * @var int The number of execution containers run so far. |
||
55 | */ |
||
56 | protected $numExecutions = 0; |
||
57 | |||
58 | /** |
||
59 | * @var Context An Context instance. |
||
60 | */ |
||
61 | protected $context = null; |
||
62 | |||
63 | /** |
||
64 | * @var Response The global response. |
||
65 | */ |
||
66 | protected $response = null; |
||
67 | |||
68 | /** |
||
69 | * @var FilterChain The global filter chain. |
||
70 | */ |
||
71 | protected $filterChain = null; |
||
72 | |||
73 | /** |
||
74 | * @var array An array of filter instances for reuse. |
||
75 | */ |
||
76 | protected $filters = array( |
||
77 | 'global' => array(), |
||
78 | 'controller' => array( |
||
79 | '*' => null |
||
80 | ), |
||
81 | 'dispatch' => null, |
||
82 | 'execution' => null, |
||
83 | 'security' => null |
||
84 | ); |
||
85 | |||
86 | /** |
||
87 | * @var string The default Output Type. |
||
88 | */ |
||
89 | protected $defaultOutputType = null; |
||
90 | |||
91 | /** |
||
92 | * @var array An array of registered Output Types. |
||
93 | */ |
||
94 | protected $outputTypes = array(); |
||
95 | |||
96 | /** |
||
97 | * @var array Ref to the request data object from the request. |
||
98 | */ |
||
99 | private $requestData = null; |
||
100 | |||
101 | /** |
||
102 | * Increment the execution counter. |
||
103 | * Will throw an exception if the maximum amount of runs is exceeded. |
||
104 | * |
||
105 | * @throws DispatcherException If too many execution runs were made. |
||
106 | * |
||
107 | * @author David Zülke <[email protected]> |
||
108 | * @since 0.11.0 |
||
109 | */ |
||
110 | public function countExecution() |
||
118 | |||
119 | /** |
||
120 | * Create and initialize new execution container instance. |
||
121 | * |
||
122 | * @param string $moduleName The name of the module. |
||
123 | * @param string $controllerName The name of the controller. |
||
124 | * @param RequestDataHolder $arguments A RequestDataHolder with additional |
||
125 | * request arguments. |
||
126 | * @param string $outputType Optional name of an initial output type |
||
127 | * to set. |
||
128 | * @param string $requestMethod Optional name of the request method to |
||
129 | * be used in this container. |
||
130 | * |
||
131 | * @return ExecutionContainer A new execution container instance, |
||
132 | * fully initialized. |
||
133 | * |
||
134 | * @author David Zülke <[email protected]> |
||
135 | * @since 0.11.0 |
||
136 | */ |
||
137 | public function createExecutionContainer($moduleName = null, $controllerName = null, RequestDataHolder $arguments = null, $outputType = null, $requestMethod = null) |
||
155 | |||
156 | /** |
||
157 | * Initialize a module and load its autoload, module config etc. |
||
158 | * |
||
159 | * @param string $moduleName The name of the module to initialize. |
||
160 | * |
||
161 | * @author Felix Gilcher <[email protected]> |
||
162 | * @since 1.0.0 |
||
163 | */ |
||
164 | public function initializeModule($moduleName) |
||
209 | |||
210 | /** |
||
211 | * Dispatch a request |
||
212 | * |
||
213 | * @param RequestDataHolder $arguments An optional request data holder object |
||
214 | * with additional request data. |
||
215 | * @param ExecutionContainer $container An optional execution container that, |
||
216 | * if given, will be executed right away, |
||
217 | * skipping routing execution. |
||
218 | * |
||
219 | * @return Response The response produced during this dispatch call. |
||
220 | * |
||
221 | * @author David Zülke <[email protected]> |
||
222 | * @since 0.9.0 |
||
223 | */ |
||
224 | public function dispatch(RequestDataHolder $arguments = null, ExecutionContainer $container = null) |
||
301 | |||
302 | /** |
||
303 | * Get the global response instance. |
||
304 | * |
||
305 | * @return Response The global response. |
||
306 | * |
||
307 | * @author David Zülke <[email protected]> |
||
308 | * @since 0.11.0 |
||
309 | */ |
||
310 | public function getGlobalResponse() |
||
314 | |||
315 | |||
316 | /** |
||
317 | * Indicates whether or not a module has a specific controller file. |
||
318 | * |
||
319 | * Please note that this is only a cursory check and does not |
||
320 | * check whether the file actually contains the proper class |
||
321 | * |
||
322 | * @param string $moduleName A module name. |
||
323 | * @param string $controllerName An controller name. |
||
324 | * |
||
325 | * @return string|false the path to the controller file if the controller file |
||
326 | * exists and is readable, false in any other case |
||
327 | * |
||
328 | * @author Felix Gilcher <[email protected]> |
||
329 | * @since 1.0.0 |
||
330 | */ |
||
331 | View Code Duplication | public function checkControllerFile($moduleName, $controllerName) |
|
351 | |||
352 | /** |
||
353 | * Retrieve an Controller implementation instance. |
||
354 | * |
||
355 | * @param string $moduleName A module name. |
||
356 | * @param string $controllerName An controller name. |
||
357 | * |
||
358 | * @return Controller An Controller implementation instance |
||
359 | * |
||
360 | * @throws FileNotFoundException|ClassNotFoundException if the controller could not be found. |
||
361 | * |
||
362 | * @author Sean Kerr <[email protected]> |
||
363 | * @author Mike Vincent <[email protected]> |
||
364 | * @author David Zülke <[email protected]> |
||
365 | * @since 0.9.0 |
||
366 | */ |
||
367 | public function createControllerInstance($moduleName, $controllerName) |
||
368 | { |
||
369 | $this->initializeModule($moduleName); |
||
370 | |||
371 | |||
372 | $longControllerName = str_replace('/', '\\', Toolkit::canonicalName(Toolkit::stripNamespace($controllerName))); |
||
373 | $ns = Config::get('app.namespace'); |
||
374 | |||
375 | if (strlen($ns) > 0) { |
||
376 | $ns .= '\\Modules\\' . $moduleName . '\\Controllers\\'; |
||
377 | } |
||
378 | $class = $ns . $longControllerName . 'Controller'; |
||
379 | if (!class_exists($class)) { |
||
380 | if (false !== ($file = $this->checkControllerFile($moduleName, $controllerName))) { |
||
381 | require($file); |
||
382 | } else { |
||
383 | throw new FileNotFoundException(sprintf('Could not find file for Controller "%s" in Module "%s".', $controllerName, $moduleName)); |
||
384 | } |
||
385 | |||
386 | if (!class_exists($class, false)) { |
||
387 | throw new ClassNotFoundException(sprintf('Failed to instantiate Controller "%s" in Module "%s" because file "%s" does not contain class "%s".', $controllerName, $moduleName, $file, $class)); |
||
388 | } |
||
389 | } |
||
390 | |||
391 | return new $class(); |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * Retrieve the current application context. |
||
396 | * |
||
397 | * @return Context An Context instance. |
||
398 | * |
||
399 | * @author Sean Kerr <[email protected]> |
||
400 | * @since 0.9.0 |
||
401 | */ |
||
402 | final public function getContext() |
||
406 | |||
407 | |||
408 | |||
409 | /** |
||
410 | * Indicates whether or not a module has a specific view file. |
||
411 | * |
||
412 | * Please note that this is only a cursory check and does not |
||
413 | * check whether the file actually contains the proper class |
||
414 | * |
||
415 | * @param string $moduleName A module name. |
||
416 | * @param string $viewName A view name. |
||
417 | * |
||
418 | * @return string|false the path to the view file if the view file |
||
419 | * exists and is readable, false in any other case |
||
420 | * |
||
421 | * @author Felix Gilcher <[email protected]> |
||
422 | * @since 1.0.0 |
||
423 | */ |
||
424 | View Code Duplication | public function checkViewFile($moduleName, $viewName) |
|
444 | |||
445 | /** |
||
446 | * Retrieve a View implementation instance. |
||
447 | * |
||
448 | * @param string $moduleName A module name. |
||
449 | * @param string $viewName A view name. |
||
450 | * |
||
451 | * @return View A View implementation instance, |
||
452 | * |
||
453 | * @throws AgaviException if the view could not be found. |
||
454 | * |
||
455 | * @author Sean Kerr <[email protected]> |
||
456 | * @author Mike Vincent <[email protected]> |
||
457 | * @author David Zülke <[email protected]> |
||
458 | * @since 0.9.0 |
||
459 | */ |
||
460 | public function createViewInstance($moduleName, $viewName) |
||
491 | |||
492 | /** |
||
493 | * Constructor. |
||
494 | * |
||
495 | * @author David Zülke <[email protected]> |
||
496 | * @since 0.11.0 |
||
497 | */ |
||
498 | public function __construct() |
||
506 | |||
507 | /** |
||
508 | * Initialize this Dispatcher. |
||
509 | * |
||
510 | * @param Context $context A Context instance. |
||
511 | * @param array $parameters An array of initialization parameters. |
||
512 | * |
||
513 | * @author David Zülke <[email protected]> |
||
514 | * @since 0.9.0 |
||
515 | */ |
||
516 | public function initialize(Context $context, array $parameters = array()) |
||
535 | |||
536 | /** |
||
537 | * Get a filter. |
||
538 | * |
||
539 | * @param string $which The name of the filter list section. |
||
540 | * |
||
541 | * @return Filter A filter instance, or null. |
||
542 | * |
||
543 | * @author David Zülke <[email protected]> |
||
544 | * @since 0.11.0 |
||
545 | */ |
||
546 | public function getFilter($which) |
||
550 | |||
551 | /** |
||
552 | * Get the global filter chain. |
||
553 | * |
||
554 | * @return FilterChain The global filter chain. |
||
555 | * |
||
556 | * @author David Zülke <[email protected]> |
||
557 | * @since 1.1.0 |
||
558 | */ |
||
559 | View Code Duplication | public function getFilterChain() |
|
568 | |||
569 | /** |
||
570 | * Load filters. |
||
571 | * |
||
572 | * @param FilterChain $filterChain A FilterChain instance. |
||
573 | * @param string $which "global" or "controller". |
||
574 | * @param string $module A module name, or "*" for the generic config. |
||
575 | * |
||
576 | * @author David Zülke <[email protected]> |
||
577 | * @since 0.11.0 |
||
578 | */ |
||
579 | public function loadFilters(FilterChain $filterChain, $which = 'global', $module = null) |
||
609 | |||
610 | /** |
||
611 | * Indicates whether or not a module has a specific model. |
||
612 | * |
||
613 | * @param string $moduleName A module name. |
||
614 | * @param string $modelName A model name. |
||
615 | * |
||
616 | * @return bool true, if the model exists, otherwise false. |
||
617 | * |
||
618 | * @author Sean Kerr <[email protected]> |
||
619 | * @since 0.9.0 |
||
620 | */ |
||
621 | public function modelExists($moduleName, $modelName) |
||
627 | |||
628 | /** |
||
629 | * Indicates whether or not a module exists. |
||
630 | * |
||
631 | * @param string $moduleName A module name. |
||
632 | * |
||
633 | * @return bool true, if the module exists, otherwise false. |
||
634 | * |
||
635 | * @author Sean Kerr <[email protected]> |
||
636 | * @since 0.9.0 |
||
637 | */ |
||
638 | public function moduleExists($moduleName) |
||
643 | |||
644 | /** |
||
645 | * Do any necessary startup work after initialization. |
||
646 | * |
||
647 | * This method is not called directly after initialize(). |
||
648 | * |
||
649 | * @author David Zülke <[email protected]> |
||
650 | * @since 0.11.0 |
||
651 | */ |
||
652 | public function startup() |
||
657 | |||
658 | /** |
||
659 | * Execute the shutdown procedure for this Dispatcher. |
||
660 | * |
||
661 | * @author Sean Kerr <[email protected]> |
||
662 | * @since 0.9.0 |
||
663 | */ |
||
664 | public function shutdown() |
||
667 | |||
668 | /** |
||
669 | * Indicates whether or not a module has a specific controller. |
||
670 | * |
||
671 | * @param string $moduleName A module name. |
||
672 | * @param string $controllerName A view name. |
||
673 | * |
||
674 | * @return bool true, if the controller exists, otherwise false. |
||
675 | * |
||
676 | * @author David Zülke <[email protected]> |
||
677 | * @since 1.0.1 |
||
678 | */ |
||
679 | public function controllerExists($moduleName, $controllerName) |
||
683 | |||
684 | /** |
||
685 | * Indicates whether or not a module has a specific view. |
||
686 | * |
||
687 | * @param string $moduleName A module name. |
||
688 | * @param string $viewName A view name. |
||
689 | * |
||
690 | * @return bool true, if the view exists, otherwise false. |
||
691 | * |
||
692 | * @author Sean Kerr <[email protected]> |
||
693 | * @since 0.9.0 |
||
694 | */ |
||
695 | public function viewExists($moduleName, $viewName) |
||
699 | |||
700 | /** |
||
701 | * Retrieve an Output Type object |
||
702 | * |
||
703 | * @param string $name The optional output type name. |
||
704 | * |
||
705 | * @return OutputType An Output Type object. |
||
706 | * |
||
707 | * @author David Zülke <[email protected]> |
||
708 | * @since 0.11.0 |
||
709 | */ |
||
710 | View Code Duplication | public function getOutputType($name = null) |
|
721 | } |
||
722 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: