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 SemRush 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 SemRush, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 19 | class SemRush extends SEOstats |
||
| 20 | { |
||
| 21 | 1 | public static function getDBs() |
|
| 37 | |||
| 38 | 1 | public static function getParams() |
|
| 69 | |||
| 70 | /** |
||
| 71 | * Returns the SEMRush main report data. |
||
| 72 | * (Only main report is public available.) |
||
| 73 | * |
||
| 74 | * @access public |
||
| 75 | * @param url string Domain name only, eg. "ebay.com" (/wo quotes). |
||
| 76 | * @param db string Optional: The database to use. Valid values are: |
||
| 77 | * au, br, ca, de, es, fr, it, ru, uk, us, us.bing (us is default) |
||
| 78 | * @return array Returns an array containing the main report data. |
||
| 79 | * @link http://www.semrush.com/api.html |
||
| 80 | */ |
||
| 81 | 6 | public static function getDomainRank($url = false, $db = false) |
|
| 82 | { |
||
| 83 | 6 | $data = self::getBackendData($url, $db, 'domain_rank'); |
|
| 84 | |||
| 85 | return is_array($data) ? $data['rank']['data'][0] : $data; |
||
| 86 | } |
||
| 87 | |||
| 88 | 6 | public static function getDomainRankHistory($url = false, $db = false) |
|
| 89 | { |
||
| 90 | 6 | $data = self::getBackendData($url, $db, 'domain_rank_history'); |
|
| 91 | |||
| 92 | return is_array($data) ? $data['rank_history'] : $data; |
||
| 93 | } |
||
| 94 | |||
| 95 | 4 | public static function getOrganicKeywords($url = false, $db = false) |
|
| 99 | |||
| 100 | 4 | public static function getCompetitors($url = false, $db = false) |
|
| 104 | |||
| 105 | 10 | public static function getDomainGraph($reportType = 1, $url = false, $db = false, $w = 400, $h = 300, $lc = 'e43011', $dc = 'e43011', $lang = 'en', $html = true) |
|
| 106 | { |
||
| 107 | 10 | $domain = static::getDomainFromUrl($url); |
|
| 108 | $database = static::getValidDatabase($db); |
||
| 109 | |||
| 110 | static::guardValidArgsForGetDomainGraph($reportType, $w, $h, $lang); |
||
| 111 | |||
| 112 | $imgUrl = sprintf(Config\Services::SEMRUSH_GRAPH_URL, |
||
| 113 | $domain, $database, $reportType, $w, $h, $lc, $dc, $lang); |
||
| 114 | |||
| 115 | if (! $html) { |
||
| 116 | return $imgUrl; |
||
| 117 | } else { |
||
| 118 | $imgTag = '<img src="%s" width="%s" height="%s" alt="SEMRush Domain Trend Graph for %s"/>'; |
||
| 119 | return sprintf($imgTag, $imgUrl, $w, $h, $domain); |
||
| 120 | } |
||
| 121 | } |
||
| 122 | |||
| 123 | 1 | protected static function getApiData($url) |
|
| 128 | |||
| 129 | protected static function getSemRushDatabase($db) |
||
| 130 | { |
||
| 131 | return false !== $db |
||
| 132 | ? $db |
||
| 133 | : Config\DefaultSettings::SEMRUSH_DB; |
||
| 134 | } |
||
| 135 | |||
| 136 | 36 | protected static function guardDomainIsValid($domain) |
|
| 137 | { |
||
| 138 | 36 | if (false == $domain) { |
|
| 139 | 36 | self::exc('Invalid domain name.'); |
|
| 140 | } |
||
| 141 | } |
||
| 142 | |||
| 143 | protected static function guardDatabaseIsValid($database) |
||
| 144 | { |
||
| 145 | if (false === $database) { |
||
| 146 | self::exc('db'); |
||
| 147 | } |
||
| 148 | } |
||
| 149 | |||
| 150 | protected static function guardValidArgsForGetDomainGraph($reportType, $width, $height, $lang) |
||
| 151 | { |
||
| 152 | if ($reportType > 5 || $reportType < 1) { |
||
| 153 | self::exc('Report type must be between 1 (one) and 5 (five).'); |
||
| 154 | } |
||
| 155 | |||
| 156 | if ($width > 400 || $width < 200) { |
||
| 157 | self::exc('Image width must be between 200 and 400 px.'); |
||
| 158 | } |
||
| 159 | |||
| 160 | if ($height > 300 || $height < 150) { |
||
| 161 | self::exc('Image height must be between 150 and 300 px.'); |
||
| 162 | } |
||
| 163 | |||
| 164 | if (strlen($lang) != 2) { |
||
| 165 | self::exc('You must specify a valid language code.'); |
||
| 166 | } |
||
| 167 | } |
||
| 168 | |||
| 169 | 12 | protected static function getBackendData($url, $db, $reportType) |
|
| 170 | { |
||
| 171 | 12 | $db = false !== $db ? $db : Config\DefaultSettings::SEMRUSH_DB; |
|
| 172 | 12 | $dataUrl = self::getBackendUrl($url, $db, $reportType); |
|
| 173 | $data = self::getApiData($dataUrl); |
||
| 174 | |||
| 175 | if (!is_array($data)) { |
||
| 176 | $data = self::getApiData(str_replace('.backend.', '.api.', $dataUrl)); |
||
| 177 | if (!is_array($data)) { |
||
| 178 | return parent::noDataDefaultValue(); |
||
|
|
|||
| 179 | } |
||
| 180 | } |
||
| 181 | |||
| 182 | return $data; |
||
| 183 | } |
||
| 184 | |||
| 185 | 15 | View Code Duplication | protected static function getBackendUrl($url, $db, $reportType) |
| 186 | { |
||
| 187 | 15 | $domain = static::getDomainFromUrl($url); |
|
| 188 | $database = static::getValidDatabase($db); |
||
| 189 | |||
| 190 | $backendUrl = Config\Services::SEMRUSH_BE_URL; |
||
| 191 | return sprintf($backendUrl, $database, $reportType, $domain); |
||
| 192 | } |
||
| 193 | |||
| 194 | 11 | View Code Duplication | protected static function getWidgetUrl($url, $db, $reportType) |
| 195 | { |
||
| 196 | 11 | $domain = static::getDomainFromUrl($url); |
|
| 197 | $database = static::getValidDatabase($db); |
||
| 198 | |||
| 199 | $widgetUrl = Config\Services::SEMRUSH_WIDGET_URL; |
||
| 200 | return sprintf($widgetUrl, $reportType, $database, $domain); |
||
| 201 | } |
||
| 202 | |||
| 203 | 8 | protected static function getWidgetData($url, $db, $reportType, $valueKey) |
|
| 204 | { |
||
| 205 | 8 | $db = false !== $db ? $db : Config\DefaultSettings::SEMRUSH_DB; |
|
| 206 | 8 | $dataUrl = self::getWidgetUrl($url, $db, $reportType); |
|
| 207 | $data = self::getApiData($dataUrl); |
||
| 208 | |||
| 209 | return !is_array($data) ? parent::noDataDefaultValue() : $data[ $valueKey ]; |
||
| 210 | } |
||
| 211 | |||
| 212 | protected static function checkDatabase($db) |
||
| 213 | { |
||
| 214 | return !in_array($db, self::getDBs()) ? false : $db; |
||
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * |
||
| 219 | * @throws E |
||
| 220 | */ |
||
| 221 | 36 | View Code Duplication | protected static function exc($err) |
| 228 | |||
| 229 | 36 | protected static function getDomainFromUrl($url) |
|
| 230 | { |
||
| 231 | 36 | $url = parent::getUrl($url); |
|
| 232 | 36 | $domain = Helper\Url::parseHost($url); |
|
| 233 | 36 | static::guardDomainIsValid($domain); |
|
| 234 | |||
| 235 | return $domain; |
||
| 236 | } |
||
| 237 | |||
| 238 | protected static function getValidDatabase($db) |
||
| 239 | { |
||
| 240 | $db = self::getSemRushDatabase($db); |
||
| 241 | $database = self::checkDatabase($db); |
||
| 246 | } |
||
| 247 |
This check looks for a call to a parent method whose name is different than the method from which it is called.
Consider the following code:
The
getFirstName()method in theSoncalls the wrong method in the parent class.