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 BrowserDetector 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 BrowserDetector, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
5 | class BrowserDetector implements DetectorInterface |
||
6 | { |
||
7 | const FUNC_PREFIX = 'checkBrowser'; |
||
8 | |||
9 | protected static $userAgentString; |
||
10 | |||
11 | /** |
||
12 | * @var Browser |
||
13 | */ |
||
14 | protected static $browser; |
||
15 | |||
16 | protected static $browsersList = array( |
||
17 | // well-known, well-used |
||
18 | // Special Notes: |
||
19 | // (1) Opera must be checked before FireFox due to the odd |
||
20 | // user agents used in some older versions of Opera |
||
21 | // (2) WebTV is strapped onto Internet Explorer so we must |
||
22 | // check for WebTV before IE |
||
23 | // (3) Because of Internet Explorer 11 using |
||
24 | // "Mozilla/5.0 ([...] Trident/7.0; rv:11.0) like Gecko" |
||
25 | // as user agent, tests for IE must be run before any |
||
26 | // tests checking for "Mozilla" |
||
27 | // (4) (deprecated) Galeon is based on Firefox and needs to be |
||
28 | // tested before Firefox is tested |
||
29 | // (5) OmniWeb is based on Safari so OmniWeb check must occur |
||
30 | // before Safari |
||
31 | // (6) Netscape 9+ is based on Firefox so Netscape checks |
||
32 | // before FireFox are necessary |
||
33 | // (7) Microsoft Edge must be checked before Chrome and Safari |
||
34 | // (7) Vivaldi must be checked before Chrome |
||
35 | 'WebTv', |
||
36 | 'InternetExplorer', |
||
37 | 'Edge', |
||
38 | 'Opera', |
||
39 | 'Vivaldi', |
||
40 | 'Dragon', |
||
41 | 'Galeon', |
||
42 | 'NetscapeNavigator9Plus', |
||
43 | 'SeaMonkey', |
||
44 | 'Firefox', |
||
45 | 'Yandex', |
||
46 | 'Samsung', |
||
47 | 'Chrome', |
||
48 | 'OmniWeb', |
||
49 | 'UCBrowser', //before Android |
||
50 | // common mobile |
||
51 | 'Android', |
||
52 | 'BlackBerry', |
||
53 | 'Nokia', |
||
54 | 'Gsa', |
||
55 | // WebKit base check (post mobile and others) |
||
56 | 'AppleNews', |
||
57 | 'Safari', |
||
58 | // everyone else |
||
59 | 'NetPositive', |
||
60 | 'Firebird', |
||
61 | 'Konqueror', |
||
62 | 'Icab', |
||
63 | 'Phoenix', |
||
64 | 'Amaya', |
||
65 | 'Lynx', |
||
66 | 'NSPlayer', |
||
67 | 'Office', |
||
68 | 'Shiretoko', |
||
69 | 'IceCat', |
||
70 | 'Iceweasel', |
||
71 | 'Mozilla', /* Mozilla is such an open standard that you must check it last */ |
||
72 | ); |
||
73 | |||
74 | //https://en.wikipedia.org/wiki/Microsoft_Edge |
||
75 | protected static $edgeHTML = [ |
||
76 | "12.10049" => "0.10.10049", |
||
77 | "12.10051" => "0.11.10051", |
||
78 | "12.10052" => "0.11.10052", |
||
79 | "12.10061" => "0.11.10061", |
||
80 | "12.10074" => "0.11.10074", |
||
81 | "12.1008" => "0.11.10080", |
||
82 | 8 | "12.10122" => "13.10122", |
|
83 | "12.1013" => "15.1013", |
||
84 | 8 | "12.10136" => "16.10136", |
|
85 | 8 | "12.10149" => "19.10149", |
|
86 | "12.10158" => "20.10158", |
||
87 | "12.10159" => "20.10159", |
||
88 | 8 | "12.10162" => "20.10162", |
|
89 | "12.10166" => "20.10166", |
||
90 | 8 | "12.1024" => "20.1024", |
|
91 | 8 | "12.10512" => "20.10512", |
|
92 | "12.10514" => "20.10514", |
||
93 | 8 | "12.10525" => "20.10525", |
|
94 | 8 | "12.10532" => "20.10532", |
|
95 | "12.10536" => "20.10536", |
||
96 | 8 | "13.10547" => "21.10547", |
|
97 | 8 | "13.10549" => "21.10549", |
|
98 | "13.10565" => "23.10565", |
||
99 | 8 | "13.10572" => "25.10572", |
|
100 | 7 | "13.10576" => "25.10576", |
|
101 | "13.10581" => "25.10581", |
||
102 | 8 | "13.10586" => "25.10586", |
|
103 | "13.11082" => "25.11082", |
||
104 | 1 | "13.11099" => "27.11099", |
|
105 | "13.11102" => "28.11102", |
||
106 | "13.14251" => "28.14251", |
||
107 | "13.14257" => "28.14257", |
||
108 | "14.14267" => "31.14267", |
||
109 | "14.14271" => "31.14271", |
||
110 | "14.14279" => "31.14279", |
||
111 | "14.14283" => "31.14283", |
||
112 | 8 | "14.14291" => "34.14291", |
|
113 | "14.14295" => "34.14295", |
||
114 | 8 | "14.143" => "34.143", |
|
115 | "14.14316" => "37.14316", |
||
116 | "14.14322" => "37.14322", |
||
117 | "14.14327" => "37.14327", |
||
118 | "14.14328" => "37.14328", |
||
119 | "14.14332" => "37.14332", |
||
120 | 8 | "14.14342" => "38.14342", |
|
121 | "14.14352" => "38.14352", |
||
122 | "14.14393" => "38.14393", |
||
123 | "14.14901" => "39.14901", |
||
124 | "14.14905" => "39.14905", |
||
125 | "14.14915" => "39.14915", |
||
126 | "14.14926" => "39.14926", |
||
127 | "14.14931" => "39.14931", |
||
128 | 8 | "14.14936" => "39.14936", |
|
129 | "15.14942" => "39.14942", |
||
130 | 8 | "15.14946" => "39.14946", |
|
131 | 1 | "15.14951" => "39.14951", |
|
132 | "15.14955" => "39.14955", |
||
133 | 1 | "15.14959" => "39.14959", |
|
134 | "15.14965" => "39.14965", |
||
135 | "15.14971" => "39.14971", |
||
136 | 7 | "15.14977" => "39.14977", |
|
137 | "15.14986" => "39.14986" |
||
138 | ]; |
||
139 | |||
140 | /** |
||
141 | * Routine to determine the browser type. |
||
142 | * |
||
143 | * @param Browser $browser |
||
144 | 3 | * @param UserAgent $userAgent |
|
145 | * |
||
146 | 3 | * @return bool |
|
147 | 2 | */ |
|
148 | 1 | public static function detect(Browser $browser, UserAgent $userAgent = null) |
|
174 | |||
175 | /** |
||
176 | * Determine if the user is using Chrome Frame. |
||
177 | * |
||
178 | * @return bool |
||
179 | */ |
||
180 | public static function checkChromeFrame() |
||
190 | |||
191 | /** |
||
192 | 2 | * Determine if the browser is a wekit webview. |
|
193 | * |
||
194 | * @return bool |
||
195 | */ |
||
196 | public static function checkWebkit() |
||
206 | |||
207 | /** |
||
208 | * Determine if the user is using Facebook. |
||
209 | * |
||
210 | * @return bool |
||
211 | */ |
||
212 | public static function checkFacebookWebView() |
||
222 | |||
223 | /** |
||
224 | * Determine if the user is using Twitter. |
||
225 | * |
||
226 | 2 | * @return bool |
|
227 | 2 | */ |
|
228 | 2 | public static function checkTwitterWebView() |
|
238 | |||
239 | /** |
||
240 | * Determine if the user is using a BlackBerry. |
||
241 | * |
||
242 | * @return bool |
||
243 | */ |
||
244 | public static function checkBrowserBlackBerry() |
||
275 | 1 | ||
276 | 1 | /** |
|
277 | * Determine if the browser is Internet Explorer. |
||
278 | 1 | * |
|
279 | * @return bool |
||
280 | */ |
||
281 | public static function checkBrowserInternetExplorer() |
||
393 | |||
394 | 1 | /** |
|
395 | * Determine if the browser is Opera. |
||
396 | * |
||
397 | 4 | * @return bool |
|
398 | */ |
||
399 | public static function checkBrowserOpera() |
||
459 | 1 | ||
460 | 1 | /** |
|
461 | 1 | * Determine if the browser is Samsung. |
|
462 | 1 | * |
|
463 | 1 | * @return bool |
|
464 | */ |
||
465 | 1 | View Code Duplication | public static function checkBrowserSamsung() |
480 | |||
481 | /** |
||
482 | * Determine if the browser is Chrome. |
||
483 | * |
||
484 | * @return bool |
||
485 | */ |
||
486 | public static function checkBrowserChrome() |
||
510 | 8 | ||
511 | /** |
||
512 | * Determine if the browser is Vivaldi. |
||
513 | * |
||
514 | * @return bool |
||
515 | */ |
||
516 | View Code Duplication | public static function checkBrowserVivaldi() |
|
531 | 1 | ||
532 | /** |
||
533 | * Determine if the browser is Microsoft Edge. |
||
534 | * |
||
535 | * @return bool |
||
536 | */ |
||
537 | public static function checkBrowserEdge() |
||
564 | |||
565 | /** |
||
566 | * Determine if the browser is Google Search Appliance. |
||
567 | * |
||
568 | * @return bool |
||
569 | */ |
||
570 | View Code Duplication | public static function checkBrowserGsa() |
|
585 | |||
586 | /** |
||
587 | * Determine if the browser is WebTv. |
||
588 | * |
||
589 | * @return bool |
||
590 | */ |
||
591 | View Code Duplication | public static function checkBrowserWebTv() |
|
606 | |||
607 | /** |
||
608 | * Determine if the browser is NetPositive. |
||
609 | * |
||
610 | * @return bool |
||
611 | */ |
||
612 | 3 | public static function checkBrowserNetPositive() |
|
627 | |||
628 | /** |
||
629 | * Determine if the browser is Galeon. |
||
630 | * |
||
631 | * @return bool |
||
632 | 1 | */ |
|
633 | View Code Duplication | public static function checkBrowserGaleon() |
|
648 | |||
649 | /** |
||
650 | * Determine if the browser is Konqueror. |
||
651 | * |
||
652 | 1 | * @return bool |
|
653 | */ |
||
654 | View Code Duplication | public static function checkBrowserKonqueror() |
|
669 | |||
670 | /** |
||
671 | 6 | * Determine if the browser is iCab. |
|
672 | 5 | * |
|
673 | 6 | * @return bool |
|
674 | */ |
||
675 | View Code Duplication | public static function checkBrowserIcab() |
|
689 | |||
690 | 1 | /** |
|
691 | * Determine if the browser is OmniWeb. |
||
692 | 1 | * |
|
693 | * @return bool |
||
694 | 1 | */ |
|
695 | View Code Duplication | public static function checkBrowserOmniWeb() |
|
708 | |||
709 | /** |
||
710 | * Determine if the browser is Phoenix. |
||
711 | 1 | * |
|
712 | * @return bool |
||
713 | 1 | */ |
|
714 | public static function checkBrowserPhoenix() |
||
728 | |||
729 | /** |
||
730 | * Determine if the browser is Firebird. |
||
731 | * |
||
732 | 2 | * @return bool |
|
733 | */ |
||
734 | 2 | public static function checkBrowserFirebird() |
|
748 | |||
749 | /** |
||
750 | * Determine if the browser is Netscape Navigator 9+. |
||
751 | * |
||
752 | * @return bool |
||
753 | */ |
||
754 | public static function checkBrowserNetscapeNavigator9Plus() |
||
778 | |||
779 | /** |
||
780 | * Determine if the browser is Shiretoko. |
||
781 | 6 | * |
|
782 | * @return bool |
||
783 | 6 | */ |
|
784 | 5 | public static function checkBrowserShiretoko() |
|
799 | 5 | ||
800 | /** |
||
801 | * Determine if the browser is Ice Cat. |
||
802 | * |
||
803 | * @return bool |
||
804 | */ |
||
805 | public static function checkBrowserIceCat() |
||
820 | 1 | ||
821 | /** |
||
822 | * Determine if the browser is Nokia. |
||
823 | * |
||
824 | * @return bool |
||
825 | */ |
||
826 | public static function checkBrowserNokia() |
||
843 | 1 | ||
844 | /** |
||
845 | * Determine if the browser is Firefox. |
||
846 | * |
||
847 | * @return bool |
||
848 | */ |
||
849 | 1 | View Code Duplication | public static function checkBrowserFirefox() |
869 | 1 | ||
870 | /** |
||
871 | 1 | * Determine if the browser is SeaMonkey. |
|
872 | * |
||
873 | * @return bool |
||
874 | */ |
||
875 | View Code Duplication | public static function checkBrowserSeaMonkey() |
|
895 | |||
896 | /** |
||
897 | * Determine if the browser is Iceweasel. |
||
898 | * |
||
899 | * @return bool |
||
900 | */ |
||
901 | 1 | View Code Duplication | public static function checkBrowserIceweasel() |
916 | 2 | ||
917 | /** |
||
918 | * Determine if the browser is Mozilla. |
||
919 | * |
||
920 | * @return bool |
||
921 | */ |
||
922 | public static function checkBrowserMozilla() |
||
957 | |||
958 | /** |
||
959 | 4 | * Determine if the browser is Lynx. |
|
960 | * |
||
961 | * @return bool |
||
962 | */ |
||
963 | View Code Duplication | public static function checkBrowserLynx() |
|
976 | |||
977 | 1 | /** |
|
978 | * Determine if the browser is Amaya. |
||
979 | * |
||
980 | 6 | * @return bool |
|
981 | */ |
||
982 | View Code Duplication | public static function checkBrowserAmaya() |
|
997 | |||
998 | |||
999 | /** |
||
1000 | * Determine if the browser is Safari. |
||
1001 | * |
||
1002 | * @return bool |
||
1003 | */ |
||
1004 | 3 | public static function checkBrowserSafari() |
|
1021 | |||
1022 | /** |
||
1023 | * Determine if the browser is Yandex. |
||
1024 | * |
||
1025 | * @return bool |
||
1026 | */ |
||
1027 | View Code Duplication | public static function checkBrowserYandex() |
|
1042 | |||
1043 | /** |
||
1044 | * Determine if the browser is Comodo Dragon / Ice Dragon / Chromodo. |
||
1045 | * |
||
1046 | * @return bool |
||
1047 | */ |
||
1048 | View Code Duplication | public static function checkBrowserDragon() |
|
1063 | |||
1064 | /** |
||
1065 | * Determine if the browser is Android. |
||
1066 | * |
||
1067 | * @return bool |
||
1068 | */ |
||
1069 | public static function checkBrowserAndroid() |
||
1099 | |||
1100 | /** |
||
1101 | * Determine if the browser is UCBrowser. |
||
1102 | * |
||
1103 | * @return bool |
||
1104 | */ |
||
1105 | View Code Duplication | public static function checkBrowserUCBrowser() |
|
1121 | |||
1122 | /** |
||
1123 | * Determine if the browser is Windows Media Player. |
||
1124 | * |
||
1125 | * @return bool |
||
1126 | */ |
||
1127 | View Code Duplication | public static function checkBrowserNSPlayer() |
|
1143 | |||
1144 | /** |
||
1145 | * Determine if the browser is Microsoft Office. |
||
1146 | * |
||
1147 | * @return bool |
||
1148 | */ |
||
1149 | public static function checkBrowserOffice() |
||
1161 | |||
1162 | /** |
||
1163 | * Determine if the browser is the Apple News app. |
||
1164 | * |
||
1165 | * @return bool |
||
1166 | */ |
||
1167 | public static function checkBrowserAppleNews() |
||
1185 | } |
||
1186 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.