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 AudioPlayer 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 AudioPlayer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
9 | class AudioPlayer |
||
10 | { |
||
11 | const SIZE_INVISIBLE = 0; |
||
12 | const SIZE_TINY = 1; |
||
13 | const SIZE_SMALL = 2; |
||
14 | const SIZE_MEDIUM = 3; |
||
15 | const SIZE_LARGE = 4; |
||
16 | |||
17 | const THEME_LIGHT = 'light'; |
||
18 | const THEME_DARK = 'dark'; |
||
19 | |||
20 | const TYPE_VIDEO = 1; |
||
21 | const TYPE_PLAYLIST = 2; |
||
22 | |||
23 | protected $id = null; |
||
24 | protected $https = true; |
||
25 | protected $type = self::TYPE_VIDEO; |
||
26 | protected $size = self::SIZE_SMALL; |
||
27 | protected $source = null; |
||
28 | protected $hd = false; |
||
29 | protected $autoplay = false; |
||
30 | protected $jsapi = false; |
||
31 | protected $progressbar = false; |
||
32 | protected $timecode = false; |
||
33 | protected $cookies = true; |
||
34 | protected $theme = self::THEME_DARK; |
||
35 | protected $loop = false; |
||
36 | |||
37 | /** |
||
38 | * Constructor. |
||
39 | * |
||
40 | * |
||
41 | * @param string $source |
||
42 | * @param array $settings |
||
43 | * |
||
44 | * @throws AudioPlayerException |
||
45 | */ |
||
46 | 12 | public function __construct($source, $settings = null) |
|
47 | { |
||
48 | 12 | $this->source($source); |
|
49 | |||
50 | 12 | if ($settings === null) { |
|
51 | 12 | $settings = []; |
|
52 | 12 | } |
|
53 | |||
54 | // Feature array, ie: array('https','hd','autoplay') |
||
|
|||
55 | 12 | if (in_array('https', $settings)) { |
|
56 | $this->https(); |
||
57 | } |
||
58 | 12 | if (in_array('hd', $settings)) { |
|
59 | $this->hd(); |
||
60 | } |
||
61 | 12 | if (in_array('autoplay', $settings)) { |
|
62 | $this->autoplay(); |
||
63 | } |
||
64 | 12 | if (in_array('jsapi', $settings)) { |
|
65 | $this->jsAPI(); |
||
66 | } |
||
67 | 12 | if (in_array('progressbar', $settings)) { |
|
68 | $this->progressBar(); |
||
69 | } |
||
70 | 12 | if (in_array('timecode', $settings)) { |
|
71 | $this->timeCode(); |
||
72 | } |
||
73 | 12 | if (in_array('cookies', $settings)) { |
|
74 | $this->cookies(); |
||
75 | } |
||
76 | 12 | if (in_array('loop', $settings)) { |
|
77 | $this->loop(); |
||
78 | } |
||
79 | |||
80 | // Associative Feature Array, ie: array('https' => true, 'hd' => false) |
||
81 | 12 | if (isset($settings['https'])) { |
|
82 | $this->https($settings['https']); |
||
83 | } |
||
84 | 12 | if (isset($settings['size'])) { |
|
85 | $this->size($settings['size']); |
||
86 | } |
||
87 | 12 | if (isset($settings['hd'])) { |
|
88 | $this->hd($settings['hd']); |
||
89 | } |
||
90 | 12 | if (isset($settings['autoplay'])) { |
|
91 | $this->autoplay($settings['autoplay']); |
||
92 | } |
||
93 | 12 | if (isset($settings['jsapi'])) { |
|
94 | $this->jsAPI($settings['jsapi']); |
||
95 | } |
||
96 | 12 | if (isset($settings['progressbar'])) { |
|
97 | $this->progressBar($settings['progressbar']); |
||
98 | } |
||
99 | 12 | if (isset($settings['timecode'])) { |
|
100 | $this->timeCode($settings['timecode']); |
||
101 | } |
||
102 | 12 | if (isset($settings['cookies'])) { |
|
103 | $this->cookies($settings['cookies']); |
||
104 | } |
||
105 | 12 | if (isset($settings['theme'])) { |
|
106 | $this->theme($settings['theme']); |
||
107 | } |
||
108 | 12 | if (isset($settings['loop'])) { |
|
109 | $this->loop($settings['loop']); |
||
110 | } |
||
111 | 12 | } |
|
112 | |||
113 | /** |
||
114 | * Factory |
||
115 | * Allows easy creation and daisy-chaining of a YTAudio object. |
||
116 | * |
||
117 | * @see __construct |
||
118 | * |
||
119 | * @param string $source |
||
120 | * @param array $settings |
||
121 | * |
||
122 | * @throws AudioPlayerException |
||
123 | * |
||
124 | * @return static |
||
125 | */ |
||
126 | public static function create($source, $settings = null) |
||
130 | |||
131 | /** |
||
132 | * Set Player Video/Playlist |
||
133 | * Does not validate whether or not YouTube video/playlist exists. |
||
134 | * |
||
135 | * |
||
136 | * @param string $source Can be a URL or just the ID |
||
137 | * |
||
138 | * @throws AudioPlayerException |
||
139 | * |
||
140 | * @return $this |
||
141 | */ |
||
142 | 12 | public function source($source) |
|
171 | |||
172 | /** |
||
173 | * Set Player Video |
||
174 | * Does not validate whether or not YouTube video exists. |
||
175 | * |
||
176 | * |
||
177 | * @param string $video Can be a URL or just the ID |
||
178 | * |
||
179 | * @throws AudioPlayerException |
||
180 | * |
||
181 | * @return $this |
||
182 | */ |
||
183 | 12 | public function video($video) |
|
232 | |||
233 | /** |
||
234 | * Set Player Playlist |
||
235 | * Does not validate whether or not YouTube playlist exists. |
||
236 | * |
||
237 | * |
||
238 | * @param string $playlist Can be a URL or just the ID |
||
239 | * |
||
240 | * @throws AudioPlayerException |
||
241 | * |
||
242 | * @return $this |
||
243 | */ |
||
244 | public function playlist($playlist) |
||
276 | |||
277 | /** |
||
278 | * Get Source |
||
279 | * Returns the source exactly as you fed it to us. |
||
280 | * The source is only changed if setting it was successful. |
||
281 | * |
||
282 | * @return string |
||
283 | */ |
||
284 | public function getSource() |
||
288 | |||
289 | /** |
||
290 | * Get Player Type. |
||
291 | * |
||
292 | * @return int |
||
293 | */ |
||
294 | 4 | public function getType() |
|
298 | |||
299 | public function isVideo() |
||
303 | |||
304 | 3 | public function isPlaylist() |
|
308 | |||
309 | /** |
||
310 | * Get Player ID. |
||
311 | * |
||
312 | * @return string |
||
313 | */ |
||
314 | public function getID() |
||
318 | |||
319 | /** |
||
320 | * Set Player Size. |
||
321 | * |
||
322 | * |
||
323 | * @param int $size [SIZE_INVISIBLE | SIZE_TINY | SIZE_SMALL | SIZE_MEDIUM | SIZE_LARGE] |
||
324 | * |
||
325 | * @throws AudioPlayerException |
||
326 | * |
||
327 | * @return $this |
||
328 | */ |
||
329 | 1 | public function size($size) |
|
346 | |||
347 | 1 | protected function isValidSize($size) |
|
354 | |||
355 | /** |
||
356 | * Get Player Size. |
||
357 | * |
||
358 | * @return int |
||
359 | */ |
||
360 | 3 | public function getSize() |
|
364 | |||
365 | 1 | public function isTiny() |
|
369 | |||
370 | 1 | public function isSmall() |
|
374 | |||
375 | 1 | public function isMedium() |
|
379 | |||
380 | 1 | public function isLarge() |
|
384 | |||
385 | /** |
||
386 | * Set Player Invisible |
||
387 | * Convenience function, since Invisibility is a SIZE. |
||
388 | * |
||
389 | * @return $this |
||
390 | */ |
||
391 | 1 | public function invisible() |
|
392 | { |
||
393 | 1 | $this->size(static::SIZE_INVISIBLE); |
|
394 | 1 | } |
|
395 | |||
396 | /** |
||
397 | * Get Player Invisibility Setting. |
||
398 | * |
||
399 | * @return bool |
||
400 | */ |
||
401 | 1 | public function getInvisible() |
|
405 | |||
406 | 1 | public function isInvisible() |
|
410 | |||
411 | /** |
||
412 | * Set Player Theme. |
||
413 | * |
||
414 | * |
||
415 | * @param int $theme [THEME_LIGHT | THEME_DARK] |
||
416 | * |
||
417 | * @throws AudioPlayerException |
||
418 | * |
||
419 | * @return $this |
||
420 | */ |
||
421 | 3 | public function theme($theme) |
|
433 | |||
434 | /** |
||
435 | * Get Player Theme. |
||
436 | * |
||
437 | * @return bool |
||
438 | */ |
||
439 | 2 | public function getTheme() |
|
443 | |||
444 | /** |
||
445 | * Set HD |
||
446 | * Choose whether or not to force the player into HD. |
||
447 | * |
||
448 | * @param bool $useHD |
||
449 | * |
||
450 | * @return $this |
||
451 | */ |
||
452 | 1 | public function hd($useHD = true) |
|
462 | |||
463 | /** |
||
464 | * Get HD Setting. |
||
465 | * |
||
466 | * @return bool |
||
467 | */ |
||
468 | 1 | public function getHD() |
|
472 | |||
473 | 1 | public function isHD() |
|
477 | |||
478 | /** |
||
479 | * Set Autoplay |
||
480 | * Choose whether or not to automatically play the video when it loads |
||
481 | * Please don't use this. You'll make me sad. |
||
482 | * |
||
483 | * @param bool $autoplay |
||
484 | * |
||
485 | * @return $this |
||
486 | */ |
||
487 | 1 | public function autoplay($autoplay = true) |
|
497 | |||
498 | /** |
||
499 | * Get Autoplay Setting. |
||
500 | * |
||
501 | * @return bool |
||
502 | */ |
||
503 | 1 | public function getAutoplay() |
|
507 | |||
508 | 1 | public function willAutoplay() |
|
512 | |||
513 | /** |
||
514 | * Set JSApi |
||
515 | * Choose whether or not to allow access via the YouTube JavaScript API. |
||
516 | * |
||
517 | * @param bool $useJSAPI |
||
518 | * |
||
519 | * @return $this |
||
520 | */ |
||
521 | 1 | public function jsAPI($useJSAPI = true) |
|
531 | |||
532 | /** |
||
533 | * Get JavaScript API Setting. |
||
534 | * |
||
535 | * @return bool |
||
536 | */ |
||
537 | 1 | public function getJSAPI() |
|
541 | |||
542 | 1 | public function canUseJSAPI() |
|
546 | |||
547 | 1 | public function canUseJavaScriptAPI() |
|
551 | |||
552 | /** |
||
553 | * Set Loop |
||
554 | * Choose whether or not to loop once the video/playlist is over. |
||
555 | * |
||
556 | * @param bool $loop |
||
557 | * |
||
558 | * @return $this |
||
559 | */ |
||
560 | 1 | public function loop($loop = true) |
|
570 | |||
571 | /** |
||
572 | * Get Loop Setting. |
||
573 | * |
||
574 | * @return bool |
||
575 | */ |
||
576 | 1 | public function getLoop() |
|
580 | |||
581 | 1 | public function willLoop() |
|
585 | |||
586 | /** |
||
587 | * Set Progress Bar |
||
588 | * Choose whether or not to display the progress bar. |
||
589 | * |
||
590 | * @param bool $useProgressBar |
||
591 | * |
||
592 | * @return $this |
||
593 | */ |
||
594 | 3 | View Code Duplication | public function progressBar($useProgressBar = true) |
610 | |||
611 | /** |
||
612 | * Get Progress Bar Setting. |
||
613 | * |
||
614 | * @return bool |
||
615 | */ |
||
616 | 1 | public function getProgressBar() |
|
620 | |||
621 | 1 | public function hasProgressBar() |
|
625 | |||
626 | /** |
||
627 | * Set Time Code |
||
628 | * Choose whether or not to display the time code. Requires Progress Bar. |
||
629 | * |
||
630 | * @param bool $useTimeCode |
||
631 | * |
||
632 | * @return $this |
||
633 | */ |
||
634 | 2 | View Code Duplication | public function timeCode($useTimeCode = true) |
652 | |||
653 | /** |
||
654 | * Get Time Code Setting. |
||
655 | * |
||
656 | * @return bool |
||
657 | */ |
||
658 | 1 | public function getTimeCode() |
|
662 | |||
663 | 1 | public function hasTimeCode() |
|
667 | |||
668 | /** |
||
669 | * Set Cookies |
||
670 | * Choose whether or not to allow YouTube to collect cookies. |
||
671 | * |
||
672 | * @param bool $useCookies |
||
673 | * |
||
674 | * @throws AudioPlayerException |
||
675 | * |
||
676 | * @return $this |
||
677 | */ |
||
678 | 1 | public function cookies($useCookies = true) |
|
692 | |||
693 | /** |
||
694 | * Get Cookie Setting. |
||
695 | * |
||
696 | * @return bool |
||
697 | */ |
||
698 | 1 | public function getCookies() |
|
702 | |||
703 | 1 | public function willUseCookies() |
|
707 | |||
708 | /** |
||
709 | * Set HTTPS |
||
710 | * Choose whether to use HTTPs or HTTP. |
||
711 | * |
||
712 | * @param bool $useHTTPS |
||
713 | * |
||
714 | * @return $this |
||
715 | */ |
||
716 | 1 | public function https($useHTTPS = true) |
|
726 | |||
727 | /** |
||
728 | * Get HTTPS Setting. |
||
729 | * |
||
730 | * @return bool |
||
731 | */ |
||
732 | 1 | public function getHTTPS() |
|
736 | |||
737 | 1 | public function isHTTPS() |
|
741 | |||
742 | 1 | public function isHTTP() |
|
746 | |||
747 | /** |
||
748 | * Get Height (px). |
||
749 | * |
||
750 | * @return int |
||
751 | */ |
||
752 | public function getHeight() |
||
760 | |||
761 | /** |
||
762 | * Get Width (px). |
||
763 | * |
||
764 | * @return int |
||
765 | */ |
||
766 | public function getWidth() |
||
789 | |||
790 | /** |
||
791 | * Get Embed URL. |
||
792 | * |
||
793 | * @param bool $encode |
||
794 | * |
||
795 | * @return string |
||
796 | */ |
||
797 | public function getEmbedURL($encode = true) |
||
851 | |||
852 | /** |
||
853 | * Render valid XHTML. |
||
854 | * |
||
855 | * @param bool $return Return the HTML instead of echoing it. |
||
856 | * |
||
857 | * @return string|bool |
||
858 | */ |
||
859 | public function render($return = false) |
||
881 | } |
||
882 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.