Total Complexity | 105 |
Total Lines | 565 |
Duplicated Lines | 0 % |
Changes | 2 | ||
Bugs | 0 | Features | 0 |
Complex classes like TApplicationConfiguration 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 TApplicationConfiguration, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
29 | class TApplicationConfiguration extends \Prado\TComponent |
||
30 | { |
||
31 | /** |
||
32 | * The cache name for installed Prado composer packages |
||
33 | */ |
||
34 | public const COMPOSER_INSTALLED_CACHE = 'prado:composer:installedcache'; |
||
35 | /** |
||
36 | * Name of the Extra field to look for Prado Extension Class |
||
37 | */ |
||
38 | public const COMPOSER_EXTRA_CLASS = 'bootstrap'; |
||
39 | /** |
||
40 | * @var array list of application initial property values, indexed by property names |
||
41 | */ |
||
42 | private $_properties = []; |
||
43 | /** |
||
44 | * @var array list of namespaces to be used |
||
45 | */ |
||
46 | private $_usings = []; |
||
47 | /** |
||
48 | * @var array list of path aliases, indexed by alias names |
||
49 | */ |
||
50 | private $_aliases = []; |
||
51 | /** |
||
52 | * @var array list of module configurations |
||
53 | */ |
||
54 | private $_modules = []; |
||
55 | /** |
||
56 | * @var array list of service configurations |
||
57 | */ |
||
58 | private $_services = []; |
||
59 | /** |
||
60 | * @var array list of parameters |
||
61 | */ |
||
62 | private $_parameters = []; |
||
63 | /** |
||
64 | * @var array list of included configurations |
||
65 | */ |
||
66 | private $_includes = []; |
||
67 | /** |
||
68 | * @var bool whether this configuration contains actual stuff |
||
69 | */ |
||
70 | private $_empty = true; |
||
71 | /** |
||
72 | * @var array<string, string> name/id of the composer extension and class for extension |
||
73 | */ |
||
74 | private static $_composerPlugins; |
||
75 | |||
76 | /** |
||
77 | * Parses the application configuration file. |
||
78 | * @param string $fname configuration file name |
||
79 | * @throws TConfigurationException if there is any parsing error |
||
80 | */ |
||
81 | public function loadFromFile($fname) |
||
82 | { |
||
83 | if (Prado::getApplication()->getConfigurationType() == TApplication::CONFIG_TYPE_PHP) { |
||
84 | $fcontent = include $fname; |
||
85 | $this->loadFromPhp($fcontent, dirname($fname)); |
||
86 | } else { |
||
87 | $dom = new TXmlDocument(); |
||
88 | $dom->loadFromFile($fname); |
||
89 | $this->loadFromXml($dom, dirname($fname)); |
||
90 | } |
||
91 | } |
||
92 | |||
93 | /** |
||
94 | * @return bool whether this configuration contains actual stuff |
||
95 | */ |
||
96 | public function getIsEmpty() |
||
97 | { |
||
98 | return $this->_empty; |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * Parses the application configuration given in terms of a PHP array. |
||
103 | * @param array $config the PHP array |
||
104 | * @param string $configPath the context path (for specifying relative paths) |
||
105 | */ |
||
106 | public function loadFromPhp($config, $configPath) |
||
134 | } |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * Parses the application configuration given in terms of a TXmlElement. |
||
139 | * @param \Prado\Xml\TXmlElement $dom the XML element |
||
140 | * @param string $configPath the context path (for specifying relative paths) |
||
141 | */ |
||
142 | public function loadFromXml($dom, $configPath) |
||
143 | { |
||
144 | // application properties |
||
145 | foreach ($dom->getAttributes() as $name => $value) { |
||
146 | $this->_properties[$name] = $value; |
||
147 | $this->_empty = false; |
||
148 | } |
||
149 | |||
150 | foreach ($dom->getElements() as $element) { |
||
151 | switch ($element->getTagName()) { |
||
152 | case 'paths': |
||
153 | $this->loadPathsXml($element, $configPath); |
||
154 | break; |
||
155 | case 'modules': |
||
156 | $this->loadModulesXml($element, $configPath); |
||
157 | break; |
||
158 | case 'services': |
||
159 | $this->loadServicesXml($element, $configPath); |
||
160 | break; |
||
161 | case 'parameters': |
||
162 | $this->loadParametersXml($element, $configPath); |
||
163 | break; |
||
164 | case 'include': |
||
165 | $this->loadExternalXml($element, $configPath); |
||
166 | break; |
||
167 | default: |
||
168 | //throw new TConfigurationException('appconfig_tag_invalid',$element->getTagName()); |
||
169 | break; |
||
170 | } |
||
171 | } |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Loads the paths PHP array |
||
176 | * @param array $pathsNode the paths PHP array |
||
177 | * @param string $configPath the context path (for specifying relative paths) |
||
178 | */ |
||
179 | protected function loadPathsPhp($pathsNode, $configPath) |
||
180 | { |
||
181 | if (isset($pathsNode['aliases']) && is_array($pathsNode['aliases'])) { |
||
182 | foreach ($pathsNode['aliases'] as $id => $path) { |
||
183 | $path = str_replace('\\', '/', $path); |
||
184 | if (preg_match('/^\\/|.:\\/|.:\\\\/', $path)) { // if absolute path |
||
185 | $p = realpath($path); |
||
186 | } else { |
||
187 | $p = realpath($configPath . DIRECTORY_SEPARATOR . $path); |
||
188 | } |
||
189 | if ($p === false || !is_dir($p)) { |
||
190 | throw new TConfigurationException('appconfig_aliaspath_invalid', $id, $path); |
||
191 | } |
||
192 | if (isset($this->_aliases[$id])) { |
||
193 | throw new TConfigurationException('appconfig_alias_redefined', $id); |
||
194 | } |
||
195 | $this->_aliases[$id] = $p; |
||
196 | } |
||
197 | } |
||
198 | |||
199 | if (isset($pathsNode['using']) && is_array($pathsNode['using'])) { |
||
200 | foreach ($pathsNode['using'] as $namespace) { |
||
201 | $this->_usings[] = $namespace; |
||
202 | } |
||
203 | } |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Loads the paths XML node. |
||
208 | * @param \Prado\Xml\TXmlElement $pathsNode the paths XML node |
||
209 | * @param string $configPath the context path (for specifying relative paths) |
||
210 | */ |
||
211 | protected function loadPathsXml($pathsNode, $configPath) |
||
212 | { |
||
213 | foreach ($pathsNode->getElements() as $element) { |
||
214 | switch ($element->getTagName()) { |
||
215 | case 'alias': |
||
216 | { |
||
217 | if (($id = $element->getAttribute('id')) !== null && ($path = $element->getAttribute('path')) !== null) { |
||
218 | $path = str_replace('\\', '/', $path); |
||
219 | if (preg_match('/^\\/|.:\\/|.:\\\\/', $path)) { // if absolute path |
||
220 | $p = realpath($path); |
||
221 | } else { |
||
222 | $p = realpath($configPath . DIRECTORY_SEPARATOR . $path); |
||
223 | } |
||
224 | if ($p === false || !is_dir($p)) { |
||
225 | throw new TConfigurationException('appconfig_aliaspath_invalid', $id, $path); |
||
226 | } |
||
227 | if (isset($this->_aliases[$id])) { |
||
228 | throw new TConfigurationException('appconfig_alias_redefined', $id); |
||
229 | } |
||
230 | $this->_aliases[$id] = $p; |
||
231 | } else { |
||
232 | throw new TConfigurationException('appconfig_alias_invalid'); |
||
233 | } |
||
234 | $this->_empty = false; |
||
235 | break; |
||
236 | } |
||
237 | case 'using': |
||
238 | { |
||
239 | if (($namespace = $element->getAttribute('namespace')) !== null) { |
||
240 | $this->_usings[] = $namespace; |
||
241 | } else { |
||
242 | throw new TConfigurationException('appconfig_using_invalid'); |
||
243 | } |
||
244 | $this->_empty = false; |
||
245 | break; |
||
246 | } |
||
247 | default: |
||
248 | throw new TConfigurationException('appconfig_paths_invalid', $element->getTagName()); |
||
249 | } |
||
250 | } |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Reads the Composer static RegisteredLoaders for their Vendor Directory. Reads the Vendor |
||
255 | * Directory composer file 'installed.json' (accumulated extensions composer.json) for the project. |
||
256 | * The ['extra']['bootstrap'] field is read for each extension, if it's there. |
||
257 | * @return array<string, string> the extension name and bootstrap class. |
||
258 | * @since 4.2.0 |
||
259 | */ |
||
260 | protected function getComposerExtensionBootStraps() |
||
261 | { |
||
262 | if ($cache = Prado::getApplication()->getCache()) { |
||
263 | $plugins = $cache->get(self::COMPOSER_INSTALLED_CACHE); |
||
264 | if ($plugins !== null) { |
||
265 | return $plugins; |
||
|
|||
266 | } |
||
267 | } |
||
268 | $dependencies = new TChainedCacheDependency(); |
||
269 | $listDeps = $dependencies->getDependencies(); |
||
270 | $plugins = []; |
||
271 | foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { |
||
272 | $file = $vendorDir . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR . 'installed.json'; |
||
273 | $manifests = json_decode(file_get_contents($file), true, 512, JSON_THROW_ON_ERROR); |
||
274 | |||
275 | // Loop through the installed packages |
||
276 | foreach ($manifests['packages'] as $package) { |
||
277 | $name = $package['name']; |
||
278 | if (isset($package['extra']) && isset($package['extra'][self::COMPOSER_EXTRA_CLASS])) { |
||
279 | $plugins[$name] = $package['extra'][self::COMPOSER_EXTRA_CLASS]; |
||
280 | //$packagepath = realpath($vendorDir . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR . $package['install-path']); |
||
281 | } |
||
282 | } |
||
283 | $listDeps[] = new TFileCacheDependency($file); |
||
284 | } |
||
285 | if ($cache) { |
||
286 | $cache->set(self::COMPOSER_INSTALLED_CACHE, $plugins, null, $dependencies); |
||
287 | } |
||
288 | return $plugins; |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Given a module id as a composer package name, returns the extension bootstrap |
||
293 | * {@see \Prado\TModule} class. |
||
294 | * @param string $name the name of the Composer Extension. |
||
295 | * @return null|string the bootstrap class of the Composer Extension. |
||
296 | * @since 4.2.0 |
||
297 | */ |
||
298 | public function getComposerExtensionClass($name) |
||
299 | { |
||
300 | if (self::$_composerPlugins === null) { |
||
301 | self::$_composerPlugins = $this->getComposerExtensionBootStraps(); |
||
302 | } |
||
303 | return self::$_composerPlugins[$name] ?? null; |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Loads the modules PHP array. |
||
308 | * @param array $modulesNode the modules PHP array |
||
309 | * @param string $configPath the context path (for specifying relative paths) |
||
310 | */ |
||
311 | protected function loadModulesPhp($modulesNode, $configPath) |
||
312 | { |
||
313 | foreach ($modulesNode as $id => $module) { |
||
314 | if (strpos($id, '/') !== false && ($class = $this->getComposerExtensionClass($id))) { |
||
315 | if (isset($module['class'])) { |
||
316 | throw new TConfigurationException('appconfig_moduletype_inapplicable', $id); |
||
317 | } |
||
318 | $module['class'] = $class; |
||
319 | } |
||
320 | if (!isset($module['class'])) { |
||
321 | throw new TConfigurationException('appconfig_moduletype_required', $id); |
||
322 | } |
||
323 | $type = $module['class']; |
||
324 | unset($module['class']); |
||
325 | $properties = []; |
||
326 | if (isset($module['properties'])) { |
||
327 | $properties = $module['properties']; |
||
328 | unset($module['properties']); |
||
329 | } |
||
330 | $properties['id'] = $id; |
||
331 | $this->_modules[$id] = [$type, $properties, $module]; |
||
332 | $this->_empty = false; |
||
333 | } |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * Loads the modules XML node. |
||
338 | * @param \Prado\Xml\TXmlElement $modulesNode the modules XML node |
||
339 | * @param string $configPath the context path (for specifying relative paths) |
||
340 | */ |
||
341 | protected function loadModulesXml($modulesNode, $configPath) |
||
342 | { |
||
343 | foreach ($modulesNode->getElements() as $element) { |
||
344 | if ($element->getTagName() === 'module') { |
||
345 | $properties = $element->getAttributes(); |
||
346 | $id = $properties->itemAt('id'); |
||
347 | $type = $properties->remove('class'); |
||
348 | if (strpos($id ?? '', '/') !== false && ($class = $this->getComposerExtensionClass($id))) { |
||
349 | if ($type) { |
||
350 | throw new TConfigurationException('appconfig_moduletype_inapplicable', $id); |
||
351 | } |
||
352 | $type = $class; |
||
353 | } |
||
354 | if ($type === null) { |
||
355 | throw new TConfigurationException('appconfig_moduletype_required', $id); |
||
356 | } |
||
357 | $element->setParent(null); |
||
358 | if ($id === null) { |
||
359 | $this->_modules[] = [$type, $properties->toArray(), $element]; |
||
360 | } else { |
||
361 | $this->_modules[$id] = [$type, $properties->toArray(), $element]; |
||
362 | } |
||
363 | $this->_empty = false; |
||
364 | } else { |
||
365 | throw new TConfigurationException('appconfig_modules_invalid', $element->getTagName()); |
||
366 | } |
||
367 | } |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * Loads the services PHP array. |
||
372 | * @param array $servicesNode the services PHP array |
||
373 | * @param string $configPath the context path (for specifying relative paths) |
||
374 | */ |
||
375 | protected function loadServicesPhp($servicesNode, $configPath) |
||
376 | { |
||
377 | foreach ($servicesNode as $id => $service) { |
||
378 | if (!isset($service['class'])) { |
||
379 | throw new TConfigurationException('appconfig_servicetype_required'); |
||
380 | } |
||
381 | $type = $service['class']; |
||
382 | $properties = $service['properties'] ?? []; |
||
383 | unset($service['properties']); |
||
384 | $properties['id'] = $id; |
||
385 | $this->_services[$id] = [$type, $properties, $service]; |
||
386 | $this->_empty = false; |
||
387 | } |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * Loads the services XML node. |
||
392 | * @param \Prado\Xml\TXmlElement $servicesNode the services XML node |
||
393 | * @param string $configPath the context path (for specifying relative paths) |
||
394 | */ |
||
395 | protected function loadServicesXml($servicesNode, $configPath) |
||
396 | { |
||
397 | foreach ($servicesNode->getElements() as $element) { |
||
398 | if ($element->getTagName() === 'service') { |
||
399 | $properties = $element->getAttributes(); |
||
400 | if (($id = $properties->itemAt('id')) === null) { |
||
401 | throw new TConfigurationException('appconfig_serviceid_required'); |
||
402 | } |
||
403 | if (($type = $properties->remove('class')) === null) { |
||
404 | throw new TConfigurationException('appconfig_servicetype_required', $id); |
||
405 | } |
||
406 | $element->setParent(null); |
||
407 | $this->_services[$id] = [$type, $properties->toArray(), $element]; |
||
408 | $this->_empty = false; |
||
409 | } else { |
||
410 | throw new TConfigurationException('appconfig_services_invalid', $element->getTagName()); |
||
411 | } |
||
412 | } |
||
413 | } |
||
414 | |||
415 | /** |
||
416 | * Loads the parameters PHP array. |
||
417 | * @param array $parametersNode the parameters PHP array |
||
418 | * @param string $configPath the context path (for specifying relative paths) |
||
419 | */ |
||
420 | protected function loadParametersPhp($parametersNode, $configPath) |
||
421 | { |
||
422 | foreach ($parametersNode as $id => $parameter) { |
||
423 | if (is_array($parameter)) { |
||
424 | if (isset($parameter['class'])) { |
||
425 | $type = $parameter['class']; |
||
426 | unset($parameter['class']); |
||
427 | $properties = $parameter['properties'] ?? []; |
||
428 | $properties['id'] = $id; |
||
429 | $this->_parameters[$id] = [$type, $properties, $parameter]; |
||
430 | } |
||
431 | } else { |
||
432 | $this->_parameters[$id] = $parameter; |
||
433 | } |
||
434 | } |
||
435 | } |
||
436 | |||
437 | /** |
||
438 | * Loads the parameters XML node. |
||
439 | * @param \Prado\Xml\TXmlElement $parametersNode the parameters XML node |
||
440 | * @param string $configPath the context path (for specifying relative paths) |
||
441 | */ |
||
442 | protected function loadParametersXml($parametersNode, $configPath) |
||
443 | { |
||
444 | foreach ($parametersNode->getElements() as $element) { |
||
445 | if ($element->getTagName() === 'parameter') { |
||
446 | $properties = $element->getAttributes(); |
||
447 | if (($id = $properties->remove('id')) === null) { |
||
448 | throw new TConfigurationException('appconfig_parameterid_required'); |
||
449 | } |
||
450 | if (($type = $properties->remove('class')) === null) { |
||
451 | if (($value = $properties->remove('value')) === null) { |
||
452 | $this->_parameters[$id] = $element; |
||
453 | } else { |
||
454 | $this->_parameters[$id] = $value; |
||
455 | } |
||
456 | } else { |
||
457 | $this->_parameters[$id] = [$type, $properties->toArray(), $element]; |
||
458 | } |
||
459 | $this->_empty = false; |
||
460 | } else { |
||
461 | throw new TConfigurationException('appconfig_parameters_invalid', $element->getTagName()); |
||
462 | } |
||
463 | } |
||
464 | } |
||
465 | |||
466 | /** |
||
467 | * Loads the external PHP array. |
||
468 | * @param array $includeNode the application PHP array |
||
469 | * @param string $configPath the context path (for specifying relative paths) |
||
470 | */ |
||
471 | protected function loadExternalPhp($includeNode, $configPath) |
||
472 | { |
||
473 | foreach ($includeNode as $include) { |
||
474 | $when = isset($include['when']) ? true : false; |
||
475 | if (!isset($include['file'])) { |
||
476 | throw new TConfigurationException('appconfig_includefile_required'); |
||
477 | } |
||
478 | $filePath = $include['file']; |
||
479 | if (isset($this->_includes[$filePath])) { |
||
480 | $this->_includes[$filePath] = '(' . $this->_includes[$filePath] . ') || (' . $when . ')'; |
||
481 | } else { |
||
482 | $$this->_includes[$filePath] = $when; |
||
483 | } |
||
484 | $this->_empty = false; |
||
485 | } |
||
486 | } |
||
487 | |||
488 | /** |
||
489 | * Loads the external XML configurations. |
||
490 | * @param \Prado\Xml\TXmlElement $includeNode the application DOM element |
||
491 | * @param string $configPath the context path (for specifying relative paths) |
||
492 | */ |
||
493 | protected function loadExternalXml($includeNode, $configPath) |
||
494 | { |
||
495 | if (($when = $includeNode->getAttribute('when')) === null) { |
||
496 | $when = true; |
||
497 | } |
||
498 | if (($filePath = $includeNode->getAttribute('file')) === null) { |
||
499 | throw new TConfigurationException('appconfig_includefile_required'); |
||
500 | } |
||
501 | if (isset($this->_includes[$filePath])) { |
||
502 | $this->_includes[$filePath] = '(' . $this->_includes[$filePath] . ') || (' . $when . ')'; |
||
503 | } else { |
||
504 | $this->_includes[$filePath] = $when; |
||
505 | } |
||
506 | $this->_empty = false; |
||
507 | } |
||
508 | |||
509 | /** |
||
510 | * Returns list of page initial property values. |
||
511 | * Each array element represents a single property with the key |
||
512 | * being the property name and the value the initial property value. |
||
513 | * @return array list of page initial property values |
||
514 | */ |
||
515 | public function getProperties() |
||
516 | { |
||
517 | return $this->_properties; |
||
518 | } |
||
519 | |||
520 | /** |
||
521 | * Returns list of path alias definitions. |
||
522 | * The definitions are aggregated (top-down) from configuration files along the path |
||
523 | * to the specified page. Each array element represents a single alias definition, |
||
524 | * with the key being the alias name and the value the absolute path. |
||
525 | * @return array list of path alias definitions |
||
526 | */ |
||
527 | public function getAliases() |
||
528 | { |
||
529 | return $this->_aliases; |
||
530 | } |
||
531 | |||
532 | /** |
||
533 | * Returns list of namespaces to be used. |
||
534 | * The namespaces are aggregated (top-down) from configuration files along the path |
||
535 | * to the specified page. Each array element represents a single namespace usage, |
||
536 | * with the value being the namespace to be used. |
||
537 | * @return array list of namespaces to be used |
||
538 | */ |
||
539 | public function getUsings() |
||
540 | { |
||
541 | return $this->_usings; |
||
542 | } |
||
543 | |||
544 | /** |
||
545 | * Returns list of module configurations. |
||
546 | * The module configurations are aggregated (top-down) from configuration files |
||
547 | * along the path to the specified page. Each array element represents |
||
548 | * a single module configuration, with the key being the module ID and |
||
549 | * the value the module configuration. Each module configuration is |
||
550 | * stored in terms of an array with the following content |
||
551 | * ([0]=>module type, [1]=>module properties, [2]=>complete module configuration) |
||
552 | * The module properties are an array of property values indexed by property names. |
||
553 | * The complete module configuration is a TXmlElement object representing |
||
554 | * the raw module configuration which may contain contents enclosed within |
||
555 | * module tags. |
||
556 | * @return array list of module configurations to be used |
||
557 | */ |
||
558 | public function getModules() |
||
559 | { |
||
560 | return $this->_modules; |
||
561 | } |
||
562 | |||
563 | /** |
||
564 | * @return array list of service configurations |
||
565 | */ |
||
566 | public function getServices() |
||
569 | } |
||
570 | |||
571 | /** |
||
572 | * Returns list of parameter definitions. |
||
573 | * The parameter definitions are aggregated (top-down) from configuration files |
||
574 | * along the path to the specified page. Each array element represents |
||
575 | * a single parameter definition, with the key being the parameter ID and |
||
576 | * the value the parameter definition. A parameter definition can be either |
||
577 | * a string representing a string-typed parameter, or an array. |
||
578 | * The latter defines a component-typed parameter whose format is as follows, |
||
579 | * ([0]=>component type, [1]=>component properties) |
||
580 | * The component properties are an array of property values indexed by property names. |
||
581 | * @return array list of parameter definitions to be used |
||
582 | */ |
||
583 | public function getParameters() |
||
586 | } |
||
587 | |||
588 | /** |
||
589 | * @return array list of external configuration files. Each element is like $filePath=>$condition |
||
590 | */ |
||
591 | public function getExternalConfigurations() |
||
594 | } |
||
595 | } |
||
596 |
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.