Complex classes like ExchangeWebServices 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 ExchangeWebServices, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
86 | class ExchangeWebServices |
||
87 | { |
||
88 | const VERSION_2007 = 'Exchange2007'; |
||
89 | |||
90 | const VERSION_2007_SP1 = 'Exchange2007_SP1'; |
||
91 | |||
92 | const VERSION_2010 = 'Exchange2010'; |
||
93 | |||
94 | const VERSION_2010_SP1 = 'Exchange2010_SP1'; |
||
95 | |||
96 | const VERSION_2010_SP2 = 'Exchange2010_SP2'; |
||
97 | |||
98 | const VERSION_2013 = 'Exchange2013'; |
||
99 | |||
100 | const VERSION_2013_SP1 = 'Exchange2013_SP1'; |
||
101 | |||
102 | /** |
||
103 | * Password to use when connecting to the Exchange server. |
||
104 | * |
||
105 | * @var string |
||
106 | */ |
||
107 | protected $password = null; |
||
108 | |||
109 | /** |
||
110 | * Location of the Exchange server. |
||
111 | * |
||
112 | * @var string |
||
113 | */ |
||
114 | protected $server = null; |
||
115 | |||
116 | /** |
||
117 | * SOAP client used to make the request |
||
118 | * |
||
119 | * @var NTLMSoapClient |
||
120 | */ |
||
121 | protected $soap = null; |
||
122 | |||
123 | /** |
||
124 | * Username to use when connecting to the Exchange server. |
||
125 | * |
||
126 | * @var string |
||
127 | */ |
||
128 | protected $username = null; |
||
129 | |||
130 | /** |
||
131 | * @var EmailAddressType |
||
132 | */ |
||
133 | protected $primarySmtpMailbox = null; |
||
134 | |||
135 | /** |
||
136 | * @var Callable[] |
||
137 | */ |
||
138 | protected static $middlewareStack = false; |
||
139 | |||
140 | /** |
||
141 | * A setting to check whether or not responses should be drilled down before being |
||
142 | * returned. Setting this to false |
||
143 | * will return the raw responses without any filtering |
||
144 | * |
||
145 | * @var bool |
||
146 | */ |
||
147 | protected $drillDownResponses = true; |
||
148 | |||
149 | /** |
||
150 | * Miscrosoft Exchange version that we are going to connect to |
||
151 | * |
||
152 | * @var string |
||
153 | */ |
||
154 | protected $version = null; |
||
155 | |||
156 | protected $options = null; |
||
157 | |||
158 | /** |
||
159 | * The timezone for the client |
||
160 | * |
||
161 | * @var bool |
||
162 | */ |
||
163 | protected $timezone = false; |
||
164 | |||
165 | /** |
||
166 | * @return EmailAddressType |
||
167 | */ |
||
168 | 27 | public function getPrimarySmtpMailbox() |
|
172 | |||
173 | 1 | public function getPrimarySmtpEmailAddress() |
|
174 | { |
||
175 | 1 | if ($this->primarySmtpMailbox == null) { |
|
176 | 1 | return null; |
|
177 | } |
||
178 | |||
179 | 1 | return $this->primarySmtpMailbox->getEmailAddress(); |
|
180 | } |
||
181 | |||
182 | 2 | public function setPrimarySmtpEmailAddress($emailAddress) |
|
183 | { |
||
184 | 2 | $mailbox = new EmailAddressType(); |
|
185 | 2 | $mailbox->setEmailAddress($emailAddress); |
|
186 | 2 | $this->primarySmtpMailbox = $mailbox; |
|
187 | |||
188 | 2 | return $this; |
|
189 | } |
||
190 | |||
191 | /** |
||
192 | * @param boolean $timezone |
||
193 | */ |
||
194 | public function setTimezone($timezone) |
||
198 | |||
199 | /** |
||
200 | * @return string |
||
201 | */ |
||
202 | public function getVersion() |
||
203 | { |
||
204 | return $this->version; |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * @return string |
||
209 | */ |
||
210 | public function getServer() |
||
211 | { |
||
212 | return $this->server; |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Constructor for the ExchangeWebServices class |
||
217 | * |
||
218 | * @param string $server |
||
219 | * @param string $username |
||
220 | * @param string $password |
||
221 | * @param array $options |
||
222 | */ |
||
223 | 35 | protected function __construct($server = null, $username = null, $password = null, $options = array()) |
|
224 | { |
||
225 | 35 | if ($server !== null) { |
|
226 | $this->createClient( |
||
227 | $server, |
||
228 | ExchangeWebServicesAuth::fromUsernameAndPassword($username, $password), |
||
229 | $options |
||
230 | ); |
||
231 | } |
||
232 | |||
233 | 35 | $this->buildMiddlewareStack(); |
|
234 | 35 | } |
|
235 | |||
236 | 34 | public static function fromUsernameAndPassword($server, $username, $password, $options) |
|
237 | { |
||
238 | 34 | $self = new self(); |
|
239 | 34 | $self->createClient($server, ExchangeWebServicesAuth::fromUsernameAndPassword($username, $password), $options); |
|
240 | 34 | $self->options = $options; |
|
241 | |||
242 | 34 | return $self; |
|
243 | } |
||
244 | |||
245 | 1 | public static function fromCallbackToken($server, $token, $options) |
|
246 | { |
||
247 | 1 | $self = new self(); |
|
248 | 1 | $self->createClient($server, ExchangeWebServicesAuth::fromCallbackToken($token), $options); |
|
249 | 1 | $self->options = $options; |
|
250 | |||
251 | 1 | return $self; |
|
252 | } |
||
253 | |||
254 | 35 | protected function createClient($server, $auth, $options) |
|
255 | { |
||
256 | 35 | $location = 'https://' . $this->cleanServerUrl($server) . '/EWS/Exchange.asmx'; |
|
257 | |||
258 | 35 | $options = array_replace_recursive([ |
|
259 | 35 | 'version' => self::VERSION_2007, |
|
260 | 35 | 'trace' => 1, |
|
261 | 35 | 'exceptions' => true, |
|
262 | 35 | 'classmap' => ClassMap::getClassMap(), |
|
263 | 'drillDownResponses' => true |
||
264 | 35 | ], $options); |
|
265 | |||
266 | 35 | $this->server = $server; |
|
267 | 35 | $this->version = $options['version']; |
|
268 | |||
269 | 35 | $backup = libxml_disable_entity_loader(false); |
|
270 | 35 | $this->soap = new NTLMSoapClient( |
|
271 | 35 | $location, |
|
272 | 35 | $auth, |
|
273 | 35 | dirname(__FILE__) . '/../../Resources/wsdl/services.wsdl', |
|
274 | $options |
||
275 | 35 | ); |
|
276 | 35 | libxml_disable_entity_loader($backup); |
|
277 | |||
278 | 35 | if (isset($options['primarySmtpEmailAddress'])) { |
|
279 | 1 | $this->setPrimarySmtpEmailAddress($options['primarySmtpEmailAddress']); |
|
280 | 1 | } |
|
281 | |||
282 | 35 | if (isset($options['impersonation'])) { |
|
283 | 1 | $this->setPrimarySmtpEmailAddress($options['impersonation']); |
|
284 | 1 | } |
|
285 | |||
286 | 35 | $this->drillDownResponses = $options['drillDownResponses']; |
|
287 | 35 | } |
|
288 | |||
289 | /** |
||
290 | * @codeCoverageIgnore |
||
291 | * |
||
292 | * @param $name |
||
293 | * @param $arguments |
||
294 | * @return Type |
||
295 | * @throws \garethp\ews\API\Exception |
||
296 | */ |
||
297 | public function __call($name, $arguments) |
||
298 | { |
||
299 | $request = MiddlewareRequest::newRequest($name, $arguments, $this->options); |
||
300 | $response = $this->executeMiddlewareStack(self::$middlewareStack, $request); |
||
301 | $response = $response->getResponse(); |
||
302 | return $response; |
||
303 | return $this->processResponse($response); |
||
|
|||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Returns the SOAP Client that may be used to make calls against the server |
||
308 | * |
||
309 | * @return NTLMSoapClient |
||
310 | */ |
||
311 | 31 | public function getClient() |
|
312 | { |
||
313 | 31 | return $this->soap; |
|
314 | } |
||
315 | |||
316 | /** |
||
317 | * Cleans the server URL for usage |
||
318 | * |
||
319 | * @param $server |
||
320 | * @return string |
||
321 | */ |
||
322 | 42 | public function cleanServerUrl($server) |
|
343 | |||
344 | /** |
||
345 | * Process a response to verify that it succeeded and take the appropriate |
||
346 | * action |
||
347 | * |
||
348 | * @param \garethp\ews\API\Message\BaseResponseMessageType $response |
||
349 | * @return Type[] |
||
350 | * @throws \garethp\ews\API\Exception |
||
351 | */ |
||
352 | 29 | protected function processResponse($response) |
|
353 | { |
||
354 | // If the soap call failed then we need to thow an exception. |
||
355 | 29 | $code = $this->getClient()->getResponseCode(); |
|
356 | 29 | $this->handleNonSuccessfulResponses($response, $code); |
|
357 | |||
358 | 29 | if (!$this->drillDownResponses) { |
|
359 | return $response; |
||
360 | } |
||
361 | |||
362 | 29 | if (!$response->exists('responseMessages')) { |
|
363 | return $response; |
||
364 | } |
||
365 | |||
366 | 29 | $response = $response->getResponseMessages(); |
|
367 | 29 | $response = $this->drillDownResponseLevels($response); |
|
368 | |||
369 | 29 | return $response; |
|
370 | } |
||
371 | |||
372 | /** |
||
373 | * @param $response |
||
374 | * @return array |
||
375 | * @throws \garethp\ews\API\Exception |
||
376 | */ |
||
377 | 29 | public function drillDownResponseLevels($response) |
|
378 | { |
||
379 | 29 | $items = $this->getItemsFromResponse($response); |
|
380 | |||
381 | 29 | if (count($items) == 1) { |
|
382 | 29 | reset($items); |
|
383 | 29 | $key = key($items); |
|
384 | 29 | $methodName = "get$key"; |
|
385 | 29 | $response = $response->$methodName(); |
|
386 | |||
387 | 29 | return $this->drillDownResponseLevels($response); |
|
388 | } |
||
389 | |||
390 | 29 | if (is_array($items) && isset($items[1]) && $items[1] instanceof Message\ResponseMessageType) { |
|
391 | 3 | $response = array(); |
|
392 | 3 | foreach ($items as $responseItem) { |
|
393 | 3 | $response[] = $this->drillDownResponseLevels($responseItem); |
|
394 | 3 | } |
|
395 | |||
396 | 3 | return $response; |
|
397 | } |
||
398 | |||
399 | 29 | return $response; |
|
400 | } |
||
401 | |||
402 | /** |
||
403 | * @param $response |
||
404 | * @return array |
||
405 | * @throws ExchangeException |
||
406 | */ |
||
407 | 29 | protected function getItemsFromResponse($response) |
|
408 | { |
||
409 | 29 | $items = array(); |
|
410 | 29 | if ($response instanceof Type) { |
|
411 | 29 | $items = $response->getNonNullItems(); |
|
412 | 29 | } |
|
413 | |||
414 | 29 | if (is_array($response)) { |
|
415 | 3 | $items = $response; |
|
416 | 3 | } |
|
417 | |||
418 | 29 | if ($response instanceof Message\ResponseMessageType) { |
|
419 | 29 | if ($response->getResponseClass() !== "Success") { |
|
420 | 1 | throw new ExchangeException($response->getMessageText()); |
|
421 | } |
||
422 | |||
423 | 29 | unset($items['responseClass']); |
|
424 | 29 | unset($items['responseCode']); |
|
425 | 29 | } |
|
426 | |||
427 | 29 | return $items; |
|
428 | } |
||
429 | |||
430 | /** |
||
431 | * @param Message\BaseResponseMessageType $response |
||
432 | * @param $code |
||
433 | * @throws ExchangeException |
||
434 | * @throws NoResponseReturnedException |
||
435 | * @throws ServiceUnavailableException |
||
436 | * @throws UnauthorizedException |
||
437 | */ |
||
438 | 29 | protected function handleNonSuccessfulResponses($response, $code) |
|
439 | { |
||
440 | 29 | if ($code == 401) { |
|
441 | throw new UnauthorizedException(); |
||
442 | } |
||
443 | |||
444 | 29 | if ($code == 503) { |
|
445 | throw new ServiceUnavailableException(); |
||
446 | } |
||
447 | |||
448 | 29 | if ($code >= 300) { |
|
449 | throw new ExchangeException('SOAP client returned status of ' . $code, $code); |
||
450 | } |
||
451 | |||
452 | 29 | if (empty($response) || empty($response->getNonNullResponseMessages())) { |
|
453 | throw new NoResponseReturnedException(); |
||
454 | } |
||
455 | 29 | } |
|
456 | |||
457 | 35 | protected function buildMiddlewareStack() |
|
480 | |||
481 | /** |
||
482 | * @param array $middlewareStack |
||
483 | * @param MiddlewareRequest $request |
||
484 | * @return MiddlewareResponse |
||
485 | */ |
||
486 | 29 | protected function executeMiddlewareStack(array $middlewareStack, MiddlewareRequest $request) |
|
487 | { |
||
488 | 29 | $newStack = []; |
|
512 | } |
||
513 |
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.