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 Client 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 Client, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
32 | class Client extends \GuzzleHttp\Client implements \Psr\Log\LoggerAwareInterface |
||
33 | { |
||
34 | const VERSION = '1.0.0'; |
||
35 | |||
36 | /** |
||
37 | * @const int Default Timeout in seconds |
||
38 | */ |
||
39 | const DEFAULT_REQUEST_TIMEOUT = 300; |
||
40 | |||
41 | /** |
||
42 | * @var bool|array|resource Whether verbose mode is enabled |
||
43 | * |
||
44 | * - true - Use STDERR |
||
45 | * - array - output/error streams (different) |
||
46 | * - resource - output/error stream (same) |
||
47 | */ |
||
48 | protected static $staticVerbose = false; |
||
49 | |||
50 | /** |
||
51 | * @var bool|resource Whether debug mode is enabled |
||
52 | */ |
||
53 | protected static $staticDebug = false; |
||
54 | |||
55 | /** |
||
56 | * @var \Akamai\Open\EdgeGrid\Authentication |
||
57 | */ |
||
58 | protected $authentication; |
||
59 | |||
60 | /** |
||
61 | * @var \Akamai\Open\EdgeGrid\Handler\Verbose |
||
62 | */ |
||
63 | protected $verboseHandler; |
||
64 | |||
65 | /** |
||
66 | * @var \Akamai\Open\EdgeGrid\Handler\Debug |
||
67 | */ |
||
68 | protected $debugHandler; |
||
69 | |||
70 | /** |
||
71 | * @var bool|array|resource Whether verbose mode is enabled |
||
72 | * |
||
73 | * - true - Use STDOUT |
||
74 | * - array - output/error streams (different) |
||
75 | * - resource - output/error stream (same) |
||
76 | */ |
||
77 | protected $verbose = false; |
||
78 | |||
79 | /** |
||
80 | * @var bool|resource Whether debugging is enabled |
||
81 | */ |
||
82 | protected $debug = false; |
||
83 | |||
84 | /** |
||
85 | * @var bool Whether to override the static verbose setting |
||
86 | */ |
||
87 | protected $verboseOverride = false; |
||
88 | |||
89 | /** |
||
90 | * @var bool Whether to override the static debug setting |
||
91 | */ |
||
92 | protected $debugOverride = false; |
||
93 | |||
94 | /** |
||
95 | * @var callable Logging Handler |
||
96 | */ |
||
97 | protected $logger; |
||
98 | |||
99 | /** |
||
100 | * \GuzzleHttp\Client-compatible constructor |
||
101 | * |
||
102 | * @param array $config Config options array |
||
103 | * @param Authentication|null $authentication |
||
104 | */ |
||
105 | 131 | public function __construct( |
|
116 | |||
117 | /** |
||
118 | * Make an Asynchronous request |
||
119 | * |
||
120 | * @param string $method |
||
121 | * @param string $uri |
||
122 | * @param array $options |
||
123 | * @return \GuzzleHttp\Promise\PromiseInterface |
||
124 | * @throws \GuzzleHttp\Exception\GuzzleException |
||
125 | */ |
||
126 | 77 | public function requestAsync($method, $uri = null, array $options = []) |
|
127 | { |
||
128 | 77 | $options = $this->setRequestOptions($options); |
|
129 | |||
130 | 77 | $query = parse_url($uri, PHP_URL_QUERY); |
|
131 | 77 | if (!empty($query)) { |
|
132 | 1 | $uri = substr($uri, 0, (strlen($query)+1) * -1); |
|
133 | 1 | parse_str($query, $options['query']); |
|
134 | } |
||
135 | |||
136 | 77 | return parent::requestAsync($method, $uri, $options); |
|
137 | } |
||
138 | |||
139 | /** |
||
140 | * Send an Asynchronous HTTP request |
||
141 | * |
||
142 | * @param \Psr\Http\Message\RequestInterface $request The HTTP request |
||
143 | * @param array $options Request options |
||
144 | * |
||
145 | * @return \GuzzleHttp\Promise\PromiseInterface |
||
146 | */ |
||
147 | 28 | public function sendAsync(\Psr\Http\Message\RequestInterface $request, array $options = []) |
|
153 | |||
154 | /** |
||
155 | * Set Akamai {OPEN} Authentication Credentials |
||
156 | * |
||
157 | * @param string $client_token |
||
158 | * @param string $client_secret |
||
159 | * @param string $access_token |
||
160 | * @return $this |
||
161 | */ |
||
162 | 60 | public function setAuth($client_token, $client_secret, $access_token) |
|
168 | |||
169 | /** |
||
170 | * Specify the headers to include when signing the request |
||
171 | * |
||
172 | * This is specified by the API, currently no APIs use this |
||
173 | * feature. |
||
174 | * |
||
175 | * @param array $headers |
||
176 | * @return $this |
||
177 | */ |
||
178 | 42 | public function setHeadersToSign(array $headers) |
|
184 | |||
185 | /** |
||
186 | * Set the max body size |
||
187 | * |
||
188 | * @param int $max_body_size |
||
189 | * @return $this |
||
190 | */ |
||
191 | 42 | public function setMaxBodySize($max_body_size) |
|
197 | |||
198 | /** |
||
199 | * Set Request Host |
||
200 | * |
||
201 | * @param string $host |
||
202 | * @return $this |
||
203 | */ |
||
204 | 2 | public function setHost($host) |
|
205 | { |
||
206 | 2 | if (substr($host, -1) === '/') { |
|
207 | 2 | $host = substr($host, 0, -1); |
|
208 | } |
||
209 | |||
210 | 2 | $headers = $this->getConfig('headers'); |
|
211 | 2 | $headers['Host'] = $host; |
|
212 | 2 | $this->setConfigOption('headers', $headers); |
|
213 | |||
214 | 2 | if (strpos('/', $host) === false) { |
|
215 | 2 | $host = 'https://' . $host; |
|
216 | } |
||
217 | 2 | $this->setConfigOption('base_uri', $host); |
|
218 | |||
219 | 2 | return $this; |
|
220 | } |
||
221 | |||
222 | /** |
||
223 | * Set the HTTP request timeout |
||
224 | * |
||
225 | * @param int $timeout_in_seconds |
||
226 | * @return $this |
||
227 | */ |
||
228 | 2 | public function setTimeout($timeout_in_seconds) |
|
234 | |||
235 | /** |
||
236 | * Print formatted JSON responses to output |
||
237 | * |
||
238 | * @param bool|resource $enable |
||
239 | * @return $this |
||
240 | */ |
||
241 | 4 | public function setInstanceVerbose($enable) |
|
247 | |||
248 | /** |
||
249 | * Print HTTP requests/responses to output |
||
250 | * |
||
251 | * @param bool|resource $enable |
||
252 | * @return $this |
||
253 | */ |
||
254 | 10 | public function setInstanceDebug($enable) |
|
260 | |||
261 | /** |
||
262 | * Set a PSR-3 compatible logger (or use monolog by default) |
||
263 | * |
||
264 | * @param \Psr\Log\LoggerInterface $logger |
||
265 | * @param string $messageFormat Message format |
||
266 | * @return $this |
||
267 | */ |
||
268 | 24 | public function setLogger( |
|
269 | \Psr\Log\LoggerInterface $logger = null, |
||
270 | $messageFormat = \GuzzleHttp\MessageFormatter::CLF |
||
271 | ) { |
||
272 | 24 | if ($logger === null) { |
|
273 | 2 | $handler = new \Monolog\Handler\ErrorLogHandler(\Monolog\Handler\ErrorLogHandler::SAPI); |
|
274 | 2 | $handler->setFormatter(new \Monolog\Formatter\LineFormatter('%message%')); |
|
275 | 2 | $logger = new \Monolog\Logger('HTTP Log', [$handler]); |
|
276 | } |
||
277 | |||
278 | 24 | $formatter = new \GuzzleHttp\MessageFormatter($messageFormat); |
|
279 | |||
280 | 24 | $handler = \GuzzleHttp\Middleware::log($logger, $formatter); |
|
281 | 24 | $this->logger = $handler; |
|
282 | |||
283 | 24 | $handlerStack = $this->getConfig('handler'); |
|
284 | 24 | $this->setLogHandler($handlerStack, $handler); |
|
285 | |||
286 | 24 | return $this; |
|
287 | } |
||
288 | |||
289 | /** |
||
290 | * Add logger using a given filename/format |
||
291 | * |
||
292 | * @param string $filename |
||
293 | * @param string $format |
||
294 | * @return \Akamai\Open\EdgeGrid\Client|bool |
||
295 | */ |
||
296 | 4 | public function setSimpleLog($filename, $format = '{code}') |
|
308 | |||
309 | 10 | /** |
|
310 | * Create instance using environment (preferred) or .edgerc file (fallback) automatically. |
||
311 | 10 | * |
|
312 | * @param string $section |
||
313 | 2 | * @param null $path |
|
314 | 2 | * @return Client |
|
315 | * @throws \Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException |
||
316 | */ |
||
317 | 2 | View Code Duplication | public static function createInstance($section = 'default', $path = null, array $config = []) |
327 | |||
328 | 8 | View Code Duplication | public static function createFromEnv($section = 'default', array $config = []) |
338 | |||
339 | /** |
||
340 | * Factory method to create a client using credentials from `.edgerc` |
||
341 | * |
||
342 | 8 | * Automatically checks your HOME directory, and the current working |
|
343 | * directory for credentials, if no path is supplied. |
||
344 | 8 | * |
|
345 | * @param string $section Credential section to use |
||
346 | 8 | * @param string $path Path to .edgerc credentials file |
|
347 | 8 | * @param array $config Options to pass to the constructor/guzzle |
|
348 | * @return \Akamai\Open\EdgeGrid\Client |
||
349 | */ |
||
350 | 8 | View Code Duplication | public static function createFromEdgeRcFile($section = 'default', $path = null, array $config = []) |
360 | 161 | ||
361 | 161 | /** |
|
362 | * Print HTTP requests/responses to STDOUT |
||
363 | * |
||
364 | * @param bool|resource $enable |
||
365 | */ |
||
366 | public static function setDebug($enable) |
||
370 | 169 | ||
371 | 169 | /** |
|
372 | * Print formatted JSON responses to STDOUT |
||
373 | * |
||
374 | * @param bool|resource|array $enable |
||
375 | */ |
||
376 | public static function setVerbose($enable) |
||
380 | |||
381 | 105 | /** |
|
382 | 2 | * Handle debug option |
|
383 | * |
||
384 | * @param array $config |
||
385 | 103 | * @return bool|resource |
|
386 | 5 | */ |
|
387 | 98 | protected function getDebugOption(array $config) |
|
401 | 105 | ||
402 | 91 | /** |
|
403 | * Debugging status for the current request |
||
404 | * |
||
405 | 16 | * @return bool|resource |
|
406 | 7 | */ |
|
407 | protected function isDebug() |
||
419 | 105 | ||
420 | 91 | /** |
|
421 | * Verbose status for the current request |
||
422 | * |
||
423 | 14 | * @return array|bool|resource |
|
424 | 2 | */ |
|
425 | protected function isVerbose() |
||
437 | |||
438 | 131 | /** |
|
439 | 131 | * Set the Authentication instance |
|
440 | 113 | * |
|
441 | * @param array $config |
||
442 | * @param Authentication|null $authentication |
||
443 | 131 | */ |
|
444 | 42 | protected function setAuthentication(array $config, Authentication $authentication = null) |
|
459 | 131 | ||
460 | /** |
||
461 | 131 | * Set the Authentication Handler |
|
462 | * |
||
463 | 131 | * @param array $config |
|
464 | 131 | * @param Authentication|null $authentication |
|
465 | 131 | * @return array |
|
466 | 29 | */ |
|
467 | protected function setAuthenticationHandler(array $config, Authentication $authentication = null) |
||
487 | |||
488 | 131 | /** |
|
489 | 129 | * Set timeout and base_uri options |
|
490 | * |
||
491 | * @param array $config |
||
492 | 131 | * @return mixed |
|
493 | 2 | */ |
|
494 | 2 | protected function setBasicOptions(array $config) |
|
506 | |||
507 | /** |
||
508 | * Set values on the private \GuzzleHttp\Client->config |
||
509 | * |
||
510 | * This is a terrible hack, and illustrates why making |
||
511 | * anything private makes it difficult to extend, and impossible |
||
512 | 4 | * when there is no setter. |
|
513 | * |
||
514 | 4 | * @param string $what Config option to set |
|
515 | 4 | * @param mixed $value Value to set the option to |
|
516 | * @return void |
||
517 | 4 | */ |
|
518 | 4 | protected function setConfigOption($what, $value) |
|
528 | 16 | ||
529 | /** |
||
530 | * Add the Debug handler to the HandlerStack |
||
531 | 16 | * |
|
532 | 6 | * @param array $options Guzzle Options |
|
533 | * @param bool|resource|null $fp Stream to write to |
||
534 | * @return array |
||
535 | 16 | */ |
|
536 | protected function setDebugHandler($options, $fp = null) |
||
571 | 24 | ||
572 | /** |
||
573 | * Add the Log handler to the HandlerStack |
||
574 | 24 | * |
|
575 | 6 | * @param \GuzzleHttp\HandlerStack $handlerStack |
|
576 | * @param callable $logHandler |
||
577 | 6 | * @return $this |
|
578 | */ |
||
579 | protected function setLogHandler(\GuzzleHttp\HandlerStack $handlerStack, callable $logHandler) |
||
593 | 14 | ||
594 | /** |
||
595 | * Add the Verbose handler to the HandlerStack |
||
596 | 14 | * |
|
597 | 11 | * @param array $options Guzzle Options |
|
598 | 3 | * @param bool|resource|array|null $fp Stream to write to |
|
599 | 1 | * @return array |
|
600 | */ |
||
601 | protected function setVerboseHandler($options, $fp = null) |
||
638 | |||
639 | 105 | /** |
|
640 | * Set request specific options |
||
641 | 105 | * |
|
642 | 63 | * @param array $options |
|
643 | * @return array |
||
644 | */ |
||
645 | 105 | protected function setRequestOptions(array $options) |
|
677 | } |
||
678 |
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.