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 EWSAutodiscover 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 EWSAutodiscover, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
40 | class EWSAutodiscover |
||
41 | { |
||
42 | /** |
||
43 | * The path appended to the various schemes and hostnames used during |
||
44 | * autodiscovery. |
||
45 | * |
||
46 | * @var string |
||
47 | */ |
||
48 | const AUTODISCOVER_PATH = '/autodiscover/autodiscover.xml'; |
||
49 | |||
50 | /** |
||
51 | * The Autodiscover XML request. Since it's used repeatedly, it's cached |
||
52 | * in this property to avoid redundant re-generation. |
||
53 | * |
||
54 | * @var string |
||
55 | */ |
||
56 | protected $requestXML; |
||
57 | |||
58 | /** |
||
59 | * The Certificate Authority path. Should point to a directory containing |
||
60 | * one or more certificates to use in SSL verification. |
||
61 | * |
||
62 | * @var string |
||
63 | */ |
||
64 | protected $certificateAuthorityPath; |
||
65 | |||
66 | /** |
||
67 | * The path to a specific Certificate Authority file. Get one and use it |
||
68 | * for full Autodiscovery compliance. |
||
69 | * |
||
70 | * @var string |
||
71 | * |
||
72 | * @link http://curl.haxx.se/ca/cacert.pem |
||
73 | * @link http://curl.haxx.se/ca/ |
||
74 | */ |
||
75 | protected $certificateAuthorityInfo; |
||
76 | |||
77 | /** |
||
78 | * @var HttpPlayback |
||
79 | */ |
||
80 | protected $httpPlayback; |
||
81 | |||
82 | /** |
||
83 | * An associative array of response headers that resulted from the |
||
84 | * last request. Keys are lowercased for easy checking. |
||
85 | * |
||
86 | * @var array |
||
87 | */ |
||
88 | public $last_response_headers; |
||
89 | |||
90 | /** |
||
91 | * The result of the most recent curl_exec. |
||
92 | * |
||
93 | * @var mixed |
||
94 | */ |
||
95 | public $last_response; |
||
96 | |||
97 | /** |
||
98 | * The output of curl_info() relating to the most recent cURL request. |
||
99 | * |
||
100 | * @var mixed |
||
101 | */ |
||
102 | public $last_info; |
||
103 | |||
104 | /** |
||
105 | * The cURL error code associated with the most recent cURL request. |
||
106 | * |
||
107 | * @var integer |
||
108 | */ |
||
109 | public $last_curl_errno; |
||
110 | |||
111 | /** |
||
112 | * Human-readable description of the most recent cURL error. |
||
113 | * |
||
114 | * @var string |
||
115 | */ |
||
116 | public $last_curl_error; |
||
117 | |||
118 | /** |
||
119 | * Information about an Autodiscover Response containing an error will |
||
120 | * be stored here. |
||
121 | * |
||
122 | * @var mixed |
||
123 | */ |
||
124 | public $error = false; |
||
125 | |||
126 | /** |
||
127 | * Information about an Autodiscover Response with a redirect will be |
||
128 | * retained here. |
||
129 | * |
||
130 | * @var mixed |
||
131 | */ |
||
132 | public $redirect = false; |
||
133 | |||
134 | /** |
||
135 | * A successful, non-error and non-redirect parsed Autodiscover response |
||
136 | * will be stored here. |
||
137 | * |
||
138 | * @var mixed |
||
139 | */ |
||
140 | public $discovered = null; |
||
141 | |||
142 | protected function __construct() |
||
145 | |||
146 | /** |
||
147 | * Parse the hex ServerVersion value and return a valid |
||
148 | * ExchangeWebServices::VERSION_* constant. |
||
149 | * |
||
150 | * @param $version_hex |
||
151 | * @return string|boolean A known version constant, or FALSE if it could not |
||
152 | * be determined. |
||
153 | * |
||
154 | * @link http://msdn.microsoft.com/en-us/library/bb204122(v=exchg.140).aspx |
||
155 | * @link http://blogs.msdn.com/b/pcreehan/archive/2009/09/21/parsing-serverversion-when-an-int-is-really-5-ints.aspx |
||
156 | */ |
||
157 | protected function parseServerVersion($version_hex) |
||
196 | |||
197 | protected function newAPI($email, $password, $username = null, $options = []) |
||
248 | |||
249 | /** |
||
250 | * Static method may fail if there are issues surrounding SSL certificates. |
||
251 | * In such cases, set up the object as needed, and then call newEWS(). |
||
252 | * |
||
253 | * @param string $email |
||
254 | * @param string $password |
||
255 | * @param string $username If left blank, the email provided will be used. |
||
256 | * @return mixed |
||
257 | */ |
||
258 | public static function getAPI($email, $password, $username = null, $options = []) |
||
264 | |||
265 | /** |
||
266 | * Execute the full discovery chain of events in the correct sequence |
||
267 | * until a valid response is received, or all methods have failed. |
||
268 | * |
||
269 | * @param string $email |
||
270 | * @param string $password |
||
271 | * @param string $username |
||
272 | * |
||
273 | * @return string The discovered settings |
||
274 | */ |
||
275 | protected function discover($email, $password, $username) |
||
293 | |||
294 | /** |
||
295 | * Perform an NTLM authenticated HTTPS POST to the top-level |
||
296 | * domain of the email address. |
||
297 | * |
||
298 | * @param string $email |
||
299 | * @param string $password |
||
300 | * @param string $username |
||
301 | * |
||
302 | * @return string The discovered settings |
||
303 | */ |
||
304 | View Code Duplication | protected function tryTopLevelDomain($email, $password, $username) |
|
311 | |||
312 | /** |
||
313 | * Perform an NTLM authenticated HTTPS POST to the 'autodiscover' |
||
314 | * subdomain of the email address' TLD. |
||
315 | * |
||
316 | * @param string $email |
||
317 | * @param string $password |
||
318 | * @param string $username |
||
319 | * |
||
320 | * @return string The discovered settings |
||
321 | */ |
||
322 | View Code Duplication | protected function tryAutoDiscoverSubDomain($email, $password, $username) |
|
329 | |||
330 | /** |
||
331 | * Perform an unauthenticated HTTP GET in an attempt to get redirected |
||
332 | * via 302 to the correct location to perform the HTTPS POST. |
||
333 | * |
||
334 | * @param string $email |
||
335 | * @param string $password |
||
336 | * @param string $username |
||
337 | * |
||
338 | * @return string The discovered settings |
||
339 | */ |
||
340 | protected function trySubdomainUnauthenticatedGet($email, $password, $username) |
||
366 | |||
367 | /** |
||
368 | * Attempt to retrieve the autodiscover host from an SRV DNS record. |
||
369 | * |
||
370 | * @link http://support.microsoft.com/kb/940881 |
||
371 | * |
||
372 | * @param string $email |
||
373 | * @param string $password |
||
374 | * @param string $username |
||
375 | * |
||
376 | * @return string The discovered settings |
||
377 | */ |
||
378 | protected function trySRVRecord($email, $password, $username) |
||
392 | |||
393 | /** |
||
394 | * Set the path to the file to be used by CURLOPT_CAINFO. |
||
395 | * |
||
396 | * @param string $path Path to a certificate file such as cacert.pem |
||
397 | * @return self |
||
398 | */ |
||
399 | public function setCAInfo($path) |
||
407 | |||
408 | /** |
||
409 | * Set the path to the file to be used by CURLOPT_CAPATH. |
||
410 | * |
||
411 | * @param string $path Path to a directory containing one or more CA |
||
412 | * certificates. |
||
413 | * @return self |
||
414 | */ |
||
415 | public function setCertificateAuthorityPath($path) |
||
423 | |||
424 | /** |
||
425 | * Perform the NTLM authenticated post against one of the chosen |
||
426 | * endpoints. |
||
427 | * |
||
428 | * @param string $url URL to try posting to |
||
429 | * @param string $email |
||
430 | * @param string $password |
||
431 | * @param string $username |
||
432 | * |
||
433 | * @return string The discovered settings |
||
434 | */ |
||
435 | protected function doNTLMPost($url, $email, $password, $username) |
||
466 | |||
467 | /** |
||
468 | * Parse the Autoresponse Payload, particularly to determine if an |
||
469 | * additional request is necessary. |
||
470 | * |
||
471 | * @param $response |
||
472 | * @return array|bool |
||
473 | * @throws AutoDiscoverFailed |
||
474 | */ |
||
475 | protected function parseAutodiscoverResponse($response) |
||
509 | |||
510 | /** |
||
511 | * Get a top level domain based on an email address |
||
512 | * |
||
513 | * @param $email |
||
514 | * @return bool|string |
||
515 | */ |
||
516 | protected function getTopLevelDomainFromEmail($email) |
||
525 | |||
526 | /** |
||
527 | * Return the generated Autodiscover XML request body. |
||
528 | * |
||
529 | * @param string $email |
||
530 | * @return string |
||
531 | */ |
||
532 | protected function getAutoDiscoverXML($email) |
||
561 | |||
562 | /** |
||
563 | * Utility function to parse XML payloads from the response into easier |
||
564 | * to manage associative arrays. |
||
565 | * |
||
566 | * @param string $xml XML to parse |
||
567 | * @return array |
||
568 | */ |
||
569 | protected function responseToArray($xml) |
||
577 | |||
578 | /** |
||
579 | * Recursive method for parsing DOM nodes. |
||
580 | * |
||
581 | * @link https://github.com/gaarf/XML-string-to-PHP-array |
||
582 | * @param object $node DOMNode object |
||
583 | * @return mixed |
||
584 | */ |
||
585 | protected function nodeToArray($node) |
||
634 | } |
||
635 |
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.