| Total Complexity | 57 |
| Total Lines | 397 |
| Duplicated Lines | 2.77 % |
| Changes | 0 | ||
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 ConfigurationLoader 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.
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 ConfigurationLoader, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 29 | class ConfigurationLoader |
||
| 30 | { |
||
| 31 | /** |
||
| 32 | * @var ConfigCacheInterface |
||
| 33 | */ |
||
| 34 | protected $cache; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * @var array |
||
| 38 | */ |
||
| 39 | protected $configurations; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * @var LoaderInterface |
||
| 43 | */ |
||
| 44 | protected $loader; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * @var LoaderInterface[] |
||
| 48 | */ |
||
| 49 | protected $loaders; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * @var Options |
||
| 53 | */ |
||
| 54 | protected $options; |
||
| 55 | |||
| 56 | /** |
||
| 57 | * @var array |
||
| 58 | */ |
||
| 59 | protected $parameters; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * @var FileResource[] |
||
| 63 | */ |
||
| 64 | protected $resources; |
||
| 65 | |||
| 66 | /** |
||
| 67 | * Constructor. |
||
| 68 | * |
||
| 69 | * @param string $cachePath |
||
| 70 | * @param bool $debug |
||
| 71 | */ |
||
| 72 | public function __construct($cachePath = null, $debug = false) |
||
| 73 | { |
||
| 74 | $this->configurations = []; |
||
| 75 | $this->resources = []; |
||
| 76 | $this->parameters = []; |
||
| 77 | $this->options = new Options(); |
||
| 78 | |||
| 79 | if (null !== $cachePath) { |
||
| 80 | $this->cache = new ConfigCache($cachePath, $debug); |
||
| 81 | } |
||
| 82 | } |
||
| 83 | |||
| 84 | /** |
||
| 85 | * Loads the configuration from a cache file if it exists, or parses a configuration file if not. |
||
| 86 | * |
||
| 87 | * @param string $file |
||
| 88 | * |
||
| 89 | * @return array |
||
| 90 | */ |
||
| 91 | public function load($file) |
||
| 92 | { |
||
| 93 | if (null !== $this->cache) { |
||
| 94 | if (!$this->cache->isFresh()) { |
||
| 95 | $configuration = $this->loadFile($file); |
||
| 96 | $this->export($configuration); |
||
| 97 | |||
| 98 | return $configuration; |
||
| 99 | } |
||
| 100 | |||
| 101 | return self::requireFile($this->cache->getPath()); |
||
| 102 | } |
||
| 103 | |||
| 104 | return $this->loadFile($file); |
||
| 105 | } |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Loads the configuration from a file. |
||
| 109 | * |
||
| 110 | * @param string $file |
||
| 111 | * |
||
| 112 | * @return array |
||
| 113 | */ |
||
| 114 | public function loadFile($file) |
||
| 131 | } |
||
| 132 | |||
| 133 | /** |
||
| 134 | * Exports the configuration to a cache file. |
||
| 135 | * |
||
| 136 | * @param array $configuration |
||
| 137 | */ |
||
| 138 | public function export(array $configuration) |
||
| 139 | { |
||
| 140 | $content = '<?php'.PHP_EOL.PHP_EOL.'return '.var_export($configuration, true).';'.PHP_EOL; |
||
| 141 | |||
| 142 | $this->cache->write($content, $this->resources); |
||
| 143 | } |
||
| 144 | |||
| 145 | /** |
||
| 146 | * Gets the configuration cache. |
||
| 147 | * |
||
| 148 | * @return ConfigCacheInterface |
||
| 149 | */ |
||
| 150 | public function getCache() |
||
| 151 | { |
||
| 152 | return $this->cache; |
||
| 153 | } |
||
| 154 | |||
| 155 | /** |
||
| 156 | * Sets the configuration cache. |
||
| 157 | * |
||
| 158 | * @param ConfigCacheInterface $cache |
||
| 159 | */ |
||
| 160 | public function setCache(ConfigCacheInterface $cache) |
||
| 161 | { |
||
| 162 | $this->cache = $cache; |
||
| 163 | } |
||
| 164 | |||
| 165 | /** |
||
| 166 | * Gets the file loaders. |
||
| 167 | * |
||
| 168 | * @return LoaderInterface[] |
||
| 169 | */ |
||
| 170 | public function getLoaders() |
||
| 171 | { |
||
| 172 | return $this->loaders; |
||
| 173 | } |
||
| 174 | |||
| 175 | /** |
||
| 176 | * Adds a file loader. |
||
| 177 | * |
||
| 178 | * @param LoaderInterface $loader |
||
| 179 | * |
||
| 180 | * @return self |
||
| 181 | */ |
||
| 182 | public function addLoader(LoaderInterface $loader) |
||
| 183 | { |
||
| 184 | $this->loaders[] = $loader; |
||
| 185 | |||
| 186 | return $this; |
||
| 187 | } |
||
| 188 | |||
| 189 | /** |
||
| 190 | * Sets the file loaders. |
||
| 191 | * |
||
| 192 | * @param LoaderInterface[] $loaders |
||
| 193 | */ |
||
| 194 | public function setLoaders(array $loaders) |
||
| 195 | { |
||
| 196 | $this->loaders = $loaders; |
||
| 197 | } |
||
| 198 | |||
| 199 | /** |
||
| 200 | * Gets the parameters. |
||
| 201 | * |
||
| 202 | * @return array |
||
| 203 | */ |
||
| 204 | public function getParameters() |
||
| 205 | { |
||
| 206 | return $this->parameters; |
||
| 207 | } |
||
| 208 | |||
| 209 | /** |
||
| 210 | * Sets a parameter's value. |
||
| 211 | * |
||
| 212 | * @param string $name |
||
| 213 | * @param mixed $value |
||
| 214 | * |
||
| 215 | * @return self |
||
| 216 | */ |
||
| 217 | public function setParameter($name, $value) |
||
| 218 | { |
||
| 219 | $this->parameters[$name] = $value; |
||
| 220 | |||
| 221 | return $this; |
||
| 222 | } |
||
| 223 | |||
| 224 | /** |
||
| 225 | * Sets the parameters. |
||
| 226 | * |
||
| 227 | * @param array $parameters |
||
| 228 | */ |
||
| 229 | public function setParameters(array $parameters) |
||
| 230 | { |
||
| 231 | $this->parameters = $parameters; |
||
| 232 | } |
||
| 233 | |||
| 234 | /** |
||
| 235 | * Gets the options. |
||
| 236 | * |
||
| 237 | * @return Options |
||
| 238 | */ |
||
| 239 | public function getOptions() |
||
| 240 | { |
||
| 241 | return $this->options; |
||
| 242 | } |
||
| 243 | |||
| 244 | /** |
||
| 245 | * Sets the options. |
||
| 246 | * |
||
| 247 | * @param Options $options |
||
| 248 | */ |
||
| 249 | public function setOptions(Options $options) |
||
| 250 | { |
||
| 251 | $this->options = $options; |
||
| 252 | } |
||
| 253 | |||
| 254 | /** |
||
| 255 | * Initializes the file loader. |
||
| 256 | */ |
||
| 257 | protected function initLoader() |
||
| 265 | } |
||
| 266 | } |
||
| 267 | |||
| 268 | /** |
||
| 269 | * Returns whether the file path is an absolute path. |
||
| 270 | * |
||
| 271 | * @param string $file |
||
| 272 | * |
||
| 273 | * @return bool |
||
| 274 | */ |
||
| 275 | protected function isAbsolutePath($file) |
||
| 276 | { |
||
| 277 | if ('/' === $file[0] || '\\' === $file[0] |
||
| 278 | || (strlen($file) > 3 && ctype_alpha($file[0]) |
||
| 279 | && ':' === $file[1] |
||
| 280 | && ('\\' === $file[2] || '/' === $file[2]) |
||
| 281 | ) |
||
| 282 | || null !== parse_url($file, PHP_URL_SCHEME) |
||
| 283 | ) { |
||
| 284 | return true; |
||
| 285 | } |
||
| 286 | |||
| 287 | return false; |
||
| 288 | } |
||
| 289 | |||
| 290 | /** |
||
| 291 | * Loads an imported file. |
||
| 292 | * |
||
| 293 | * @param string $path |
||
| 294 | * @param string $originalFile |
||
| 295 | * @param string|null $key |
||
| 296 | */ |
||
| 297 | protected function loadImport($path, $originalFile, $key = null) |
||
| 307 | } |
||
| 308 | } |
||
| 309 | |||
| 310 | /** |
||
| 311 | * Loads file imports recursively. |
||
| 312 | * |
||
| 313 | * @param array $values |
||
| 314 | * @param string|null $originalFile |
||
| 315 | */ |
||
| 316 | protected function loadImports(&$values, $originalFile = null) |
||
| 317 | { |
||
| 318 | if (isset($values[$this->options->getImportsKey()])) { |
||
| 319 | $imports = $values[$this->options->getImportsKey()]; |
||
| 320 | |||
| 321 | if (is_string($imports)) { |
||
| 322 | $this->loadImport($imports, $originalFile); |
||
| 323 | } elseif (is_array($imports)) { |
||
| 324 | foreach ($imports as $key => $file) { |
||
| 325 | $this->loadImport($file, $originalFile, is_string($key) ? $key : null); |
||
| 326 | } |
||
| 327 | } |
||
| 328 | } |
||
| 329 | |||
| 330 | unset($values[$this->options->getImportsKey()]); |
||
| 331 | } |
||
| 332 | |||
| 333 | /** |
||
| 334 | * Merges all loaded configurations into a single array. |
||
| 335 | * |
||
| 336 | * @return array |
||
| 337 | */ |
||
| 338 | protected function mergeConfiguration() |
||
| 339 | { |
||
| 340 | if (count($this->configurations) > 1) { |
||
| 341 | return call_user_func_array('array_replace_recursive', $this->configurations); |
||
| 342 | } |
||
| 343 | |||
| 344 | return $this->configurations[0]; |
||
| 345 | } |
||
| 346 | |||
| 347 | /** |
||
| 348 | * Merges new parameters with existing ones. |
||
| 349 | * |
||
| 350 | * @param array $parameters |
||
| 351 | */ |
||
| 352 | protected function mergeParameters(array $parameters) |
||
| 353 | { |
||
| 354 | $this->parameters = array_replace_recursive($this->parameters, $parameters); |
||
| 355 | } |
||
| 356 | |||
| 357 | /** |
||
| 358 | * Parses a configuration file. |
||
| 359 | * |
||
| 360 | * @param string $file |
||
| 361 | * @param string $key |
||
| 362 | */ |
||
| 363 | protected function parseFile($file, $key = null) |
||
| 379 | } |
||
| 380 | } |
||
| 381 | |||
| 382 | /** |
||
| 383 | * Parses the configuration and replaces placeholders with the corresponding parameters values. |
||
| 384 | * |
||
| 385 | * @param array $configuration |
||
| 386 | */ |
||
| 387 | protected function replacePlaceholders(array &$configuration) |
||
| 390 | } |
||
| 391 | |||
| 392 | /** |
||
| 393 | * Replaces configuration placeholders with the corresponding parameters values. |
||
| 394 | * |
||
| 395 | * @param string $string |
||
| 396 | */ |
||
| 397 | protected function replaceStringPlaceholders(&$string) |
||
| 412 | } |
||
| 413 | } |
||
| 414 | } |
||
| 415 | |||
| 416 | /** |
||
| 417 | * Includes a PHP file. |
||
| 418 | * |
||
| 419 | * @param string $file |
||
| 420 | * |
||
| 421 | * @return array |
||
| 422 | */ |
||
| 423 | private static function requireFile($file) |
||
| 428 |
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.