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 |
||
16 | class Api implements ApiInterface |
||
17 | { |
||
18 | ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ |
||
19 | const ERROR_NO_NAME = 'Could not set name for entry'; |
||
20 | const ERROR_NOT_FOUND = 'Not Found'; |
||
21 | |||
22 | const API_GIT_DATA = 'git'; |
||
23 | const API_REPO = 'repo'; |
||
24 | |||
25 | const KEY_BLOB = 'blob'; |
||
26 | const KEY_DIRECTORY = 'dir'; |
||
27 | const KEY_FILE = 'file'; |
||
28 | const KEY_FILENAME = 'basename'; |
||
29 | const KEY_MODE = 'mode'; |
||
30 | const KEY_NAME = 'name'; |
||
31 | const KEY_PATH = 'path'; |
||
32 | const KEY_SHA = 'sha'; |
||
33 | const KEY_SIZE = 'size'; |
||
34 | const KEY_STREAM = 'stream'; |
||
35 | const KEY_TIMESTAMP = 'timestamp'; |
||
36 | const KEY_TREE = 'tree'; |
||
37 | const KEY_TYPE = 'type'; |
||
38 | const KEY_VISIBILITY = 'visibility'; |
||
39 | |||
40 | const GITHUB_API_URL = 'https://api.github.com'; |
||
41 | const GITHUB_URL = 'https://github.com'; |
||
42 | |||
43 | const MIME_TYPE_DIRECTORY = 'directory'; |
||
44 | |||
45 | /** @var Client */ |
||
46 | private $client; |
||
47 | /** @var Contents */ |
||
48 | private $contents; |
||
49 | /** @var SettingsInterface */ |
||
50 | private $settings; |
||
51 | /** @var bool */ |
||
52 | private $isAuthenticationAttempted = false; |
||
53 | |||
54 | //////////////////////////// SETTERS AND GETTERS \\\\\\\\\\\\\\\\\\\\\\\\\\\ |
||
55 | /** |
||
56 | * @param string $name |
||
57 | * |
||
58 | * @return \Github\Api\ApiInterface |
||
59 | * |
||
60 | * @throws \Github\Exception\InvalidArgumentException |
||
61 | */ |
||
62 | private function getApi($name) |
||
67 | |||
68 | /** |
||
69 | * @return GitData |
||
70 | * |
||
71 | * @throws \Github\Exception\InvalidArgumentException |
||
72 | */ |
||
73 | private function getGitDataApi() |
||
77 | |||
78 | /** |
||
79 | * @return Repo |
||
80 | * |
||
81 | * @throws \Github\Exception\InvalidArgumentException |
||
82 | */ |
||
83 | private function getRepositoryApi() |
||
87 | |||
88 | /** |
||
89 | * @return \Github\Api\Repository\Contents |
||
90 | * |
||
91 | * @throws \Github\Exception\InvalidArgumentException |
||
92 | */ |
||
93 | private function getRepositoryContent() |
||
100 | |||
101 | //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ |
||
102 | final public function __construct(Client $client, SettingsInterface $settings) |
||
111 | |||
112 | /** |
||
113 | * @param string $path |
||
114 | * |
||
115 | * @return bool |
||
116 | * |
||
117 | * @throws \Github\Exception\InvalidArgumentException |
||
118 | */ |
||
119 | final public function exists($path) |
||
128 | |||
129 | /** |
||
130 | * @param $path |
||
131 | * |
||
132 | * @return null|string |
||
133 | * @throws \Github\Exception\InvalidArgumentException |
||
134 | * |
||
135 | * @throws \Github\Exception\ErrorException |
||
136 | */ |
||
137 | final public function getFileContents($path) |
||
146 | |||
147 | /** |
||
148 | * @param string $path |
||
149 | * |
||
150 | * @return array |
||
151 | * |
||
152 | * @throws \Github\Exception\InvalidArgumentException |
||
153 | */ |
||
154 | View Code Duplication | final public function getLastUpdatedTimestamp($path) |
|
164 | |||
165 | /** |
||
166 | * @param string $path |
||
167 | * |
||
168 | * @return array |
||
169 | * |
||
170 | * @throws \Github\Exception\InvalidArgumentException |
||
171 | */ |
||
172 | View Code Duplication | final public function getCreatedTimestamp($path) |
|
182 | |||
183 | /** |
||
184 | * @param string $path |
||
185 | * |
||
186 | * @return array|bool |
||
187 | * |
||
188 | * @throws \Github\Exception\InvalidArgumentException |
||
189 | * @throws \Github\Exception\RuntimeException |
||
190 | */ |
||
191 | final public function getMetaData($path) |
||
241 | |||
242 | /** |
||
243 | * @param string $path |
||
244 | * @param bool $recursive |
||
245 | * |
||
246 | * @return array |
||
247 | * @throws \Github\Exception\InvalidArgumentException |
||
248 | */ |
||
249 | final public function getTreeMetadata($path, $recursive) |
||
290 | |||
291 | /** |
||
292 | * @param string $path |
||
293 | * |
||
294 | * @return null|string |
||
295 | * |
||
296 | * @throws \Github\Exception\ErrorException |
||
297 | * @throws \Github\Exception\InvalidArgumentException |
||
298 | * @throws \Github\Exception\RuntimeException |
||
299 | */ |
||
300 | final public function guessMimeType($path) |
||
314 | |||
315 | ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ |
||
316 | /** |
||
317 | * |
||
318 | * @throws \Github\Exception\InvalidArgumentException If no authentication method was given |
||
319 | */ |
||
320 | private function authenticate() |
||
340 | |||
341 | /** |
||
342 | * @param array $tree |
||
343 | * @param string $path |
||
344 | * @param bool $recursive |
||
345 | * |
||
346 | * @return array |
||
347 | */ |
||
348 | private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) |
||
371 | |||
372 | /** |
||
373 | * @param $permissions |
||
374 | * @return string |
||
375 | */ |
||
376 | private function guessVisibility($permissions) |
||
386 | |||
387 | /** |
||
388 | * @param array $metadata |
||
389 | * |
||
390 | * @return array |
||
391 | */ |
||
392 | private function normalizeTreeMetadata($metadata) |
||
415 | |||
416 | /** |
||
417 | * @param $path |
||
418 | * |
||
419 | * @return array |
||
420 | * |
||
421 | * @throws \Github\Exception\InvalidArgumentException |
||
422 | */ |
||
423 | private function commitsForFile($path) |
||
434 | |||
435 | /** |
||
436 | * @param array $entry |
||
437 | * @param string $key |
||
438 | * @param bool $default |
||
439 | * |
||
440 | * @return mixed |
||
441 | */ |
||
442 | private function setDefaultValue(array &$entry, $key, $default = false) |
||
448 | |||
449 | /** |
||
450 | * @param $entry |
||
451 | */ |
||
452 | private function setEntryType(&$entry) |
||
466 | |||
467 | /** |
||
468 | * @param $entry |
||
469 | */ |
||
470 | private function setEntryVisibility(&$entry) |
||
479 | |||
480 | /** |
||
481 | * @param $entry |
||
482 | */ |
||
483 | private function setEntryName(&$entry) |
||
495 | |||
496 | /** |
||
497 | * @param $metadata |
||
498 | * @return bool |
||
499 | */ |
||
500 | private function isMetadataForDirectory($metadata) |
||
512 | |||
513 | /** |
||
514 | * @param $subject |
||
515 | * @param $key |
||
516 | * @return mixed |
||
517 | */ |
||
518 | private function hasKey(&$subject, $key) |
||
529 | } |
||
530 |
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.