awurth /
PHP-Config
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
| 1 | <?php |
||
| 2 | |||
| 3 | /* |
||
| 4 | * This file is part of the awurth/config package. |
||
| 5 | * |
||
| 6 | * (c) Alexis Wurth <[email protected]> |
||
| 7 | * |
||
| 8 | * For the full copyright and license information, please view the LICENSE |
||
| 9 | * file that was distributed with this source code. |
||
| 10 | */ |
||
| 11 | |||
| 12 | namespace AWurth\Config; |
||
| 13 | |||
| 14 | use AWurth\Config\Loader\JsonFileLoader; |
||
| 15 | use AWurth\Config\Loader\PhpFileLoader; |
||
| 16 | use AWurth\Config\Loader\YamlFileLoader; |
||
| 17 | use Symfony\Component\Config\ConfigCache; |
||
| 18 | use Symfony\Component\Config\ConfigCacheInterface; |
||
| 19 | use Symfony\Component\Config\Loader\DelegatingLoader; |
||
| 20 | use Symfony\Component\Config\Loader\LoaderInterface; |
||
| 21 | use Symfony\Component\Config\Loader\LoaderResolver; |
||
| 22 | use Symfony\Component\Config\Resource\FileResource; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Configuration Loader. |
||
| 26 | * |
||
| 27 | * @author Alexis Wurth <[email protected]> |
||
| 28 | */ |
||
| 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) |
||
| 115 | { |
||
| 116 | $this->initLoader(); |
||
| 117 | |||
| 118 | $this->parseFile($file); |
||
| 119 | |||
| 120 | $configuration = $this->mergeConfiguration(); |
||
| 121 | |||
| 122 | View Code Duplication | if ($this->options->areParametersEnabled()) { |
|
|
0 ignored issues
–
show
|
|||
| 123 | if (isset($configuration[$this->options->getParametersKey()])) { |
||
| 124 | $this->mergeParameters($configuration[$this->options->getParametersKey()]); |
||
| 125 | } |
||
| 126 | |||
| 127 | $this->replacePlaceholders($configuration); |
||
| 128 | } |
||
| 129 | |||
| 130 | return $configuration; |
||
| 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() |
||
| 258 | { |
||
| 259 | if (null === $this->loader) { |
||
| 260 | $this->addLoader(new PhpFileLoader()); |
||
| 261 | $this->addLoader(new YamlFileLoader()); |
||
| 262 | $this->addLoader(new JsonFileLoader()); |
||
| 263 | |||
| 264 | $this->loader = new DelegatingLoader(new LoaderResolver($this->loaders)); |
||
| 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) |
||
| 298 | { |
||
| 299 | if ($this->options->areParametersEnabled()) { |
||
| 300 | $this->replaceStringPlaceholders($path); |
||
| 301 | } |
||
| 302 | |||
| 303 | if ($this->isAbsolutePath($path) && file_exists($path)) { |
||
| 304 | $this->parseFile($path, $key); |
||
| 305 | } else { |
||
| 306 | $this->parseFile(dirname($originalFile).DIRECTORY_SEPARATOR.$path, $key); |
||
| 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) |
||
| 364 | { |
||
| 365 | $values = $this->loader->load($file); |
||
| 366 | |||
| 367 | if (!empty($values)) { |
||
| 368 | View Code Duplication | if ($this->options->areParametersEnabled() && isset($values[$this->options->getParametersKey()])) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. Loading history...
|
|||
| 369 | $this->replacePlaceholders($values[$this->options->getParametersKey()]); |
||
| 370 | $this->mergeParameters($values[$this->options->getParametersKey()]); |
||
| 371 | } |
||
| 372 | |||
| 373 | if ($this->options->areImportsEnabled()) { |
||
| 374 | $this->loadImports($values, $file); |
||
| 375 | } |
||
| 376 | |||
| 377 | $this->configurations[] = null !== $key ? [$key => $values] : $values; |
||
| 378 | $this->resources[] = new FileResource($file); |
||
| 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) |
||
| 388 | { |
||
| 389 | array_walk_recursive($configuration, [$this, 'replaceStringPlaceholders']); |
||
| 390 | } |
||
| 391 | |||
| 392 | /** |
||
| 393 | * Replaces configuration placeholders with the corresponding parameters values. |
||
| 394 | * |
||
| 395 | * @param string $string |
||
| 396 | */ |
||
| 397 | protected function replaceStringPlaceholders(&$string) |
||
| 398 | { |
||
| 399 | if (is_string($string)) { |
||
| 400 | if (preg_match('/^%([0-9A-Za-z._-]+)%$/', $string, $matches)) { |
||
| 401 | if (isset($this->parameters[$matches[1]])) { |
||
| 402 | $string = $this->parameters[$matches[1]]; |
||
| 403 | } |
||
| 404 | } else { |
||
| 405 | $string = preg_replace_callback('/%([0-9A-Za-z._-]+)%/', function ($matches) { |
||
| 406 | if (isset($this->parameters[$matches[1]]) && !in_array(gettype($this->parameters[$matches[1]]), ['object', 'array'])) { |
||
| 407 | return $this->parameters[$matches[1]]; |
||
| 408 | } else { |
||
| 409 | return $matches[0]; |
||
| 410 | } |
||
| 411 | }, $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) |
||
| 424 | { |
||
| 425 | return require $file; |
||
| 426 | } |
||
| 427 | } |
||
| 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.