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 NodeMenu 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 NodeMenu, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 15 | class NodeMenu  | 
            ||
| 16 | { | 
            ||
| 17 | /**  | 
            ||
| 18 | * @var EntityManagerInterface  | 
            ||
| 19 | */  | 
            ||
| 20 | private $em;  | 
            ||
| 21 | |||
| 22 | /**  | 
            ||
| 23 | * @var TokenStorageInterface  | 
            ||
| 24 | */  | 
            ||
| 25 | private $tokenStorage;  | 
            ||
| 26 | |||
| 27 | /**  | 
            ||
| 28 | * @var AclHelper  | 
            ||
| 29 | */  | 
            ||
| 30 | private $aclHelper;  | 
            ||
| 31 | |||
| 32 | /**  | 
            ||
| 33 | * @var string  | 
            ||
| 34 | */  | 
            ||
| 35 | private $locale;  | 
            ||
| 36 | |||
| 37 | /**  | 
            ||
| 38 | * @var Node  | 
            ||
| 39 | */  | 
            ||
| 40 | private $currentNode;  | 
            ||
| 41 | |||
| 42 | /**  | 
            ||
| 43 | * @var string  | 
            ||
| 44 | */  | 
            ||
| 45 | private $permission = PermissionMap::PERMISSION_VIEW;  | 
            ||
| 46 | |||
| 47 | /**  | 
            ||
| 48 | * @var bool  | 
            ||
| 49 | */  | 
            ||
| 50 | private $includeOffline = false;  | 
            ||
| 51 | |||
| 52 | /**  | 
            ||
| 53 | * @var bool  | 
            ||
| 54 | */  | 
            ||
| 55 | private $includeHiddenFromNav = false;  | 
            ||
| 56 | |||
| 57 | /**  | 
            ||
| 58 | * @var NodeMenuItem[]  | 
            ||
| 59 | */  | 
            ||
| 60 | private $topNodeMenuItems;  | 
            ||
| 61 | |||
| 62 | /**  | 
            ||
| 63 | * @var NodeMenuItem[]  | 
            ||
| 64 | */  | 
            ||
| 65 | private $breadCrumb;  | 
            ||
| 66 | |||
| 67 | /**  | 
            ||
| 68 | * @var Node[]  | 
            ||
| 69 | */  | 
            ||
| 70 | private $allNodes = array();  | 
            ||
| 71 | |||
| 72 | /**  | 
            ||
| 73 | * @var Node[]  | 
            ||
| 74 | */  | 
            ||
| 75 | private $childNodes = array();  | 
            ||
| 76 | |||
| 77 | /**  | 
            ||
| 78 | * @var Node[]  | 
            ||
| 79 | */  | 
            ||
| 80 | private $nodesByInternalName = array();  | 
            ||
| 81 | |||
| 82 | /**  | 
            ||
| 83 | * @var bool  | 
            ||
| 84 | */  | 
            ||
| 85 | private $initialized = false;  | 
            ||
| 86 | |||
| 87 | /**  | 
            ||
| 88 | * @var NodeMenuItem  | 
            ||
| 89 | */  | 
            ||
| 90 | private $rootNodeMenuItem;  | 
            ||
| 91 | |||
| 92 | /**  | 
            ||
| 93 | * @var DomainConfigurationInterface  | 
            ||
| 94 | */  | 
            ||
| 95 | private $domainConfiguration;  | 
            ||
| 96 | |||
| 97 | /**  | 
            ||
| 98 | * @param EntityManagerInterface $em The entity manager  | 
            ||
| 99 | * @param TokenStorageInterface $tokenStorage The security token storage  | 
            ||
| 100 | * @param AclHelper $aclHelper The ACL helper pages  | 
            ||
| 101 | * @param DomainConfigurationInterface $domainConfiguration The current domain configuration  | 
            ||
| 102 | */  | 
            ||
| 103 | public function __construct(  | 
            ||
| 114 | |||
| 115 | /**  | 
            ||
| 116 | * @param string $locale  | 
            ||
| 117 | */  | 
            ||
| 118 | public function setLocale($locale)  | 
            ||
| 122 | |||
| 123 | /**  | 
            ||
| 124 | * @param Node $currentNode  | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 125 | */  | 
            ||
| 126 | public function setCurrentNode(Node $currentNode = null)  | 
            ||
| 130 | |||
| 131 | /**  | 
            ||
| 132 | * @param string $permission  | 
            ||
| 133 | */  | 
            ||
| 134 | public function setPermission($permission)  | 
            ||
| 142 | |||
| 143 | /**  | 
            ||
| 144 | * @param bool $includeOffline  | 
            ||
| 145 | */  | 
            ||
| 146 | public function setIncludeOffline($includeOffline)  | 
            ||
| 150 | |||
| 151 | /**  | 
            ||
| 152 | * @param bool $includeHiddenFromNav  | 
            ||
| 153 | */  | 
            ||
| 154 | public function setIncludeHiddenFromNav($includeHiddenFromNav)  | 
            ||
| 162 | |||
| 163 | /**  | 
            ||
| 164 | * This method initializes the nodemenu only once, the method may be  | 
            ||
| 165 | * executed multiple times  | 
            ||
| 166 | */  | 
            ||
| 167 | private function init()  | 
            ||
| 207 | |||
| 208 | /**  | 
            ||
| 209 | * @return NodeMenuItem[]  | 
            ||
| 210 | */  | 
            ||
| 211 | public function getTopNodes()  | 
            ||
| 237 | |||
| 238 | /**  | 
            ||
| 239 | * @return NodeMenuItem[]  | 
            ||
| 240 | */  | 
            ||
| 241 | public function getBreadCrumb()  | 
            ||
| 242 |     { | 
            ||
| 243 | $this->init();  | 
            ||
| 244 |         if (!\is_array($this->breadCrumb)) { | 
            ||
| 245 | $this->breadCrumb = array();  | 
            ||
| 246 | |||
| 247 | /* @var NodeRepository $repo */  | 
            ||
| 248 |             $repo = $this->em->getRepository('KunstmaanNodeBundle:Node'); | 
            ||
| 249 | |||
| 250 | // Generate breadcrumb MenuItems - fetch *all* languages so you can link translations if needed  | 
            ||
| 251 | $parentNodes = $repo->getAllParents($this->currentNode);  | 
            ||
| 252 | $parentNodeMenuItem = null;  | 
            ||
| 253 | /* @var Node $parentNode */  | 
            ||
| 254 |             foreach ($parentNodes as $parentNode) { | 
            ||
| 255 | $nodeTranslation = $parentNode->getNodeTranslation(  | 
            ||
| 256 | $this->locale,  | 
            ||
| 257 | $this->includeOffline  | 
            ||
| 258 | );  | 
            ||
| 259 |                 if (!\is_null($nodeTranslation)) { | 
            ||
| 260 | $nodeMenuItem = new NodeMenuItem(  | 
            ||
| 261 | $parentNode,  | 
            ||
| 262 | $nodeTranslation,  | 
            ||
| 263 | $parentNodeMenuItem,  | 
            ||
| 264 | $this  | 
            ||
| 265 | );  | 
            ||
| 266 | $this->breadCrumb[] = $nodeMenuItem;  | 
            ||
| 267 | $parentNodeMenuItem = $nodeMenuItem;  | 
            ||
| 268 | }  | 
            ||
| 269 | }  | 
            ||
| 270 | }  | 
            ||
| 271 | |||
| 272 | return $this->breadCrumb;  | 
            ||
| 273 | }  | 
            ||
| 274 | |||
| 275 | /**  | 
            ||
| 276 | * @return NodeMenuItem|null  | 
            ||
| 277 | */  | 
            ||
| 278 | View Code Duplication | public function getCurrent()  | 
            |
| 288 | |||
| 289 | /**  | 
            ||
| 290 | * @param int $depth  | 
            ||
| 291 | *  | 
            ||
| 292 | * @return NodeMenuItem|null  | 
            ||
| 293 | */  | 
            ||
| 294 | View Code Duplication | public function getActiveForDepth($depth)  | 
            |
| 303 | |||
| 304 | /**  | 
            ||
| 305 | * @param Node $node  | 
            ||
| 306 | * @param bool $includeHiddenFromNav  | 
            ||
| 307 | *  | 
            ||
| 308 | * @return NodeMenuItem[]  | 
            ||
| 309 | */  | 
            ||
| 310 | public function getChildren(Node $node, $includeHiddenFromNav = true)  | 
            ||
| 311 |     { | 
            ||
| 312 | $this->init();  | 
            ||
| 313 | $children = array();  | 
            ||
| 314 | |||
| 315 |         if (\array_key_exists($node->getId(), $this->childNodes)) { | 
            ||
| 316 | $nodes = $this->childNodes[$node->getId()];  | 
            ||
| 317 | /* @var Node $childNode */  | 
            ||
| 318 | View Code Duplication |             foreach ($nodes as $childNode) { | 
            |
| 319 | $nodeTranslation = $childNode->getNodeTranslation(  | 
            ||
| 320 | $this->locale,  | 
            ||
| 321 | $this->includeOffline  | 
            ||
| 322 | );  | 
            ||
| 323 |                 if (!\is_null($nodeTranslation)) { | 
            ||
| 324 | $children[] = new NodeMenuItem(  | 
            ||
| 325 | $childNode,  | 
            ||
| 326 | $nodeTranslation,  | 
            ||
| 327 | false,  | 
            ||
| 328 | $this  | 
            ||
| 329 | );  | 
            ||
| 330 | }  | 
            ||
| 331 | }  | 
            ||
| 332 | |||
| 333 | $children = array_filter(  | 
            ||
| 334 | $children,  | 
            ||
| 335 | View Code Duplication |                 function (NodeMenuItem $entry) use ($includeHiddenFromNav) { | 
            |
| 336 |                     if ($entry->getNode()->isHiddenFromNav() && !$includeHiddenFromNav) { | 
            ||
| 337 | return false;  | 
            ||
| 338 | }  | 
            ||
| 339 | |||
| 340 | return true;  | 
            ||
| 341 | }  | 
            ||
| 342 | );  | 
            ||
| 343 | }  | 
            ||
| 344 | |||
| 345 | return $children;  | 
            ||
| 346 | }  | 
            ||
| 347 | |||
| 348 | /**  | 
            ||
| 349 | * @param \Kunstmaan\NodeBundle\Entity\Node $node  | 
            ||
| 350 | * @param bool $includeHiddenFromNav  | 
            ||
| 351 | *  | 
            ||
| 352 | * @return array|\Kunstmaan\NodeBundle\Helper\NodeMenuItem[]  | 
            ||
| 353 | */  | 
            ||
| 354 | public function getSiblings(Node $node, $includeHiddenFromNav = true)  | 
            ||
| 371 | |||
| 372 | /**  | 
            ||
| 373 | * @param \Kunstmaan\NodeBundle\Entity\Node $node  | 
            ||
| 374 | * @param bool $includeHiddenFromNav  | 
            ||
| 375 | *  | 
            ||
| 376 | * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem  | 
            ||
| 377 | */  | 
            ||
| 378 | public function getPreviousSibling(Node $node, $includeHiddenFromNav = true)  | 
            ||
| 394 | |||
| 395 | /**  | 
            ||
| 396 | * @param \Kunstmaan\NodeBundle\Entity\Node $node  | 
            ||
| 397 | * @param bool $includeHiddenFromNav  | 
            ||
| 398 | *  | 
            ||
| 399 | * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem  | 
            ||
| 400 | */  | 
            ||
| 401 | public function getNextSibling(Node $node, $includeHiddenFromNav = true)  | 
            ||
| 420 | |||
| 421 | /**  | 
            ||
| 422 | * @param Node $node  | 
            ||
| 423 | *  | 
            ||
| 424 | * @return NodeMenuItem  | 
            ||
| 425 | */  | 
            ||
| 426 | public function getParent(Node $node)  | 
            ||
| 439 | |||
| 440 | /**  | 
            ||
| 441 | * @param NodeTranslation $parentNode The parent node  | 
            ||
| 442 | * @param string $slug The slug  | 
            ||
| 443 | *  | 
            ||
| 444 | * @return NodeTranslation  | 
            ||
| 445 | */  | 
            ||
| 446 | public function getNodeBySlug(NodeTranslation $parentNode, $slug)  | 
            ||
| 451 | |||
| 452 | /**  | 
            ||
| 453 | * @param string $internalName The  | 
            ||
| 454 | * internal  | 
            ||
| 455 | * name  | 
            ||
| 456 | * @param NodeTranslation|NodeMenuItem|HasNodeInterface $parent The  | 
            ||
| 457 | * parent  | 
            ||
| 458 | * @param bool $includeOffline  | 
            ||
| 459 | *  | 
            ||
| 460 | * @return NodeMenuItem|null  | 
            ||
| 461 | */  | 
            ||
| 462 | public function getNodeByInternalName(  | 
            ||
| 551 | |||
| 552 | /**  | 
            ||
| 553 | * Returns the current root node menu item  | 
            ||
| 554 | */  | 
            ||
| 555 | public function getRootNodeMenuItem()  | 
            ||
| 577 | |||
| 578 | /**  | 
            ||
| 579 | * @return bool  | 
            ||
| 580 | */  | 
            ||
| 581 | public function isIncludeOffline()  | 
            ||
| 585 | |||
| 586 | /**  | 
            ||
| 587 | * @return string  | 
            ||
| 588 | */  | 
            ||
| 589 | public function getPermission()  | 
            ||
| 593 | |||
| 594 | /**  | 
            ||
| 595 | * @return BaseUser  | 
            ||
| 596 | */  | 
            ||
| 597 | public function getUser()  | 
            ||
| 601 | |||
| 602 | /**  | 
            ||
| 603 | * @return EntityManagerInterface  | 
            ||
| 604 | */  | 
            ||
| 605 | public function getEntityManager()  | 
            ||
| 609 | |||
| 610 | /**  | 
            ||
| 611 | * @return TokenStorageInterface  | 
            ||
| 612 | */  | 
            ||
| 613 | public function getTokenStorage()  | 
            ||
| 617 | |||
| 618 | /**  | 
            ||
| 619 | * @return AclHelper  | 
            ||
| 620 | */  | 
            ||
| 621 | public function getAclHelper()  | 
            ||
| 625 | |||
| 626 | /**  | 
            ||
| 627 | * @return string  | 
            ||
| 628 | */  | 
            ||
| 629 | public function getLocale()  | 
            ||
| 633 | |||
| 634 | /**  | 
            ||
| 635 | * @return bool  | 
            ||
| 636 | */  | 
            ||
| 637 | public function isIncludeHiddenFromNav()  | 
            ||
| 641 | |||
| 642 | /**  | 
            ||
| 643 | * Check if provided slug is in active path  | 
            ||
| 644 | *  | 
            ||
| 645 | * @param string $slug  | 
            ||
| 646 | *  | 
            ||
| 647 | * @return bool  | 
            ||
| 648 | */  | 
            ||
| 649 | public function getActive($slug)  | 
            ||
| 660 | |||
| 661 | /**  | 
            ||
| 662 | * @return bool  | 
            ||
| 663 | */  | 
            ||
| 664 | public function isInitialized()  | 
            ||
| 668 | |||
| 669 | /**  | 
            ||
| 670 | * @return array  | 
            ||
| 671 | */  | 
            ||
| 672 | private function getTopNodeMenuItems()  | 
            ||
| 694 | }  | 
            ||
| 695 | 
This check looks for
@paramannotations where the type inferred by our type inference engine differs from the declared type.It makes a suggestion as to what type it considers more descriptive.
Most often this is a case of a parameter that can be null in addition to its declared types.