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 Menu 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 Menu, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class Menu extends \Zend\View\Helper\Navigation\Menu |
||
29 | { |
||
30 | use \UIComponents\View\Helper\Traits\ComponentClassnamesTrait; |
||
31 | use \UIComponents\View\Helper\Traits\ComponentAttributesTrait; |
||
32 | |||
33 | /** |
||
34 | * default CSS class to use for li elements |
||
35 | * |
||
36 | * @var string |
||
37 | */ |
||
38 | protected $defaultLiClass = ''; |
||
39 | |||
40 | /** |
||
41 | * CSS class to use for the ul sub-menu element |
||
42 | * |
||
43 | * @var string |
||
44 | */ |
||
45 | protected $subUlClass = 'dropdown-menu'; |
||
46 | |||
47 | /** |
||
48 | * CSS class to use for the 1. level (NOT root level!) ul sub-menu element |
||
49 | * |
||
50 | * @var string |
||
51 | */ |
||
52 | protected $subUlClassLevel1 = 'dropdown-menu'; |
||
53 | |||
54 | /** |
||
55 | * CSS class to use for the active li sub-menu element |
||
56 | * |
||
57 | * @var string |
||
58 | */ |
||
59 | protected $subLiClass = 'dropdown-submenu'; |
||
60 | |||
61 | /** |
||
62 | * CSS class to use for the active li sub-menu element |
||
63 | * |
||
64 | * @var string |
||
65 | */ |
||
66 | protected $subLiClassLevel0 = 'dropdown'; |
||
67 | |||
68 | /** |
||
69 | * CSS class prefix to use for the menu element's icon class |
||
70 | * |
||
71 | * @var string |
||
72 | */ |
||
73 | protected $iconPrefixClass = 'icon-'; |
||
74 | |||
75 | /** |
||
76 | * HREF string to use for the sub-menu toggle element's HREF attribute, |
||
77 | * to override current page's href/'htmlify' setting |
||
78 | * |
||
79 | * @var string |
||
80 | */ |
||
81 | protected $hrefSubToggleOverride = null; |
||
82 | |||
83 | /** |
||
84 | * Partial view script to use for rendering menu link/item |
||
85 | * |
||
86 | * @var string|array |
||
87 | */ |
||
88 | protected $htmlifyPartial = null; |
||
89 | |||
90 | |||
91 | |||
92 | /** |
||
93 | * View helper entry point: |
||
94 | * Retrieves helper and optionally sets container to operate on |
||
95 | * |
||
96 | * @param AbstractContainer $container [optional] container to operate on |
||
97 | * @return self |
||
98 | */ |
||
99 | public function __invoke($container = null) |
||
107 | |||
108 | /** |
||
109 | * Returns the navigation container helper operates on by default |
||
110 | * |
||
111 | * Implements {@link HelperInterface::getContainer()}. |
||
112 | * |
||
113 | * If no container is set, a new container will be instantiated and |
||
114 | * stored in the helper. |
||
115 | * |
||
116 | * @return Navigation\AbstractContainer navigation container |
||
117 | */ |
||
118 | public function getContainer() |
||
126 | |||
127 | /** |
||
128 | * Renders helper |
||
129 | * |
||
130 | * Renders a HTML 'ul' for the given $container. If $container is not given, |
||
131 | * the container registered in the helper will be used. |
||
132 | * |
||
133 | * Available $options: |
||
134 | * |
||
135 | * |
||
136 | * @param AbstractContainer $container [optional] container to create menu from. |
||
137 | * Default is to use the container retrieved |
||
138 | * from {@link getContainer()}. |
||
139 | * @param array $options [optional] options for controlling rendering |
||
140 | * @return string |
||
141 | */ |
||
142 | public function renderMenu($container = null, array $options = []) |
||
179 | |||
180 | /** |
||
181 | * Renders a normal menu (called from {@link renderMenu()}) |
||
182 | * |
||
183 | * @param AbstractContainer $container container to render |
||
184 | * @param string $ulClass CSS class for first UL |
||
185 | * @param string $indent initial indentation |
||
186 | * @param int|null $minDepth minimum depth |
||
187 | * @param int|null $maxDepth maximum depth |
||
188 | * @param bool $onlyActive render only active branch? |
||
189 | * @param bool $escapeLabels Whether or not to escape the labels |
||
190 | * @param bool $addClassToListItem Whether or not page class applied to <li> element |
||
191 | * @param string $liActiveClass CSS class for active LI |
||
192 | * @return string |
||
193 | */ |
||
194 | protected function renderNormalMenu( |
||
330 | |||
331 | /** |
||
332 | * Finds the deepest active page in the given container |
||
333 | * |
||
334 | * @param Navigation\AbstractContainer $container container to search |
||
335 | * @param int|null $minDepth [optional] minimum depth |
||
336 | * required for page to be |
||
337 | * valid. Default is to use |
||
338 | * {@link getMinDepth()}. A |
||
339 | * null value means no minimum |
||
340 | * depth required. |
||
341 | * @param int|null $maxDepth [optional] maximum depth |
||
342 | * a page can have to be |
||
343 | * valid. Default is to use |
||
344 | * {@link getMaxDepth()}. A |
||
345 | * null value means no maximum |
||
346 | * depth required. |
||
347 | * @return array an associative array with |
||
348 | * the values 'depth' and |
||
349 | * 'page', or an empty array |
||
350 | * if not found |
||
351 | */ |
||
352 | public function findActive($container = null, $minDepth = null, $maxDepth = -1) |
||
359 | |||
360 | /** |
||
361 | * Returns an HTML string containing an 'a' element for the given page if |
||
362 | * the page's href is not empty, and a 'span' element if it is empty |
||
363 | * |
||
364 | * Overrides {@link AbstractHelper::htmlify()}. |
||
365 | * |
||
366 | * @param AbstractPage $page page to generate HTML for |
||
367 | * @param bool $escapeLabel Whether or not to escape the label |
||
368 | * @param bool $addClassToListItem Whether or not to add the page class to the list item |
||
369 | * @return string |
||
370 | */ |
||
371 | public function htmlify(AbstractPage $page, $escapeLabel = true, $addClassToListItem = false) |
||
425 | |||
426 | /** |
||
427 | * Renders the given $page by invoking the partial view helper |
||
428 | * |
||
429 | * The container will simply be passed on as a model to the view script |
||
430 | * as-is, and will be available in the partial script as 'container', e.g. |
||
431 | * <code>echo 'Number of pages: ', count($this->container);</code>. |
||
432 | * |
||
433 | * @param string|array $partial [optional] partial view script to use. |
||
434 | * Default is to use the partial |
||
435 | * registered in the helper. If an array |
||
436 | * is given, it is expected to contain two |
||
437 | * values; the partial view script to use, |
||
438 | * and the module where the script can be |
||
439 | * found. |
||
440 | * @return string |
||
441 | * @throws Exception\RuntimeException if no partial provided |
||
442 | * @throws Exception\InvalidArgumentException if partial is invalid array |
||
443 | */ |
||
444 | public function renderHtmlifyPartial(AbstractPage $page, $escapeLabel = true, $addClassToListItem = false, $partial = null) |
||
480 | |||
481 | /** |
||
482 | * @return the $defaultLiClass |
||
483 | */ |
||
484 | public function getDefaultLiClass() { |
||
487 | |||
488 | /** |
||
489 | * @param string $defaultLiClass |
||
490 | */ |
||
491 | public function setDefaultLiClass($defaultLiClass) { |
||
495 | |||
496 | /** |
||
497 | * @return the $subUlClass |
||
498 | */ |
||
499 | public function getSubUlClass() { |
||
502 | |||
503 | /** |
||
504 | * @param string $subUlClass |
||
505 | */ |
||
506 | public function setSubUlClass($subUlClass) { |
||
510 | |||
511 | /** |
||
512 | * @return the $subUlClassLevel1 |
||
513 | */ |
||
514 | public function getSubUlClassLevel1() { |
||
517 | |||
518 | /** |
||
519 | * @param string $subUlClassLevel1 |
||
520 | */ |
||
521 | public function setSubUlClassLevel1($subUlClassLevel1) { |
||
525 | |||
526 | /** |
||
527 | * @return the $subLiClass |
||
528 | */ |
||
529 | public function getSubLiClass() { |
||
532 | |||
533 | /** |
||
534 | * @param string $subLiClass |
||
535 | */ |
||
536 | public function setSubLiClass($subLiClass) { |
||
540 | |||
541 | /** |
||
542 | * @return the $subLiClassLevel0 |
||
543 | */ |
||
544 | public function getSubLiClassLevel0() { |
||
547 | |||
548 | /** |
||
549 | * @param string $subLiClassLevel0 |
||
550 | */ |
||
551 | public function setSubLiClassLevel0($subLiClassLevel0) { |
||
555 | |||
556 | /** |
||
557 | * @return the $iconPrefixClass |
||
558 | */ |
||
559 | public function getIconPrefixClass() { |
||
562 | |||
563 | /** |
||
564 | * @param string $iconPrefixClass |
||
565 | */ |
||
566 | public function setIconPrefixClass($iconPrefixClass) { |
||
570 | |||
571 | /** |
||
572 | * @return the $hrefSubToggleOverride |
||
573 | */ |
||
574 | public function getHrefSubToggleOverride() { |
||
577 | |||
578 | /** |
||
579 | * @param string $hrefSubToggleOverride |
||
580 | */ |
||
581 | public function setHrefSubToggleOverride($hrefSubToggleOverride) { |
||
585 | |||
586 | /** |
||
587 | * Sets which partial view script to use for rendering menu |
||
588 | * |
||
589 | * @param string|array $partial partial view script or null. If an array is |
||
590 | * given, it is expected to contain two |
||
591 | * values; the partial view script to use, |
||
592 | * and the module where the script can be |
||
593 | * found. |
||
594 | * @return self |
||
595 | */ |
||
596 | View Code Duplication | public function setHtmlifyPartial($partial) |
|
604 | |||
605 | /** |
||
606 | * Returns partial view script to use for rendering menu |
||
607 | * |
||
608 | * @return string|array|null |
||
609 | */ |
||
610 | public function getHtmlifyPartial() |
||
614 | |||
615 | /** |
||
616 | * Translate a message (for label, title, …) |
||
617 | * |
||
618 | * @param string $message ID of the message to translate |
||
619 | * @param string $textDomain Text domain (category name for the translations) |
||
620 | * @return string Translated message |
||
621 | */ |
||
622 | public function translate($message, $textDomain = null) |
||
626 | |||
627 | |||
628 | } |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.