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 API 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 API, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
11 | class API |
||
12 | { |
||
13 | /** |
||
14 | * API URl |
||
15 | * @var string $api_url |
||
16 | */ |
||
17 | protected $api_url = 'https://api.copy.com'; |
||
18 | |||
19 | /** |
||
20 | * Instance of curl |
||
21 | * @var resource $curl |
||
22 | */ |
||
23 | private $curl; |
||
24 | |||
25 | /** |
||
26 | * @var array |
||
27 | * User data |
||
28 | */ |
||
29 | private $signature; |
||
30 | |||
31 | /** |
||
32 | * Constructor |
||
33 | * |
||
34 | * @param string $consumerKey OAuth consumer key |
||
35 | * @param string $consumerSecret OAuth consumer secret |
||
36 | * @param string $accessToken OAuth access token |
||
37 | * @param string $tokenSecret OAuth token secret |
||
38 | */ |
||
39 | 12 | public function __construct($consumerKey, $consumerSecret, $accessToken, $tokenSecret) |
|
51 | |||
52 | /** |
||
53 | * Wakeup function on unserialize |
||
54 | * |
||
55 | */ |
||
56 | 12 | private function __wakeup() |
|
76 | |||
77 | /** |
||
78 | * Upload a file from a string |
||
79 | * |
||
80 | * @param string $path full path containing leading slash and file name |
||
81 | * @param string $data binary data |
||
82 | * |
||
83 | * @return object described in createFile() |
||
84 | */ |
||
85 | 1 | public function uploadFromString($path, $data) |
|
99 | |||
100 | /** |
||
101 | * Upload a file from a stream resource |
||
102 | * |
||
103 | * @param string $path full path containing leading slash and file name |
||
104 | * @param resource $stream resource to read data from |
||
105 | * |
||
106 | * @return object described in createFile() |
||
107 | */ |
||
108 | 1 | public function uploadFromStream($path, $stream) |
|
136 | |||
137 | /** |
||
138 | * Read a file to a string |
||
139 | * |
||
140 | * @param string $path full path containing leading slash and file name |
||
141 | * |
||
142 | * @return array contains key of contents which contains binary data of the file |
||
143 | */ |
||
144 | 1 | public function readToString($path) |
|
153 | |||
154 | /** |
||
155 | * Read a file to a stream |
||
156 | * |
||
157 | * @param string $path full path containing leading slash and file name |
||
158 | * |
||
159 | * @return array contains key of stream which contains a stream resource |
||
160 | */ |
||
161 | 1 | public function readToStream($path) |
|
190 | |||
191 | /** |
||
192 | * Send a request to remove a given file. |
||
193 | * |
||
194 | * @param string $path full path containing leading slash and file name |
||
195 | * |
||
196 | * @return bool true if the file was removed successfully |
||
197 | */ |
||
198 | 2 | public function removeFile($path) |
|
202 | |||
203 | /** |
||
204 | * Send a request to remove a given dir. |
||
205 | * |
||
206 | * @param string $path full path containing leading slash and dir name |
||
207 | * |
||
208 | * @return bool true if the dir was removed successfully |
||
209 | */ |
||
210 | 1 | public function removeDir($path) |
|
214 | |||
215 | /** |
||
216 | * Send a request to remove a given item. |
||
217 | * |
||
218 | * @param string $path full path containing leading slash and file name |
||
219 | * @param string $type file or dir |
||
220 | * |
||
221 | * @return bool true if the item was removed successfully |
||
222 | */ |
||
223 | 3 | private function removeItem($path, $type) |
|
232 | |||
233 | /** |
||
234 | * Rename a file |
||
235 | * |
||
236 | * Object structure: |
||
237 | * { |
||
238 | * object_id: "4008" |
||
239 | * path: "/example" |
||
240 | * type: "dir" || "file" |
||
241 | * share_id: "0" |
||
242 | * share_owner: "21956799" |
||
243 | * company_id: NULL |
||
244 | * size: filesize in bytes, 0 for folders |
||
245 | * created_time: unix timestamp, e.g. "1389731126" |
||
246 | * modified_time: unix timestamp, e.g. "1389731126" |
||
247 | * date_last_synced: unix timestamp, e.g. "1389731126" |
||
248 | * removed_time: unix timestamp, e.g. "1389731126" or empty string for non-deleted files/folders |
||
249 | * mime_type: string |
||
250 | * revisions: array of revision objects |
||
251 | * } |
||
252 | * |
||
253 | * @param string $source_path full path containing leading slash and file name |
||
254 | * @param string $destination_path full path containing leading slash and file name |
||
255 | * |
||
256 | * @return stdClass using structure as noted above |
||
257 | */ |
||
258 | 1 | public function rename($source_path, $destination_path) |
|
262 | |||
263 | /** |
||
264 | * Copy an item |
||
265 | * |
||
266 | * Object structure: |
||
267 | * { |
||
268 | * object_id: "4008" |
||
269 | * path: "/example" |
||
270 | * type: "dir" || "file" |
||
271 | * share_id: "0" |
||
272 | * share_owner: "21956799" |
||
273 | * company_id: NULL |
||
274 | * size: filesize in bytes, 0 for folders |
||
275 | * created_time: unix timestamp, e.g. "1389731126" |
||
276 | * modified_time: unix timestamp, e.g. "1389731126" |
||
277 | * date_last_synced: unix timestamp, e.g. "1389731126" |
||
278 | * removed_time: unix timestamp, e.g. "1389731126" or empty string for non-deleted files/folders |
||
279 | * mime_type: string |
||
280 | * revisions: array of revision objects |
||
281 | * } |
||
282 | * |
||
283 | * @param string $source_path full path containing leading slash and file name |
||
284 | * @param string $destination_path full path containing leading slash and file name |
||
285 | * |
||
286 | * @return stdClass using structure as noted above |
||
287 | */ |
||
288 | 1 | public function copy($source_path, $destination_path) |
|
292 | |||
293 | /** |
||
294 | * List objects within a path |
||
295 | * |
||
296 | * Object structure: |
||
297 | * { |
||
298 | * object_id: "4008" |
||
299 | * path: "/example" |
||
300 | * type: "dir" || "file" |
||
301 | * share_id: "0" |
||
302 | * share_owner: "21956799" |
||
303 | * company_id: NULL |
||
304 | * size: filesize in bytes, 0 for folders |
||
305 | * created_time: unix timestamp, e.g. "1389731126" |
||
306 | * modified_time: unix timestamp, e.g. "1389731126" |
||
307 | * date_last_synced: unix timestamp, e.g. "1389731126" |
||
308 | * removed_time: unix timestamp, e.g. "1389731126" or empty string for non-deleted files/folders |
||
309 | * mime_type: string |
||
310 | * revisions: array of revision objects |
||
311 | * } |
||
312 | * |
||
313 | * @param string $path full path with leading slash and optionally a filename |
||
314 | * @param array $additionalOptions used for passing options such as include_parts |
||
315 | * |
||
316 | * @return array List of file/folder objects described above. |
||
317 | */ |
||
318 | 3 | public function listPath($path, $additionalOptions = null) |
|
346 | |||
347 | /** |
||
348 | * Get directory or file meta data |
||
349 | * |
||
350 | * Object structure: |
||
351 | * { |
||
352 | * id: "/copy/example" |
||
353 | * path: "/example" |
||
354 | * name: "example", |
||
355 | * type: "dir" || "file" |
||
356 | * share_id: "0" |
||
357 | * share_owner: "21956799" |
||
358 | * company_id: NULL |
||
359 | * size: filesize in bytes, 0 for folders |
||
360 | * created_time: unix timestamp, e.g. "1389731126" |
||
361 | * modified_time: unix timestamp, e.g. "1389731126" |
||
362 | * date_last_synced: unix timestamp, e.g. "1389731126" |
||
363 | * removed_time: unix timestamp, e.g. "1389731126" or empty string for non-deleted files/folders |
||
364 | * mime_type: string |
||
365 | * revisions: array of revision objects |
||
366 | * children: array of children objects |
||
367 | * } |
||
368 | * |
||
369 | * @param string $path full path with leading slash and optionally a filename |
||
370 | * @param string $root Optional, "copy" is the first level of the real filesystem |
||
371 | * |
||
372 | * @return array List of file/folder objects described above. |
||
373 | */ |
||
374 | 1 | public function getMeta($path, $root = "copy") |
|
392 | |||
393 | /** |
||
394 | * Create a dir |
||
395 | * |
||
396 | * Object structure: |
||
397 | * { |
||
398 | * object_id: "4008" |
||
399 | * path: "/example" |
||
400 | * type: "dir" |
||
401 | * share_id: "0" |
||
402 | * share_owner: "21956799" |
||
403 | * company_id: NULL |
||
404 | * size: filesize in bytes, 0 for folders |
||
405 | * created_time: unix timestamp, e.g. "1389731126" |
||
406 | * modified_time: unix timestamp, e.g. "1389731126" |
||
407 | * date_last_synced: unix timestamp, e.g. "1389731126" |
||
408 | * removed_time: unix timestamp, e.g. "1389731126" or empty string for non-deleted files/folders |
||
409 | * } |
||
410 | * |
||
411 | * @param string $path full path containing leading slash and dir name |
||
412 | * @param bool $recursive true to create parent directories |
||
413 | * |
||
414 | * @return object described above. |
||
415 | */ |
||
416 | 1 | public function createDir($path, $recursive = true) |
|
425 | |||
426 | /** |
||
427 | * Create a file with a set of data parts |
||
428 | * |
||
429 | * Object structure: |
||
430 | * { |
||
431 | * object_id: "4008" |
||
432 | * path: "/example" |
||
433 | * type: "file" |
||
434 | * share_id: "0" |
||
435 | * share_owner: "21956799" |
||
436 | * company_id: NULL |
||
437 | * size: filesize in bytes, 0 for folders |
||
438 | * created_time: unix timestamp, e.g. "1389731126" |
||
439 | * modified_time: unix timestamp, e.g. "1389731126" |
||
440 | * date_last_synced: unix timestamp, e.g. "1389731126" |
||
441 | * removed_time: unix timestamp, e.g. "1389731126" or empty string for non-deleted files/folders |
||
442 | * mime_type: string |
||
443 | * revisions: array of revision objects |
||
444 | * } |
||
445 | * |
||
446 | * @param string $path full path containing leading slash and file name |
||
447 | * @param array $parts contains arrays of parts returned by \Barracuda\Copy\API\sendData |
||
448 | * |
||
449 | * @return object described above. |
||
450 | */ |
||
451 | 2 | public function createFile($path, $parts) |
|
474 | |||
475 | /** |
||
476 | * Generate the fingerprint for a string of data. |
||
477 | * |
||
478 | * @param string $data Data part to generate the fingerprint for. |
||
479 | * |
||
480 | * @return string Fingerprint for $data. |
||
481 | **/ |
||
482 | 2 | public function fingerprint($data) |
|
486 | |||
487 | /** |
||
488 | * Send a piece of data |
||
489 | * |
||
490 | * @param string $data binary data |
||
491 | * @param int $shareId setting this to zero is best, unless share id is known |
||
492 | * |
||
493 | * @return array contains fingerprint and size, to be used when creating a file |
||
494 | */ |
||
495 | 2 | public function sendData($data, $shareId = 0) |
|
509 | |||
510 | /** |
||
511 | * Send a data part |
||
512 | * |
||
513 | * @param string $fingerprint md5 and sha1 concatenated |
||
514 | * @param int $size number of bytes |
||
515 | * @param string $data binary data |
||
516 | * @param int $shareId setting this to zero is best, unless share id is known |
||
517 | * |
||
518 | */ |
||
519 | 1 | public function sendPart($fingerprint, $size, $data, $shareId = 0) |
|
543 | |||
544 | /** |
||
545 | * Check to see if a part already exists |
||
546 | * |
||
547 | * @param string $fingerprint md5 and sha1 concatenated |
||
548 | * @param int $size number of bytes |
||
549 | * @param int $shareId setting this to zero is best, unless share id is known |
||
550 | * @return bool true if part already exists |
||
551 | */ |
||
552 | 2 | public function hasPart($fingerprint, $size, $shareId = 0) |
|
577 | |||
578 | /** |
||
579 | * Get a part |
||
580 | * |
||
581 | * @param string $fingerprint md5 and sha1 concatinated |
||
582 | * @param int $size number of bytes |
||
583 | * @param int $shareId setting this to zero is best, unless share id is known |
||
584 | * |
||
585 | * @return string binary data |
||
586 | */ |
||
587 | 2 | public function getPart($fingerprint, $size, $shareId = 0) |
|
637 | |||
638 | /** |
||
639 | * Create a New Link |
||
640 | * |
||
641 | * Object structure: |
||
642 | * { |
||
643 | * id: "MBrss3roGDk4", |
||
644 | * name: "My Cool Shared Files", |
||
645 | * public: true, |
||
646 | * url: "https://copy.com/MBrss3roGDk4", |
||
647 | * url_short: "https://copy.com/MBrss3roGDk4", |
||
648 | * creator_id: "1381231", |
||
649 | * company_id: null, |
||
650 | * confirmation_required: false, |
||
651 | * status: "viewed", |
||
652 | * permissions: "read" |
||
653 | * } |
||
654 | * |
||
655 | * @param array|string $paths target item(s) path |
||
656 | * @param array $options option attributes, (bool) "public", (string) "name" |
||
657 | * @param string $root |
||
658 | * |
||
659 | * @throws \Exception |
||
660 | * |
||
661 | * @return object described above. |
||
662 | */ |
||
663 | 1 | public function createLink($paths, $options = array(), $root = 'copy') |
|
685 | |||
686 | /** |
||
687 | * Update meta object |
||
688 | * |
||
689 | * Object structure: |
||
690 | * { |
||
691 | * object_id: "4008" |
||
692 | * path: "/example" |
||
693 | * type: "dir" || "file" |
||
694 | * share_id: "0" |
||
695 | * share_owner: "21956799" |
||
696 | * company_id: NULL |
||
697 | * size: filesize in bytes, 0 for folders |
||
698 | * created_time: unix timestamp, e.g. "1389731126" |
||
699 | * modified_time: unix timestamp, e.g. "1389731126" |
||
700 | * date_last_synced: unix timestamp, e.g. "1389731126" |
||
701 | * removed_time: unix timestamp, e.g. "1389731126" or empty string for non-deleted files/folders |
||
702 | * mime_type: string |
||
703 | * revisions: array of revision objects |
||
704 | * } |
||
705 | * |
||
706 | * @param string $action |
||
707 | * @param string $path |
||
708 | * @param array $meta contains action, path, and other attributes of the object to update |
||
709 | * |
||
710 | * @return stdClass using structure as noted above |
||
711 | */ |
||
712 | 8 | private function updateObject($action, $path, $meta) |
|
723 | |||
724 | /** |
||
725 | * Create and execute cURL request to send data. |
||
726 | * |
||
727 | * @param string $method API method |
||
728 | * @param string $data raw request |
||
729 | * @param boolean $decodeResponse true to decode response |
||
730 | * |
||
731 | * @return mixed result from curl_exec |
||
732 | */ |
||
733 | 11 | private function post($method, $data, $decodeResponse = false) |
|
760 | |||
761 | /** |
||
762 | * Create and execute cURL request by GET method. |
||
763 | * |
||
764 | * @param string $method API method |
||
765 | * |
||
766 | * @return mixed result from curl_exec |
||
767 | */ |
||
768 | 1 | protected function get($method) |
|
787 | |||
788 | /** |
||
789 | * Return which cloud API end point to use for a given method. |
||
790 | * |
||
791 | * @param string $method API method |
||
792 | * |
||
793 | * @return string uri of endpoint without leading slash |
||
794 | */ |
||
795 | 12 | private function getEndpoint($method) |
|
805 | |||
806 | /** |
||
807 | * Generate the HTTP headers need for a given Cloud API method. |
||
808 | * |
||
809 | * @param string $method API method |
||
810 | * @param string $http_method Optional, HTTP request method |
||
811 | * |
||
812 | * @return array contains headers to use for HTTP requests |
||
813 | */ |
||
814 | 12 | public function getHeaders($method, $http_method = "POST") |
|
841 | |||
842 | /** |
||
843 | * JSON encode request data. |
||
844 | * |
||
845 | * @param string $method Cloud API method |
||
846 | * @param array $json contains data to be encoded |
||
847 | * |
||
848 | * @return string JSON formatted request body |
||
849 | */ |
||
850 | 10 | private function encodeRequest($method, $json) |
|
861 | |||
862 | /** |
||
863 | * Decode a JSON response. |
||
864 | * |
||
865 | * @param string $response JSON response |
||
866 | * |
||
867 | * @return array JSON decoded string |
||
868 | */ |
||
869 | 10 | private function decodeResponse($response) |
|
881 | |||
882 | /** |
||
883 | * Copies the phar cacert from a phar into the temp directory. |
||
884 | * |
||
885 | * @param string $pharCacertPath Path to the phar cacert. |
||
886 | * |
||
887 | * @return string Returns the path to the extracted cacert file. |
||
888 | */ |
||
889 | public static function extractPharCacert($pharCacertPath) |
||
909 | } |
||
910 |
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.