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 Config 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 Config, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
39 | class Config implements ConfigInterface, NamespaceAwareInterface |
||
40 | { |
||
41 | use NamespaceAwareTrait; |
||
42 | |||
43 | /** |
||
44 | * The value that identifies a version order by creation time. |
||
45 | */ |
||
46 | const VERSION_ORDER_CREATION_TIME = 'creation'; |
||
47 | |||
48 | /** |
||
49 | * The value that identifies a version order by execution time. |
||
50 | */ |
||
51 | const VERSION_ORDER_EXECUTION_TIME = 'execution'; |
||
52 | |||
53 | /** |
||
54 | * @var array |
||
55 | */ |
||
56 | private $values = []; |
||
57 | |||
58 | /** |
||
59 | * @var string |
||
60 | */ |
||
61 | protected $configFilePath; |
||
62 | |||
63 | /** |
||
64 | * {@inheritdoc} |
||
65 | */ |
||
66 | 448 | public function __construct(array $configArray, $configFilePath = null) |
|
71 | |||
72 | /** |
||
73 | * Create a new instance of the config class using a Yaml file path. |
||
74 | * |
||
75 | * @param string $configFilePath Path to the Yaml File |
||
76 | * @throws \RuntimeException |
||
77 | * @return \Phinx\Config\Config |
||
78 | */ |
||
79 | 2 | View Code Duplication | public static function fromYaml($configFilePath) |
93 | |||
94 | /** |
||
95 | * Create a new instance of the config class using a JSON file path. |
||
96 | * |
||
97 | * @param string $configFilePath Path to the JSON File |
||
98 | * @throws \RuntimeException |
||
99 | * @return \Phinx\Config\Config |
||
100 | 2 | */ |
|
101 | View Code Duplication | public static function fromJson($configFilePath) |
|
113 | |||
114 | /** |
||
115 | * Create a new instance of the config class using a PHP file path. |
||
116 | * |
||
117 | * @param string $configFilePath Path to the PHP File |
||
118 | * @throws \RuntimeException |
||
119 | 3 | * @return \Phinx\Config\Config |
|
120 | */ |
||
121 | 3 | View Code Duplication | public static function fromPhp($configFilePath) |
139 | |||
140 | /** |
||
141 | 25 | * {@inheritdoc} |
|
142 | */ |
||
143 | 25 | public function getEnvironments() |
|
158 | |||
159 | /** |
||
160 | 25 | * {@inheritdoc} |
|
161 | */ |
||
162 | 25 | public function getEnvironment($name) |
|
177 | |||
178 | /** |
||
179 | 9 | * {@inheritdoc} |
|
180 | */ |
||
181 | 9 | public function hasEnvironment($name) |
|
185 | |||
186 | /** |
||
187 | 20 | * {@inheritdoc} |
|
188 | */ |
||
189 | public function getDefaultEnvironment() |
||
226 | |||
227 | 7 | /** |
|
228 | * {@inheritdoc} |
||
229 | 7 | */ |
|
230 | public function getAlias($alias) |
||
234 | |||
235 | 448 | /** |
|
236 | * {@inheritdoc} |
||
237 | 448 | */ |
|
238 | public function getAliases() |
||
242 | |||
243 | 423 | /** |
|
244 | * {@inheritdoc} |
||
245 | 423 | */ |
|
246 | 1 | public function getConfigFilePath() |
|
250 | 219 | ||
251 | 219 | /** |
|
252 | * {@inheritdoc} |
||
253 | 422 | */ |
|
254 | View Code Duplication | public function getMigrationPaths() |
|
255 | { |
||
256 | if (!isset($this->values['paths']['migrations'])) { |
||
257 | throw new \UnexpectedValueException('Migrations path missing from config file'); |
||
258 | } |
||
259 | |||
260 | if (is_string($this->values['paths']['migrations'])) { |
||
261 | $this->values['paths']['migrations'] = [$this->values['paths']['migrations']]; |
||
262 | 14 | } |
|
263 | |||
264 | 14 | return $this->values['paths']['migrations']; |
|
265 | } |
||
266 | 14 | ||
267 | /** |
||
268 | * Gets the base class name for migrations. |
||
269 | * |
||
270 | * @param bool $dropNamespace Return the base migration class name without the namespace. |
||
271 | * @return string |
||
272 | 48 | */ |
|
273 | public function getMigrationBaseClassName($dropNamespace = true) |
||
279 | 13 | ||
280 | 13 | /** |
|
281 | * {@inheritdoc} |
||
282 | 20 | */ |
|
283 | View Code Duplication | public function getSeedPaths() |
|
284 | { |
||
285 | if (!isset($this->values['paths']['seeds'])) { |
||
286 | throw new \UnexpectedValueException('Seeds path missing from config file'); |
||
287 | } |
||
288 | |||
289 | if (is_string($this->values['paths']['seeds'])) { |
||
290 | 14 | $this->values['paths']['seeds'] = [$this->values['paths']['seeds']]; |
|
291 | } |
||
292 | 14 | ||
293 | 13 | return $this->values['paths']['seeds']; |
|
294 | } |
||
295 | |||
296 | 1 | /** |
|
297 | * Get the template file name. |
||
298 | * |
||
299 | * @return string|false |
||
300 | */ |
||
301 | View Code Duplication | public function getTemplateFile() |
|
309 | |||
310 | 4 | /** |
|
311 | * Get the template class name. |
||
312 | * |
||
313 | * @return string|false |
||
314 | */ |
||
315 | View Code Duplication | public function getTemplateClass() |
|
323 | |||
324 | 222 | /** |
|
325 | * Get the version order. |
||
326 | * |
||
327 | * @return string |
||
328 | */ |
||
329 | public function getVersionOrder() |
||
330 | { |
||
331 | if (!isset($this->values['version_order'])) { |
||
332 | 357 | return self::VERSION_ORDER_CREATION_TIME; |
|
333 | } |
||
334 | 357 | ||
335 | return $this->values['version_order']; |
||
336 | 357 | } |
|
337 | |||
338 | /** |
||
339 | * Is version order creation time? |
||
340 | * |
||
341 | * @return bool |
||
342 | */ |
||
343 | public function isVersionOrderCreationTime() |
||
344 | { |
||
345 | $versionOrder = $this->getVersionOrder(); |
||
346 | |||
347 | 448 | return $versionOrder == self::VERSION_ORDER_CREATION_TIME; |
|
348 | } |
||
349 | |||
350 | /** |
||
351 | 448 | * Get the bootstrap file path |
|
352 | 448 | * |
|
353 | 448 | * @return string|false |
|
354 | 2 | */ |
|
355 | 2 | public function getBootstrapFile() |
|
363 | 448 | ||
364 | /** |
||
365 | * Replace tokens in the specified array. |
||
366 | * |
||
367 | * @param array $arr Array to replace |
||
368 | * @return array |
||
369 | */ |
||
370 | protected function replaceTokens(array $arr) |
||
388 | 43 | ||
389 | 448 | /** |
|
390 | 448 | * Recurse an array for the specified tokens and replace them. |
|
391 | * |
||
392 | * @param array $arr Array to recurse |
||
393 | * @param array $tokens Array of tokens to search for |
||
394 | * @return array |
||
395 | */ |
||
396 | 213 | protected function recurseArrayForTokens($arr, $tokens) |
|
416 | 1 | ||
417 | /** |
||
418 | 1 | * {@inheritdoc} |
|
419 | */ |
||
420 | public function offsetSet($id, $value) |
||
424 | 1 | ||
425 | /** |
||
426 | 1 | * {@inheritdoc} |
|
427 | 1 | */ |
|
428 | public function offsetGet($id) |
||
436 | |||
437 | /** |
||
438 | * {@inheritdoc} |
||
439 | */ |
||
440 | public function offsetExists($id) |
||
444 | |||
445 | /** |
||
446 | * {@inheritdoc} |
||
447 | */ |
||
448 | public function offsetUnset($id) |
||
452 | } |
||
453 |
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.