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 MenuItem 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 MenuItem, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 24 | class MenuItem implements MenuItemInterface |
||
| 25 | { |
||
| 26 | /** |
||
| 27 | * @var mixed |
||
| 28 | */ |
||
| 29 | protected $id; |
||
| 30 | |||
| 31 | /** |
||
| 32 | * @var MenuItemInterface |
||
| 33 | */ |
||
| 34 | protected $root; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * @var int |
||
| 38 | */ |
||
| 39 | protected $lft; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * @var int |
||
| 43 | */ |
||
| 44 | protected $rgt; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * @var int |
||
| 48 | */ |
||
| 49 | protected $level; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * Name of this menu item (used for id by parent menu). |
||
| 53 | * |
||
| 54 | * @var string |
||
| 55 | */ |
||
| 56 | protected $name = null; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * Label to output. |
||
| 60 | * |
||
| 61 | * @var string |
||
| 62 | */ |
||
| 63 | protected $label = null; |
||
| 64 | |||
| 65 | /** |
||
| 66 | * Attributes for the item link. |
||
| 67 | * |
||
| 68 | * @var array |
||
| 69 | */ |
||
| 70 | protected $linkAttributes = []; |
||
| 71 | |||
| 72 | /** |
||
| 73 | * Attributes for the children list. |
||
| 74 | * |
||
| 75 | * @var array |
||
| 76 | */ |
||
| 77 | protected $childrenAttributes = []; |
||
| 78 | |||
| 79 | /** |
||
| 80 | * Attributes for the item text. |
||
| 81 | * |
||
| 82 | * @var array |
||
| 83 | */ |
||
| 84 | protected $labelAttributes = []; |
||
| 85 | |||
| 86 | /** |
||
| 87 | * Uri to use in the anchor tag. |
||
| 88 | * |
||
| 89 | * @var string |
||
| 90 | */ |
||
| 91 | protected $uri = null; |
||
| 92 | |||
| 93 | /** |
||
| 94 | * Attributes for the item. |
||
| 95 | * |
||
| 96 | * @var array |
||
| 97 | */ |
||
| 98 | protected $attributes = []; |
||
| 99 | |||
| 100 | /** |
||
| 101 | * Extra stuff associated to the item. |
||
| 102 | * |
||
| 103 | * @var array |
||
| 104 | */ |
||
| 105 | protected $extras = []; |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Whether the item is displayed. |
||
| 109 | * |
||
| 110 | * @var bool |
||
| 111 | */ |
||
| 112 | protected $display = true; |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Whether the children of the item are displayed. |
||
| 116 | * |
||
| 117 | * @var bool |
||
| 118 | */ |
||
| 119 | protected $displayChildren = true; |
||
| 120 | |||
| 121 | /** |
||
| 122 | * Child items. |
||
| 123 | * |
||
| 124 | * @var Collection|ItemInterface[] |
||
| 125 | */ |
||
| 126 | protected $children = []; |
||
| 127 | |||
| 128 | /** |
||
| 129 | * Parent item. |
||
| 130 | * |
||
| 131 | * @var ItemInterface|null |
||
| 132 | */ |
||
| 133 | protected $parent = null; |
||
| 134 | |||
| 135 | /** |
||
| 136 | * whether the item is current. null means unknown. |
||
| 137 | * |
||
| 138 | * @var bool|null |
||
| 139 | */ |
||
| 140 | protected $isCurrent = null; |
||
| 141 | |||
| 142 | /** |
||
| 143 | * @var int |
||
| 144 | */ |
||
| 145 | protected $position; |
||
| 146 | |||
| 147 | /** |
||
| 148 | * MenuItem constructor. |
||
| 149 | */ |
||
| 150 | 17 | public function __construct() |
|
| 151 | { |
||
| 152 | 17 | $this->setChildren([]); |
|
| 153 | 17 | } |
|
| 154 | |||
| 155 | /** |
||
| 156 | * {@inheritdoc} |
||
| 157 | */ |
||
| 158 | 16 | public function getId() |
|
| 159 | { |
||
| 160 | 16 | return $this->id; |
|
| 161 | } |
||
| 162 | |||
| 163 | /** |
||
| 164 | * {@inheritdoc} |
||
| 165 | */ |
||
| 166 | 10 | public function getRoot() |
|
| 167 | { |
||
| 168 | 10 | return $this->root; |
|
| 169 | } |
||
| 170 | |||
| 171 | /** |
||
| 172 | * {@inheritdoc} |
||
| 173 | */ |
||
| 174 | public function getLeft(): int |
||
| 175 | { |
||
| 176 | return $this->lft; |
||
| 177 | } |
||
| 178 | |||
| 179 | /** |
||
| 180 | * {@inheritdoc} |
||
| 181 | */ |
||
| 182 | public function setLeft(int $left) |
||
| 183 | { |
||
| 184 | $this->lft = $left; |
||
| 185 | } |
||
| 186 | |||
| 187 | /** |
||
| 188 | * {@inheritdoc} |
||
| 189 | */ |
||
| 190 | public function getRight(): int |
||
| 191 | { |
||
| 192 | return $this->rgt; |
||
| 193 | } |
||
| 194 | |||
| 195 | /** |
||
| 196 | * @param int $right |
||
| 197 | */ |
||
| 198 | public function setRight(int $right) |
||
| 199 | { |
||
| 200 | $this->rgt = $right; |
||
| 201 | } |
||
| 202 | |||
| 203 | /** |
||
| 204 | * {@inheritdoc} |
||
| 205 | */ |
||
| 206 | public function getLevel(): int |
||
| 207 | { |
||
| 208 | return $this->level; |
||
| 209 | } |
||
| 210 | |||
| 211 | /** |
||
| 212 | * {@inheritdoc} |
||
| 213 | */ |
||
| 214 | public function setLevel(int $level) |
||
| 215 | { |
||
| 216 | $this->level = $level; |
||
| 217 | } |
||
| 218 | |||
| 219 | /** |
||
| 220 | * {@inheritdoc} |
||
| 221 | */ |
||
| 222 | public function setFactory(FactoryInterface $factory) |
||
| 223 | { |
||
| 224 | } |
||
| 225 | |||
| 226 | /** |
||
| 227 | * {@inheritdoc} |
||
| 228 | */ |
||
| 229 | 14 | public function getName() |
|
| 233 | |||
| 234 | /** |
||
| 235 | * {@inheritdoc} |
||
| 236 | */ |
||
| 237 | 17 | public function setName($name) |
|
| 238 | { |
||
| 239 | 17 | $this->name = $name; |
|
| 240 | |||
| 241 | 17 | return $this; |
|
| 242 | } |
||
| 243 | |||
| 244 | /** |
||
| 245 | * {@inheritdoc} |
||
| 246 | */ |
||
| 247 | 14 | public function getUri() |
|
| 251 | |||
| 252 | /** |
||
| 253 | * {@inheritdoc} |
||
| 254 | */ |
||
| 255 | 17 | public function setUri($uri) |
|
| 261 | |||
| 262 | /** |
||
| 263 | * {@inheritdoc} |
||
| 264 | */ |
||
| 265 | 14 | public function getLabel() |
|
| 266 | { |
||
| 267 | 14 | return $this->label; |
|
| 268 | } |
||
| 269 | |||
| 270 | /** |
||
| 271 | * {@inheritdoc} |
||
| 272 | */ |
||
| 273 | 17 | public function setLabel($label) |
|
| 274 | { |
||
| 275 | 17 | $this->label = $label; |
|
| 276 | |||
| 277 | 17 | return $this; |
|
| 278 | } |
||
| 279 | |||
| 280 | /** |
||
| 281 | * {@inheritdoc} |
||
| 282 | */ |
||
| 283 | public function getAttributes() |
||
| 284 | { |
||
| 285 | return $this->attributes; |
||
| 286 | } |
||
| 287 | |||
| 288 | /** |
||
| 289 | * {@inheritdoc} |
||
| 290 | */ |
||
| 291 | 17 | public function setAttributes(array $attributes) |
|
| 292 | { |
||
| 293 | 17 | $this->attributes = $attributes; |
|
| 294 | |||
| 295 | 17 | return $this; |
|
| 296 | } |
||
| 297 | |||
| 298 | /** |
||
| 299 | * {@inheritdoc} |
||
| 300 | */ |
||
| 301 | public function getAttribute($name, $default = null) |
||
| 302 | { |
||
| 303 | if (isset($this->attributes[$name])) { |
||
| 304 | return $this->attributes[$name]; |
||
| 305 | } |
||
| 306 | |||
| 307 | return $default; |
||
| 308 | } |
||
| 309 | |||
| 310 | /** |
||
| 311 | * {@inheritdoc} |
||
| 312 | */ |
||
| 313 | public function setAttribute($name, $value) |
||
| 314 | { |
||
| 315 | $this->attributes[$name] = $value; |
||
| 316 | |||
| 317 | return $this; |
||
| 318 | } |
||
| 319 | |||
| 320 | /** |
||
| 321 | * {@inheritdoc} |
||
| 322 | */ |
||
| 323 | public function getLinkAttributes() |
||
| 324 | { |
||
| 325 | return $this->linkAttributes; |
||
| 326 | } |
||
| 327 | |||
| 328 | /** |
||
| 329 | * {@inheritdoc} |
||
| 330 | */ |
||
| 331 | 17 | public function setLinkAttributes(array $linkAttributes) |
|
| 332 | { |
||
| 333 | 17 | $this->linkAttributes = $linkAttributes; |
|
| 334 | |||
| 335 | 17 | return $this; |
|
| 336 | } |
||
| 337 | |||
| 338 | /** |
||
| 339 | * {@inheritdoc} |
||
| 340 | */ |
||
| 341 | public function getLinkAttribute($name, $default = null) |
||
| 342 | { |
||
| 343 | if (isset($this->linkAttributes[$name])) { |
||
| 344 | return $this->linkAttributes[$name]; |
||
| 345 | } |
||
| 346 | |||
| 347 | return $default; |
||
| 348 | } |
||
| 349 | |||
| 350 | /** |
||
| 351 | * {@inheritdoc} |
||
| 352 | */ |
||
| 353 | public function setLinkAttribute($name, $value) |
||
| 354 | { |
||
| 355 | $this->linkAttributes[$name] = $value; |
||
| 356 | |||
| 357 | return $this; |
||
| 358 | } |
||
| 359 | |||
| 360 | /** |
||
| 361 | * {@inheritdoc} |
||
| 362 | */ |
||
| 363 | public function getChildrenAttributes() |
||
| 364 | { |
||
| 365 | return $this->childrenAttributes; |
||
| 366 | } |
||
| 367 | |||
| 368 | /** |
||
| 369 | * {@inheritdoc} |
||
| 370 | */ |
||
| 371 | 17 | public function setChildrenAttributes(array $childrenAttributes) |
|
| 372 | { |
||
| 373 | 17 | $this->childrenAttributes = $childrenAttributes; |
|
| 374 | |||
| 375 | 17 | return $this; |
|
| 376 | } |
||
| 377 | |||
| 378 | /** |
||
| 379 | * {@inheritdoc} |
||
| 380 | */ |
||
| 381 | public function getChildrenAttribute($name, $default = null) |
||
| 382 | { |
||
| 383 | if (isset($this->childrenAttributes[$name])) { |
||
| 384 | return $this->childrenAttributes[$name]; |
||
| 385 | } |
||
| 386 | |||
| 387 | return $default; |
||
| 388 | } |
||
| 389 | |||
| 390 | /** |
||
| 391 | * {@inheritdoc} |
||
| 392 | */ |
||
| 393 | public function setChildrenAttribute($name, $value) |
||
| 394 | { |
||
| 395 | $this->childrenAttributes[$name] = $value; |
||
| 396 | |||
| 397 | return $this; |
||
| 398 | } |
||
| 399 | |||
| 400 | /** |
||
| 401 | * {@inheritdoc} |
||
| 402 | */ |
||
| 403 | public function getLabelAttributes() |
||
| 404 | { |
||
| 405 | return $this->labelAttributes; |
||
| 406 | } |
||
| 407 | |||
| 408 | /** |
||
| 409 | * {@inheritdoc} |
||
| 410 | */ |
||
| 411 | 17 | public function setLabelAttributes(array $labelAttributes) |
|
| 412 | { |
||
| 413 | 17 | $this->labelAttributes = $labelAttributes; |
|
| 414 | |||
| 415 | 17 | return $this; |
|
| 416 | } |
||
| 417 | |||
| 418 | /** |
||
| 419 | * {@inheritdoc} |
||
| 420 | */ |
||
| 421 | public function getLabelAttribute($name, $default = null) |
||
| 422 | { |
||
| 423 | if (isset($this->labelAttributes[$name])) { |
||
| 424 | return $this->labelAttributes[$name]; |
||
| 425 | } |
||
| 426 | |||
| 427 | return $default; |
||
| 428 | } |
||
| 429 | |||
| 430 | /** |
||
| 431 | * {@inheritdoc} |
||
| 432 | */ |
||
| 433 | public function setLabelAttribute($name, $value) |
||
| 439 | |||
| 440 | /** |
||
| 441 | * {@inheritdoc} |
||
| 442 | */ |
||
| 443 | public function getExtras() |
||
| 447 | |||
| 448 | /** |
||
| 449 | * {@inheritdoc} |
||
| 450 | */ |
||
| 451 | public function setExtras(array $extras) |
||
| 452 | { |
||
| 453 | $this->extras = $extras; |
||
| 454 | |||
| 455 | return $this; |
||
| 456 | } |
||
| 457 | |||
| 458 | /** |
||
| 459 | * {@inheritdoc} |
||
| 460 | */ |
||
| 461 | public function getExtra($name, $default = null) |
||
| 462 | { |
||
| 463 | if (isset($this->extras[$name])) { |
||
| 464 | return $this->extras[$name]; |
||
| 465 | } |
||
| 466 | |||
| 467 | return $default; |
||
| 468 | } |
||
| 469 | |||
| 470 | /** |
||
| 471 | * {@inheritdoc} |
||
| 472 | */ |
||
| 473 | public function setExtra($name, $value) |
||
| 474 | { |
||
| 475 | $this->extras[$name] = $value; |
||
| 476 | |||
| 477 | return $this; |
||
| 478 | } |
||
| 479 | |||
| 480 | /** |
||
| 481 | * {@inheritdoc} |
||
| 482 | */ |
||
| 483 | public function getDisplayChildren() |
||
| 484 | { |
||
| 485 | return $this->displayChildren; |
||
| 486 | } |
||
| 487 | |||
| 488 | /** |
||
| 489 | * {@inheritdoc} |
||
| 490 | */ |
||
| 491 | 17 | public function setDisplayChildren($bool) |
|
| 492 | { |
||
| 493 | 17 | $this->displayChildren = (bool) $bool; |
|
| 494 | |||
| 495 | 17 | return $this; |
|
| 496 | } |
||
| 497 | |||
| 498 | /** |
||
| 499 | * {@inheritdoc} |
||
| 500 | */ |
||
| 501 | public function isDisplayed() |
||
| 502 | { |
||
| 503 | return $this->display; |
||
| 504 | } |
||
| 505 | |||
| 506 | /** |
||
| 507 | * {@inheritdoc} |
||
| 508 | */ |
||
| 509 | 17 | public function setDisplay($bool) |
|
| 510 | { |
||
| 511 | 17 | $this->display = (bool) $bool; |
|
| 512 | |||
| 513 | 17 | return $this; |
|
| 514 | } |
||
| 515 | |||
| 516 | /** |
||
| 517 | * {@inheritdoc} |
||
| 518 | */ |
||
| 519 | public function hasChild(ItemInterface $menuItem): bool |
||
| 523 | |||
| 524 | /** |
||
| 525 | * {@inheritdoc} |
||
| 526 | */ |
||
| 527 | public function addChild($child, array $options = array()) |
||
| 528 | { |
||
| 529 | if (!$this->hasChild($child)) { |
||
|
|
|||
| 530 | $child->setParent($this); |
||
| 531 | $this->children->set($child->getName(), $child); |
||
| 532 | } |
||
| 533 | |||
| 534 | return $child; |
||
| 535 | } |
||
| 536 | |||
| 537 | /** |
||
| 538 | * {@inheritdoc} |
||
| 539 | */ |
||
| 540 | public function getChild($name) |
||
| 541 | { |
||
| 542 | return $this->children->get($name); |
||
| 543 | } |
||
| 544 | |||
| 545 | /** |
||
| 546 | * {@inheritdoc} |
||
| 547 | */ |
||
| 548 | public function reorderChildren($order) |
||
| 549 | { |
||
| 550 | if (count($order) != $this->count()) { |
||
| 551 | throw new \InvalidArgumentException('Cannot reorder children, order does not contain all children.'); |
||
| 552 | } |
||
| 553 | |||
| 554 | $newChildren = array(); |
||
| 555 | foreach ($order as $name) { |
||
| 556 | if (!$this->children->containsKey($name)) { |
||
| 557 | throw new \InvalidArgumentException('Cannot find children named '.$name); |
||
| 558 | } |
||
| 559 | $child |
||
| 560 | = $this->getChild($name); |
||
| 561 | $newChildren[$name] = $child; |
||
| 562 | } |
||
| 563 | |||
| 564 | $this->setChildren($newChildren); |
||
| 565 | |||
| 566 | return $this; |
||
| 567 | } |
||
| 568 | |||
| 569 | /** |
||
| 570 | * {@inheritdoc} |
||
| 571 | */ |
||
| 572 | public function copy() |
||
| 573 | { |
||
| 574 | $newMenu = clone $this; |
||
| 575 | $newMenu->children = new ArrayCollection(); |
||
| 576 | $newMenu->setParent(null); |
||
| 577 | foreach ($this->getChildren() as $child) { |
||
| 578 | $newMenu->addChild($child->copy()); |
||
| 579 | } |
||
| 580 | |||
| 581 | return $newMenu; |
||
| 582 | } |
||
| 583 | |||
| 584 | /** |
||
| 585 | * {@inheritdoc} |
||
| 586 | */ |
||
| 587 | 15 | public function isRoot() |
|
| 591 | |||
| 592 | /** |
||
| 593 | * {@inheritdoc} |
||
| 594 | */ |
||
| 595 | 16 | public function getParent() |
|
| 596 | { |
||
| 597 | 16 | return $this->parent; |
|
| 598 | } |
||
| 599 | |||
| 600 | /** |
||
| 601 | * {@inheritdoc} |
||
| 602 | */ |
||
| 603 | 10 | public function setParent(ItemInterface $parent = null) |
|
| 604 | { |
||
| 605 | 10 | if ($parent === $this) { |
|
| 606 | throw new \InvalidArgumentException('Item cannot be a child of itself'); |
||
| 607 | } |
||
| 608 | |||
| 609 | 10 | $this->parent = $parent; |
|
| 610 | |||
| 611 | 10 | return $this; |
|
| 612 | } |
||
| 613 | |||
| 614 | /** |
||
| 615 | * {@inheritdoc} |
||
| 616 | */ |
||
| 617 | public function getChildren() |
||
| 618 | { |
||
| 619 | if (is_array($this->children)) { |
||
| 620 | return $this->children; |
||
| 621 | } |
||
| 622 | |||
| 623 | return $this->children->toArray(); |
||
| 624 | } |
||
| 625 | |||
| 626 | /** |
||
| 627 | * {@inheritdoc} |
||
| 628 | */ |
||
| 629 | 17 | public function setChildren(array $children) |
|
| 630 | { |
||
| 631 | 17 | $this->children = new ArrayCollection($children); |
|
| 632 | |||
| 633 | 17 | return $this; |
|
| 635 | |||
| 636 | /** |
||
| 637 | * {@inheritdoc} |
||
| 638 | */ |
||
| 639 | public function removeChild($name) |
||
| 651 | |||
| 652 | /** |
||
| 653 | * {@inheritdoc} |
||
| 654 | */ |
||
| 655 | public function getFirstChild() |
||
| 659 | |||
| 660 | /** |
||
| 661 | * {@inheritdoc} |
||
| 662 | */ |
||
| 663 | public function getLastChild() |
||
| 667 | |||
| 668 | /** |
||
| 669 | * {@inheritdoc} |
||
| 670 | */ |
||
| 671 | public function hasChildren() |
||
| 681 | |||
| 682 | /** |
||
| 683 | * {@inheritdoc} |
||
| 684 | */ |
||
| 685 | 17 | public function setCurrent($bool) |
|
| 691 | |||
| 692 | /** |
||
| 693 | * {@inheritdoc} |
||
| 694 | */ |
||
| 695 | public function isCurrent() |
||
| 699 | |||
| 700 | /** |
||
| 701 | * {@inheritdoc} |
||
| 702 | */ |
||
| 703 | public function isLast() |
||
| 712 | |||
| 713 | /** |
||
| 714 | * {@inheritdoc} |
||
| 715 | */ |
||
| 716 | public function isFirst() |
||
| 725 | |||
| 726 | /** |
||
| 727 | * {@inheritdoc} |
||
| 728 | */ |
||
| 729 | View Code Duplication | public function actsLikeFirst() |
|
| 756 | |||
| 757 | /** |
||
| 758 | * {@inheritdoc} |
||
| 759 | */ |
||
| 760 | View Code Duplication | public function actsLikeLast() |
|
| 788 | |||
| 789 | /** |
||
| 790 | * {@inheritdoc} |
||
| 791 | */ |
||
| 792 | public function count() |
||
| 796 | |||
| 797 | /** |
||
| 798 | * {@inheritdoc} |
||
| 799 | */ |
||
| 800 | 14 | public function getIterator() |
|
| 804 | |||
| 805 | /** |
||
| 806 | * {@inheritdoc} |
||
| 807 | */ |
||
| 808 | public function offsetExists($name) |
||
| 812 | |||
| 813 | /** |
||
| 814 | * {@inheritdoc} |
||
| 815 | */ |
||
| 816 | public function offsetGet($name) |
||
| 820 | |||
| 821 | /** |
||
| 822 | * {@inheritdoc} |
||
| 823 | */ |
||
| 824 | public function offsetSet($name, $value) |
||
| 828 | |||
| 829 | /** |
||
| 830 | * {@inheritdoc} |
||
| 831 | */ |
||
| 832 | public function offsetUnset($name) |
||
| 836 | |||
| 837 | /** |
||
| 838 | * {@inheritdoc} |
||
| 839 | */ |
||
| 840 | 5 | public function getPosition(): int |
|
| 844 | |||
| 845 | /** |
||
| 846 | * {@inheritdoc} |
||
| 847 | */ |
||
| 848 | 3 | public function setPosition(int $position) |
|
| 852 | |||
| 853 | /** |
||
| 854 | * {@inheritdoc} |
||
| 855 | */ |
||
| 856 | public function getParentId() |
||
| 863 | |||
| 864 | /** |
||
| 865 | * {@inheritdoc} |
||
| 866 | */ |
||
| 867 | public function getRootId() |
||
| 873 | } |
||
| 874 |
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.