Completed
Push — master ( 52b242...112fdc )
by Thomas
17s queued 11s
created

ConfigurationManager   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 389
Duplicated Lines 0 %

Test Coverage

Coverage 88.89%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 173
c 1
b 0
f 1
dl 0
loc 389
ccs 128
cts 144
cp 0.8889
rs 8.8
wmc 45

18 Methods

Rating   Name   Duplication   Size   Complexity  
A getInstance() 0 7 2
A getConfigurationFileContent() 0 13 3
A getProcessedActionsConfiguration() 0 7 1
A getDefaultsConfiguration() 0 3 1
A setSubstituteOriginalDrupalKernelConfiguration() 0 13 1
A getSubstituteOriginalDrupalKernelConfiguration() 0 3 1
A makeRelativePathsAbsolutes() 0 18 6
A doesConfigurationFilePathExists() 0 3 1
A doesConfigurationChanged() 0 3 1
A getConfigurationFilePath() 0 3 1
A __construct() 0 37 5
A getProcessedSubstituteOriginalDrupalKernelConfiguration() 0 7 1
B setConfigurationFilePathInfo() 0 43 7
A getProcessedConfiguration() 0 7 1
B setActionsConfigurations() 0 74 10
A setDefaultsConfiguration() 0 14 1
A getProcessedDefaultsConfiguration() 0 7 1
A getActionConfiguration() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like ConfigurationManager 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 ConfigurationManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the ekino Drupal Debug project.
7
 *
8
 * (c) ekino
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Ekino\Drupal\Debug\Configuration;
15
16
use Ekino\Drupal\Debug\ActionMetadata\ActionMetadataFactory;
17
use Ekino\Drupal\Debug\ActionMetadata\ActionMetadataManager;
18
use Ekino\Drupal\Debug\Cache\FileCache;
19
use Ekino\Drupal\Debug\Configuration\Model\ActionConfiguration;
20
use Ekino\Drupal\Debug\Configuration\Model\DefaultsConfiguration as DefaultsConfigurationModel;
21
use Ekino\Drupal\Debug\Configuration\Model\SubstituteOriginalDrupalKernelConfiguration as SubstituteOriginalDrupalKernelConfigurationModel;
22
use Ekino\Drupal\Debug\Resource\Model\ResourcesCollection;
23
use Symfony\Component\Config\Definition\ConfigurationInterface;
24
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
25
use Symfony\Component\Config\Definition\Processor;
26
use Symfony\Component\Config\Resource\FileExistenceResource;
27
use Symfony\Component\Config\Resource\FileResource;
28
use Symfony\Component\Filesystem\Filesystem;
29
use Symfony\Component\PropertyAccess\PropertyAccess;
30
use Symfony\Component\PropertyAccess\PropertyAccessor;
31
use Symfony\Component\Yaml\Parser;
32
33
class ConfigurationManager
34
{
35
    /**
36
     * @var string
37
     */
38
    public const CONFIGURATION_FILE_PATH_ENVIRONMENT_VARIABLE_NAME = 'DRUPAL_DEBUG_CONFIGURATION_FILE_PATH';
39
40
    /**
41
     * @var string
42
     */
43
    public const CONFIGURATION_CACHE_DIRECTORY_ENVIRONMENT_VARIABLE_NAME = 'DRUPAL_DEBUG_CONFIGURATION_CACHE_DIRECTORY_PATH';
44
45
    /**
46
     * @var string
47
     */
48
    public const ROOT_KEY = 'drupal-debug';
49
50
    /**
51
     * @var string
52
     */
53
    private const DEFAULT_CONFIGURATION_FILE_NAME = 'drupal-debug.yml.dist';
54
55
    /**
56
     * @var self|null
57
     */
58
    private static $instance = null;
59
60
    /**
61
     * @var Filesystem
62
     */
63
    private $filesystem;
64
65
    /**
66
     * @var PropertyAccessor
67
     */
68
    private $propertyAccessor;
69
70
    /**
71
     * @var bool
72
     */
73
    private $configurationChanged;
74
75
    /**
76
     * @var string
77
     */
78
    private $configurationFilePath;
79
80
    /**
81
     * @var bool
82
     */
83
    private $configurationFilePathExists;
84
85
    /**
86
     * @var string
87
     */
88
    private $configurationFilePathDirectory;
89
90
    /**
91
     * @var DefaultsConfigurationModel
92
     */
93
    private $defaultsConfiguration;
94
95
    /**
96
     * @var SubstituteOriginalDrupalKernelConfigurationModel
97
     */
98
    private $substituteOriginalDrupalKernelConfiguration;
99
100
    /**
101
     * @var ActionConfiguration[]
102
     */
103
    private $actionsConfigurations;
104
105 35
    private function __construct()
106
    {
107 35
        $configurationCacheDirectory = \getenv(self::CONFIGURATION_CACHE_DIRECTORY_ENVIRONMENT_VARIABLE_NAME);
108 35
        if (false === $configurationCacheDirectory) {
109 33
            $configurationCacheDirectory = \sys_get_temp_dir();
110
        }
111
112 35
        $this->filesystem = new Filesystem();
113
114 35
        $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
115
116 35
        $this->setConfigurationFilePathInfo();
117
118 35
        $fileCache = new FileCache(\sprintf('%s/drupal_debug_configuration.php', $configurationCacheDirectory), new ResourcesCollection(array(
119 35
            $this->doesConfigurationFilePathExists() ? new FileResource($this->configurationFilePath) : new FileExistenceResource($this->configurationFilePath),
120 35
            new FileResource(\sprintf('%s/ConfigurationManager.php', __DIR__)),
121
        )));
122
123 35
        if (!($this->configurationChanged = !$fileCache->isFresh() || empty($data = $fileCache->getData()))) {
124
            list(
125 16
                'defaults' => $this->defaultsConfiguration,
126 16
                'substitute_original_drupal_kernel' => $this->substituteOriginalDrupalKernelConfiguration,
127
                'actions' => $this->actionsConfigurations) = \array_map(static function ($serializedConfiguration) {
128 16
                    return \unserialize($serializedConfiguration);
129 16
                }, $data ?? $fileCache->getData());
130
        } else {
131 19
            $configurationFileContent = $this->getConfigurationFileContent();
132
133 19
            $this->setDefaultsConfiguration($configurationFileContent[self::ROOT_KEY][DefaultsConfiguration::ROOT_KEY] ?? array());
134 19
            $this->setSubstituteOriginalDrupalKernelConfiguration($configurationFileContent[self::ROOT_KEY][SubstituteOriginalDrupalKernelConfiguration::ROOT_KEY] ?? array(), $defaultsConfiguration = $this->getDefaultsConfiguration());
135 19
            $this->setActionsConfigurations($configurationFileContent[self::ROOT_KEY][ActionsConfiguration::ROOT_KEY] ?? array(), $defaultsConfiguration);
136
137 19
            $fileCache->invalidate();
138 19
            $fileCache->write(array(
139 19
                'defaults' => \serialize($this->defaultsConfiguration),
140 19
                'substitute_original_drupal_kernel' => \serialize($this->substituteOriginalDrupalKernelConfiguration),
141 19
                'actions' => \serialize($this->actionsConfigurations),
142
            ));
143
        }
144 35
    }
145
146 35
    public static function getInstance(): self
147
    {
148 35
        if (!self::$instance instanceof self) {
149 35
            self::$instance = new self();
150
        }
151
152 35
        return self::$instance;
153
    }
154
155 19
    public function getDefaultsConfiguration(): DefaultsConfigurationModel
156
    {
157 19
        return $this->defaultsConfiguration;
158
    }
159
160 19
    private function setDefaultsConfiguration($configurationFileContent): void
161
    {
162 19
        $this->defaultsConfiguration = new DefaultsConfigurationModel(
163 19
            $this->makeRelativePathsAbsolutes(
164 19
                $this->getProcessedDefaultsConfiguration($configurationFileContent),
165
                \array_map(static function (array $elements): string {
166 19
                    return \sprintf('[%s]', \implode('][', $elements));
167 19
                }, array(
168
                    array(
169 19
                        'cache_directory_path',
170
                    ),
171
                    array(
172
                        'logger',
173
                        'file_path',
174
                    ),
175
                ))
176
            )
177
        );
178 19
    }
179
180
    public function getSubstituteOriginalDrupalKernelConfiguration(): SubstituteOriginalDrupalKernelConfigurationModel
181
    {
182
        return $this->substituteOriginalDrupalKernelConfiguration;
183
    }
184
185 19
    private function setSubstituteOriginalDrupalKernelConfiguration($configurationFileContent, DefaultsConfigurationModel $defaultsConfiguration): void
186
    {
187 19
        $this->substituteOriginalDrupalKernelConfiguration = new SubstituteOriginalDrupalKernelConfigurationModel(
188 19
            $this->makeRelativePathsAbsolutes(
189 19
                $this->getProcessedSubstituteOriginalDrupalKernelConfiguration($configurationFileContent, $defaultsConfiguration),
190
                \array_map(static function (array $elements): string {
191 19
                    return \sprintf('[%s]', \implode('][', $elements));
192 19
                }, array(
193
                    array(
194 19
                        'composer_autoload_file_path',
195
                    ),
196
                    array(
197
                        'cache_directory_path',
198
                    ),
199
                ))
200
            )
201
        );
202 19
    }
203
204 2
    public function getActionConfiguration(string $class): ActionConfiguration
205
    {
206 2
        return $this->actionsConfigurations[$class];
207
    }
208
209 19
    private function setActionsConfigurations($configurationFileContent, DefaultsConfigurationModel $defaultsConfiguration): void
210
    {
211 19
        $this->actionsConfigurations = array();
212
213 19
        $actionMetadataManager = ActionMetadataManager::getInstance();
214 19
        $actionMetadataFactory = new ActionMetadataFactory();
215
216 19
        foreach ($configurationFileContent as $shortName => $config) {
217 4
            if ($actionMetadataManager->isCoreAction($shortName)) {
218 4
                continue;
219
            }
220
221
            $actionMetadataManager->add($actionMetadataFactory->create($shortName));
222
        }
223
224 19
        $processedActionsConfiguration = $this->getProcessedActionsConfiguration($configurationFileContent, $actionMetadataManager->all(), $defaultsConfiguration);
225
        $propertyPaths = \array_map(static function (array $elements): string {
226 19
            return \sprintf('[%s]', \implode('][', $elements));
227 19
        }, array(
228
            array(
229 19
                'display_pretty_exceptions',
230
                'logger',
231
                'file_path',
232
            ),
233
            array(
234
                'throw_errors_as_exceptions',
235
                'logger',
236
                'file_path',
237
            ),
238
            array(
239
                'watch_container_definitions',
240
                'cache_directory_path',
241
            ),
242
            array(
243
                'watch_modules_hooks_implementations',
244
                'cache_directory_path',
245
            ),
246
            array(
247
                'watch_routing_definitions',
248
                'cache_directory_path',
249
            ),
250
        ));
251
252
        $buildPropertyPathsRecursively = static function (array $array, array $previous) use (&$buildPropertyPathsRecursively, &$propertyPaths): void {
253
            foreach ($array as $key => $row) {
254
                if (\is_string($row) && 1 === \preg_match('/_path$/i', $key)) {
255
                    $propertyPaths[] = \sprintf('[%s]', \implode('][', \array_merge($previous, array(
256
                        $key,
257
                    ))));
258
                } elseif (\is_array($row)) {
259
                    $buildPropertyPathsRecursively($row, \array_merge($previous, array(
260
                        $key,
261
                    )));
262
                }
263
            }
264 19
        };
265
266 19
        foreach ($processedActionsConfiguration as $shortName => $processedActionConfiguration) {
267 19
            if ($actionMetadataManager->isCoreAction($shortName)) {
268 19
                continue;
269
            }
270
271
            $buildPropertyPathsRecursively($processedActionConfiguration, array(
272
                $shortName,
273
            ));
274
        }
275
276
        foreach (
277 19
            $this->makeRelativePathsAbsolutes(
278 19
                $processedActionsConfiguration,
279
                $propertyPaths
280
            ) as $shortName => $processedActionConfiguration
281
        ) {
282 19
            $this->actionsConfigurations[$shortName] = new ActionConfiguration($processedActionConfiguration);
283
        }
284 19
    }
285
286 18
    public function getConfigurationFilePath(): string
287
    {
288 18
        return $this->configurationFilePath;
289
    }
290
291 35
    public function doesConfigurationFilePathExists(): bool
292
    {
293 35
        return $this->configurationFilePathExists;
294
    }
295
296 6
    public function doesConfigurationChanged(): bool
297
    {
298 6
        return $this->configurationChanged;
299
    }
300
301 35
    private function setConfigurationFilePathInfo(): void
302
    {
303 35
        $possibleConfigurationFilePath = \getenv(self::CONFIGURATION_FILE_PATH_ENVIRONMENT_VARIABLE_NAME);
304 35
        if (false === $possibleConfigurationFilePath) {
305
            // The default configuration file location is the same than the vendor directory.
306
            $possibleAutoloadPaths = array(
307
                // Vendor of a project : Configuration\src\drupal-debug\ekino\autoload.php
308 13
                \sprintf('%s/../../../../autoload.php', __DIR__),
309
                // Directly this project : Configuration\src\/vendor/autoload.php
310 13
                \sprintf('%s/../../vendor/autoload.php', __DIR__),
311
                // For other cases (if they exist), please use the dedicated environment variable.
312
            );
313
314 13
            foreach ($possibleAutoloadPaths as $possibleAutoloadPath) {
315 13
                if (\is_file($possibleAutoloadPath)) {
316 13
                    $possibleConfigurationFilePath = \sprintf('%s/../%s', \dirname($possibleAutoloadPath), self::DEFAULT_CONFIGURATION_FILE_NAME);
317
318 13
                    break;
319
                }
320
            }
321
322 13
            if (false === $possibleConfigurationFilePath) {
323
                throw new \RuntimeException('The composer autoload.php file could not be found.');
324
            }
325
        }
326
327 35
        $possibleConfigurationFilePaths = \array_unique(array(
328 35
            $possibleConfigurationFilePath,
329 35
            \rtrim($possibleConfigurationFilePath, '.dist'),
330
        ));
331
332 35
        $exists = false;
333 35
        foreach ($possibleConfigurationFilePaths as $possibleConfigurationFilePath) {
334 35
            if (\is_file($possibleConfigurationFilePath)) {
335 14
                $exists = true;
336
337 14
                break;
338
            }
339
        }
340
341 35
        $this->configurationFilePath = $possibleConfigurationFilePath;
342 35
        $this->configurationFilePathExists = $exists;
343 35
        $this->configurationFilePathDirectory = \dirname($this->configurationFilePath);
344 35
    }
345
346 19
    private function getConfigurationFileContent(): array
347
    {
348 19
        if (!$this->configurationFilePathExists) {
349 7
            return array();
350
        }
351
352 12
        $parser = new Parser();
353 12
        $content = $parser->parseFile($this->configurationFilePath);
354 12
        if (!\is_array($content)) {
355
            throw new InvalidConfigurationException('The content of the drupal-debug configuration file should be an array.');
356
        }
357
358 12
        return $content;
359
    }
360
361
    /**
362
     * @internal
363
     */
364 19
    public function getProcessedDefaultsConfiguration($configurationFileContent): array
365
    {
366 19
        return $this->getProcessedConfiguration(
367
            array(
368 19
                DefaultsConfiguration::ROOT_KEY => $configurationFileContent,
369
            ),
370 19
            new DefaultsConfiguration()
371
        );
372
    }
373
374 19
    private function getProcessedSubstituteOriginalDrupalKernelConfiguration($configurationFileContent, DefaultsConfigurationModel $defaultsConfiguration): array
375
    {
376 19
        return $this->getProcessedConfiguration(
377
            array(
378 19
                SubstituteOriginalDrupalKernelConfiguration::ROOT_KEY => $configurationFileContent,
379
            ),
380 19
            new SubstituteOriginalDrupalKernelConfiguration($defaultsConfiguration)
381
        );
382
    }
383
384 19
    private function getProcessedActionsConfiguration($configurationFileContent, array $actionMetadata, DefaultsConfigurationModel $defaultsConfiguration): array
385
    {
386 19
        return $this->getProcessedConfiguration(
387
            array(
388 19
                ActionsConfiguration::ROOT_KEY => $configurationFileContent,
389
            ),
390 19
            new ActionsConfiguration($actionMetadata, $defaultsConfiguration)
391
        );
392
    }
393
394 19
    private function getProcessedConfiguration(array $configurationFileContent, ConfigurationInterface $configuration): array
395
    {
396 19
        return (new Processor())->process(
397 19
            $configuration
398 19
                ->getConfigTreeBuilder()
399 19
                ->buildTree(),
400
            $configurationFileContent
401
        );
402
    }
403
404 19
    private function makeRelativePathsAbsolutes(array $processedConfiguration, array $propertyPaths): array
405
    {
406 19
        foreach ($propertyPaths as $propertyPath) {
407 19
            if (!$this->propertyAccessor->isReadable($processedConfiguration, $propertyPath)) {
408
                continue;
409
            }
410
411 19
            $path = $this->propertyAccessor->getValue($processedConfiguration, $propertyPath);
412 19
            if (null === $path || '' === $path) {
413
                continue;
414
            }
415
416 19
            if (!$this->filesystem->isAbsolutePath($path)) {
417 19
                $this->propertyAccessor->setValue($processedConfiguration, $propertyPath, \sprintf('%s/%s', $this->configurationFilePathDirectory, $path));
418
            }
419
        }
420
421 19
        return $processedConfiguration;
422
    }
423
}
424