Completed
Pull Request — master (#77)
by Thomas
05:39
created

ConfigurationManager::setActionsConfigurations()   B

Complexity

Conditions 10
Paths 18

Size

Total Lines 74
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 14.5537

Importance

Changes 0
Metric Value
cc 10
eloc 47
c 0
b 0
f 0
nc 18
nop 2
dl 0
loc 74
ccs 18
cts 28
cp 0.6429
crap 14.5537
rs 7.2896

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 33
    private function __construct()
106
    {
107 33
        $configurationCacheDirectory = \getenv(self::CONFIGURATION_CACHE_DIRECTORY_ENVIRONMENT_VARIABLE_NAME);
108 33
        if (false === $configurationCacheDirectory) {
109 33
            $configurationCacheDirectory = \sys_get_temp_dir();
110
        }
111
112 33
        $this->filesystem = new Filesystem();
113
114 33
        $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
115
116 33
        $this->setConfigurationFilePathInfo();
117
118 33
        $fileCache = new FileCache(\sprintf('%s/drupal_debug_configuration.php', $configurationCacheDirectory), new ResourcesCollection(array(
119 33
            $this->doesConfigurationFilePathExists() ? new FileResource($this->configurationFilePath) : new FileExistenceResource($this->configurationFilePath),
120 33
            new FileResource(\sprintf('%s/ConfigurationManager.php', __DIR__)),
121
        )));
122
123 33
        if (!($this->configurationChanged = !$fileCache->isFresh() || empty($data = $fileCache->getData()))) {
124
            list(
125 15
                'defaults' => $this->defaultsConfiguration,
126 15
                'substitute_original_drupal_kernel' => $this->substituteOriginalDrupalKernelConfiguration,
127
                'actions' => $this->actionsConfigurations) = \array_map(static function ($serializedConfiguration) {
128 15
                    return \unserialize($serializedConfiguration);
129 15
                }, $data ?? $fileCache->getData());
130
        } else {
131 18
            $configurationFileContent = $this->getConfigurationFileContent();
132
133 18
            $this->setDefaultsConfiguration($configurationFileContent[self::ROOT_KEY][DefaultsConfiguration::ROOT_KEY] ?? array());
134 18
            $this->setSubstituteOriginalDrupalKernelConfiguration($configurationFileContent[self::ROOT_KEY][SubstituteOriginalDrupalKernelConfiguration::ROOT_KEY] ?? array(), $defaultsConfiguration = $this->getDefaultsConfiguration());
135 18
            $this->setActionsConfigurations($configurationFileContent[self::ROOT_KEY][ActionsConfiguration::ROOT_KEY] ?? array(), $defaultsConfiguration);
136
137 18
            $fileCache->invalidate();
138 18
            $fileCache->write(array(
139 18
                'defaults' => \serialize($this->defaultsConfiguration),
140 18
                'substitute_original_drupal_kernel' => \serialize($this->substituteOriginalDrupalKernelConfiguration),
141 18
                'actions' => \serialize($this->actionsConfigurations),
142
            ));
143
        }
144 33
    }
145
146 33
    public static function getInstance(): self
147
    {
148 33
        if (!self::$instance instanceof self) {
149 33
            self::$instance = new self();
150
        }
151
152 33
        return self::$instance;
153
    }
154
155 18
    public function getDefaultsConfiguration(): DefaultsConfigurationModel
156
    {
157 18
        return $this->defaultsConfiguration;
158
    }
159
160 18
    private function setDefaultsConfiguration($configurationFileContent): void
161
    {
162 18
        $this->defaultsConfiguration = new DefaultsConfigurationModel(
163 18
            $this->makeRelativePathsAbsolutes(
164 18
                $this->getProcessedDefaultsConfiguration($configurationFileContent),
165
                \array_map(static function (array $elements): string {
166 18
                    return \sprintf('[%s]', \implode('][', $elements));
167 18
                }, array(
168
                    array(
169 18
                        'cache_directory_path',
170
                    ),
171
                    array(
172
                        'logger',
173
                        'file_path',
174
                    ),
175
                ))
176
            )
177
        );
178 18
    }
179
180
    public function getSubstituteOriginalDrupalKernelConfiguration(): SubstituteOriginalDrupalKernelConfigurationModel
181
    {
182
        return $this->substituteOriginalDrupalKernelConfiguration;
183
    }
184
185 18
    private function setSubstituteOriginalDrupalKernelConfiguration($configurationFileContent, DefaultsConfigurationModel $defaultsConfiguration): void
186
    {
187 18
        $this->substituteOriginalDrupalKernelConfiguration = new SubstituteOriginalDrupalKernelConfigurationModel(
188 18
            $this->makeRelativePathsAbsolutes(
189 18
                $this->getProcessedSubstituteOriginalDrupalKernelConfiguration($configurationFileContent, $defaultsConfiguration),
190
                \array_map(static function (array $elements): string {
191 18
                    return \sprintf('[%s]', \implode('][', $elements));
192 18
                }, array(
193
                    array(
194 18
                        'composer_autoload_file_path',
195
                    ),
196
                    array(
197
                        'cache_directory_path',
198
                    ),
199
                ))
200
            )
201
        );
202 18
    }
203
204
    public function getActionConfiguration(string $class): ActionConfiguration
205
    {
206
        return $this->actionsConfigurations[$class];
207
    }
208
209 18
    private function setActionsConfigurations($configurationFileContent, DefaultsConfigurationModel $defaultsConfiguration): void
210
    {
211 18
        $this->actionsConfigurations = array();
212
213 18
        $actionMetadataManager = ActionMetadataManager::getInstance();
214 18
        $actionMetadataFactory = new ActionMetadataFactory();
215
216 18
        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 18
        $processedActionsConfiguration = $this->getProcessedActionsConfiguration($configurationFileContent, $actionMetadataManager->all(), $defaultsConfiguration);
225
        $propertyPaths = \array_map(static function (array $elements): string {
226 18
            return \sprintf('[%s]', \implode('][', $elements));
227 18
        }, array(
228
            array(
229 18
                '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 18
        };
265
266 18
        foreach ($processedActionsConfiguration as $shortName => $processedActionConfiguration) {
267 18
            if ($actionMetadataManager->isCoreAction($shortName)) {
268 18
                continue;
269
            }
270
271
            $buildPropertyPathsRecursively($processedActionConfiguration, array(
272
                $shortName,
273
            ));
274
        }
275
276
        foreach (
277 18
            $this->makeRelativePathsAbsolutes(
278 18
                $processedActionsConfiguration,
279
                $propertyPaths
280
            ) as $shortName => $processedActionConfiguration
281
        ) {
282 18
            $this->actionsConfigurations[$shortName] = new ActionConfiguration($processedActionConfiguration);
283
        }
284 18
    }
285
286 18
    public function getConfigurationFilePath(): string
287
    {
288 18
        return $this->configurationFilePath;
289
    }
290
291 33
    public function doesConfigurationFilePathExists(): bool
292
    {
293 33
        return $this->configurationFilePathExists;
294
    }
295
296 6
    public function doesConfigurationChanged(): bool
297
    {
298 6
        return $this->configurationChanged;
299
    }
300
301 33
    private function setConfigurationFilePathInfo(): void
302
    {
303 33
        $possibleConfigurationFilePath = \getenv(self::CONFIGURATION_FILE_PATH_ENVIRONMENT_VARIABLE_NAME);
304 33
        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 33
        $possibleConfigurationFilePaths = \array_unique(array(
328 33
            $possibleConfigurationFilePath,
329 33
            \rtrim($possibleConfigurationFilePath, '.dist'),
330
        ));
331
332 33
        $exists = false;
333 33
        foreach ($possibleConfigurationFilePaths as $possibleConfigurationFilePath) {
334 33
            if (\is_file($possibleConfigurationFilePath)) {
335 12
                $exists = true;
336
337 12
                break;
338
            }
339
        }
340
341 33
        $this->configurationFilePath = $possibleConfigurationFilePath;
342 33
        $this->configurationFilePathExists = $exists;
343 33
        $this->configurationFilePathDirectory = \dirname($this->configurationFilePath);
344 33
    }
345
346 18
    private function getConfigurationFileContent(): array
347
    {
348 18
        if (!$this->configurationFilePathExists) {
349 7
            return array();
350
        }
351
352 11
        $parser = new Parser();
353 11
        $content = $parser->parseFile($this->configurationFilePath);
354 11
        if (!\is_array($content)) {
355
            throw new InvalidConfigurationException('The content of the drupal-debug configuration file should be an array.');
356
        }
357
358 11
        return $content;
359
    }
360
361
    /**
362
     * @internal
363
     */
364 18
    public function getProcessedDefaultsConfiguration($configurationFileContent): array
365
    {
366 18
        return $this->getProcessedConfiguration(
367
            array(
368 18
                DefaultsConfiguration::ROOT_KEY => $configurationFileContent,
369
            ),
370 18
            new DefaultsConfiguration()
371
        );
372
    }
373
374 18
    private function getProcessedSubstituteOriginalDrupalKernelConfiguration($configurationFileContent, DefaultsConfigurationModel $defaultsConfiguration): array
375
    {
376 18
        return $this->getProcessedConfiguration(
377
            array(
378 18
                SubstituteOriginalDrupalKernelConfiguration::ROOT_KEY => $configurationFileContent,
379
            ),
380 18
            new SubstituteOriginalDrupalKernelConfiguration($defaultsConfiguration)
381
        );
382
    }
383
384 18
    private function getProcessedActionsConfiguration($configurationFileContent, array $actionMetadata, DefaultsConfigurationModel $defaultsConfiguration): array
385
    {
386 18
        return $this->getProcessedConfiguration(
387
            array(
388 18
                ActionsConfiguration::ROOT_KEY => $configurationFileContent,
389
            ),
390 18
            new ActionsConfiguration($actionMetadata, $defaultsConfiguration)
391
        );
392
    }
393
394 18
    private function getProcessedConfiguration(array $configurationFileContent, ConfigurationInterface $configuration): array
395
    {
396 18
        return (new Processor())->process(
397 18
            $configuration
398 18
                ->getConfigTreeBuilder()
399 18
                ->buildTree(),
400
            $configurationFileContent
401
        );
402
    }
403
404 18
    private function makeRelativePathsAbsolutes(array $processedConfiguration, array $propertyPaths): array
405
    {
406 18
        foreach ($propertyPaths as $propertyPath) {
407 18
            if (!$this->propertyAccessor->isReadable($processedConfiguration, $propertyPath)) {
408
                continue;
409
            }
410
411 18
            $path = $this->propertyAccessor->getValue($processedConfiguration, $propertyPath);
412 18
            if (null === $path || '' === $path) {
413
                continue;
414
            }
415
416 18
            if (!$this->filesystem->isAbsolutePath($path)) {
417 18
                $this->propertyAccessor->setValue($processedConfiguration, $propertyPath, \sprintf('%s/%s', $this->configurationFilePathDirectory, $path));
418
            }
419
        }
420
421 18
        return $processedConfiguration;
422
    }
423
}
424