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 ExchangeAutodiscover 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 ExchangeAutodiscover, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
8 | class ExchangeAutodiscover |
||
9 | { |
||
10 | protected $autodiscoverPath = '/autodiscover/autodiscover.xml'; |
||
11 | |||
12 | /** |
||
13 | * @var HttpPlayback |
||
14 | */ |
||
15 | protected $httpPlayback; |
||
16 | |||
17 | 2 | protected function __construct() |
|
20 | |||
21 | /** |
||
22 | * Parse the hex ServerVersion value and return a valid |
||
23 | * ExchangeWebServices::VERSION_* constant. |
||
24 | * |
||
25 | * @param $versionHex |
||
26 | * @return string|boolean A known version constant, or FALSE if it could not |
||
27 | * be determined. |
||
28 | * |
||
29 | * @link http://msdn.microsoft.com/en-us/library/bb204122(v=exchg.140).aspx |
||
30 | * @link http://blogs.msdn.com/b/pcreehan/archive/2009/09/21/parsing-serverversion-when-an-int-is-really-5-ints.aspx |
||
31 | */ |
||
32 | 5 | protected function parseServerVersion($versionHex) |
|
68 | |||
69 | /** |
||
70 | * @param string $email |
||
71 | * @param string $password |
||
72 | * @param string $username |
||
73 | */ |
||
74 | 2 | protected function newAPI($email, $password, $username = null, $options = []) |
|
107 | |||
108 | 1 | protected function getServerVersionFromResponse($response) |
|
121 | |||
122 | 1 | protected function getServerFromResponse($response) |
|
132 | |||
133 | /** |
||
134 | * Static method may fail if there are issues surrounding SSL certificates. |
||
135 | * In such cases, set up the object as needed, and then call newEWS(). |
||
136 | * |
||
137 | * @param string $email |
||
138 | * @param string $password |
||
139 | * @param string $username If left blank, the email provided will be used. |
||
140 | * @throws AutodiscoverFailed |
||
141 | * @return API |
||
142 | */ |
||
143 | 2 | public static function getAPI($email, $password, $username = null, $options = []) |
|
149 | |||
150 | /** |
||
151 | * Execute the full discovery chain of events in the correct sequence |
||
152 | * until a valid response is received, or all methods have failed. |
||
153 | * |
||
154 | * @param string $email |
||
155 | * @param string $password |
||
156 | * @param string $username |
||
157 | * |
||
158 | * @return string The discovered settings |
||
159 | */ |
||
160 | 2 | protected function discover($email, $password, $username) |
|
178 | |||
179 | /** |
||
180 | * Perform an NTLM authenticated HTTPS POST to the top-level |
||
181 | * domain of the email address. |
||
182 | * |
||
183 | * @param string $email |
||
184 | * @param string $password |
||
185 | * @param string $username |
||
186 | * |
||
187 | * @return string The discovered settings |
||
188 | */ |
||
189 | 2 | View Code Duplication | protected function tryTopLevelDomain($email, $password, $username) |
196 | |||
197 | /** |
||
198 | * Perform an NTLM authenticated HTTPS POST to the 'autodiscover' |
||
199 | * subdomain of the email address' TLD. |
||
200 | * |
||
201 | * @param string $email |
||
202 | * @param string $password |
||
203 | * @param string $username |
||
204 | * |
||
205 | * @return string The discovered settings |
||
206 | */ |
||
207 | 2 | View Code Duplication | protected function tryAutoDiscoverSubDomain($email, $password, $username) |
214 | |||
215 | /** |
||
216 | * Perform an unauthenticated HTTP GET in an attempt to get redirected |
||
217 | * via 302 to the correct location to perform the HTTPS POST. |
||
218 | * |
||
219 | * @param string $email |
||
220 | * @param string $password |
||
221 | * @param string $username |
||
222 | * |
||
223 | * @return string The discovered settings |
||
224 | */ |
||
225 | 2 | protected function trySubdomainUnauthenticatedGet($email, $password, $username) |
|
252 | |||
253 | /** |
||
254 | * Attempt to retrieve the autodiscover host from an SRV DNS record. |
||
255 | * |
||
256 | * @link http://support.microsoft.com/kb/940881 |
||
257 | * |
||
258 | * @param string $email |
||
259 | * @param string $password |
||
260 | * @param string $username |
||
261 | * |
||
262 | * @return string The discovered settings |
||
263 | */ |
||
264 | 1 | protected function trySRVRecord($email, $password, $username) |
|
278 | |||
279 | /** |
||
280 | * Perform the NTLM authenticated post against one of the chosen |
||
281 | * endpoints. |
||
282 | * |
||
283 | * @param string $url URL to try posting to |
||
284 | * @param string $email |
||
285 | * @param string $password |
||
286 | * @param string $username |
||
287 | * |
||
288 | * @return string The discovered settings |
||
289 | */ |
||
290 | 2 | protected function doNTLMPost($url, $email, $password, $username) |
|
322 | |||
323 | /** |
||
324 | * Parse the Autoresponse Payload, particularly to determine if an |
||
325 | * additional request is necessary. |
||
326 | * |
||
327 | * @param $response |
||
328 | * @return array|bool |
||
329 | * @throws AutodiscoverFailed |
||
330 | */ |
||
331 | 1 | protected function parseAutodiscoverResponse($response) |
|
351 | |||
352 | /** |
||
353 | * Get a top level domain based on an email address |
||
354 | * |
||
355 | * @param string $email |
||
356 | * @return string|false |
||
357 | */ |
||
358 | 2 | protected function getTopLevelDomainFromEmail($email) |
|
367 | |||
368 | /** |
||
369 | * Utility function to parse XML payloads from the response into easier |
||
370 | * to manage associative arrays. |
||
371 | * |
||
372 | * @param string $xml XML to parse |
||
373 | * @return array |
||
374 | */ |
||
375 | 1 | protected function responseToArray($xml) |
|
381 | } |
||
382 |
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.