MergeExtensionConfigurationPass::process()   F
last analyzed

Complexity

Conditions 15
Paths 321

Size

Total Lines 68
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 68
rs 3.5708
c 0
b 0
f 0
cc 15
nc 321
nop 1

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
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[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 Symfony\Component\DependencyInjection\Compiler;
13
14
use Symfony\Component\Config\Definition\BaseNode;
15
use Symfony\Component\DependencyInjection\ContainerBuilder;
16
use Symfony\Component\DependencyInjection\Exception\LogicException;
17
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
18
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
19
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
20
use Symfony\Component\DependencyInjection\Extension\Extension;
21
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
22
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
23
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
24
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
25
26
/**
27
 * Merges extension configs into the container builder.
28
 *
29
 * @author Fabien Potencier <[email protected]>
30
 */
31
class MergeExtensionConfigurationPass implements CompilerPassInterface
32
{
33
    public function process(ContainerBuilder $container): void
34
    {
35
        $parameters = $container->getParameterBag()->all();
36
        $definitions = $container->getDefinitions();
37
        $aliases = $container->getAliases();
38
        $exprLangProviders = $container->getExpressionLanguageProviders();
39
        $configAvailable = class_exists(BaseNode::class);
40
41
        foreach ($container->getExtensions() as $extension) {
42
            if ($extension instanceof PrependExtensionInterface) {
43
                $extension->prepend($container);
44
            }
45
        }
46
47
        foreach ($container->getExtensions() as $name => $extension) {
48
            if (!$config = $container->getExtensionConfig($name)) {
49
                // this extension was not called
50
                continue;
51
            }
52
            $resolvingBag = $container->getParameterBag();
53
            if ($resolvingBag instanceof EnvPlaceholderParameterBag && $extension instanceof Extension) {
54
                // create a dedicated bag so that we can track env vars per-extension
55
                $resolvingBag = new MergeExtensionConfigurationParameterBag($resolvingBag);
56
                if ($configAvailable) {
57
                    BaseNode::setPlaceholderUniquePrefix($resolvingBag->getEnvPlaceholderUniquePrefix());
58
                }
59
            }
60
61
            try {
62
                $config = $resolvingBag->resolveValue($config);
63
            } catch (ParameterNotFoundException $e) {
64
                $e->setSourceExtensionName($name);
65
66
                throw $e;
67
            }
68
69
            try {
70
                $tmpContainer = new MergeExtensionConfigurationContainerBuilder($extension, $resolvingBag);
71
                $tmpContainer->setResourceTracking($container->isTrackingResources());
72
                $tmpContainer->addObjectResource($extension);
73
                if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) {
74
                    $tmpContainer->addObjectResource($configuration);
75
                }
76
77
                foreach ($exprLangProviders as $provider) {
78
                    $tmpContainer->addExpressionLanguageProvider($provider);
79
                }
80
81
                $extension->load($config, $tmpContainer);
82
            } catch (\Exception $e) {
83
                if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) {
84
                    $container->getParameterBag()->mergeEnvPlaceholders($resolvingBag);
0 ignored issues
show
Bug introduced by
The method mergeEnvPlaceholders() does not exist on Symfony\Component\Depend...g\ParameterBagInterface. It seems like you code against a sub-type of Symfony\Component\Depend...g\ParameterBagInterface such as Symfony\Component\Depend...PlaceholderParameterBag. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

84
                    $container->getParameterBag()->/** @scrutinizer ignore-call */ mergeEnvPlaceholders($resolvingBag);
Loading history...
85
                }
86
87
                throw $e;
88
            }
89
90
            if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) {
91
                // don't keep track of env vars that are *overridden* when configs are merged
92
                $resolvingBag->freezeAfterProcessing($extension, $tmpContainer);
93
            }
94
95
            $container->merge($tmpContainer);
96
            $container->getParameterBag()->add($parameters);
97
        }
98
99
        $container->addDefinitions($definitions);
100
        $container->addAliases($aliases);
101
    }
102
}
103
104
/**
105
 * @internal
106
 */
107
class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag
108
{
109
    private array $processedEnvPlaceholders;
110
111
    public function __construct(parent $parameterBag)
112
    {
113
        parent::__construct($parameterBag->all());
114
        $this->mergeEnvPlaceholders($parameterBag);
115
    }
116
117
    public function freezeAfterProcessing(Extension $extension, ContainerBuilder $container): void
118
    {
119
        if (!$config = $extension->getProcessedConfigs()) {
120
            // Extension::processConfiguration() wasn't called, we cannot know how configs were merged
121
            return;
122
        }
123
        $this->processedEnvPlaceholders = [];
124
125
        // serialize config and container to catch env vars nested in object graphs
126
        $config = serialize($config).serialize($container->getDefinitions()).serialize($container->getAliases()).serialize($container->getParameterBag()->all());
127
128
        if (false === stripos($config, 'env_')) {
129
            return;
130
        }
131
132
        preg_match_all('/env_[a-f0-9]{16}_\w+_[a-f0-9]{32}/Ui', $config, $matches);
133
        $usedPlaceholders = array_flip($matches[0]);
134
        foreach (parent::getEnvPlaceholders() as $env => $placeholders) {
135
            foreach ($placeholders as $placeholder) {
136
                if (isset($usedPlaceholders[$placeholder])) {
137
                    $this->processedEnvPlaceholders[$env] = $placeholders;
138
                    break;
139
                }
140
            }
141
        }
142
    }
143
144
    public function getEnvPlaceholders(): array
145
    {
146
        return $this->processedEnvPlaceholders ?? parent::getEnvPlaceholders();
147
    }
148
149
    public function getUnusedEnvPlaceholders(): array
150
    {
151
        return !isset($this->processedEnvPlaceholders) ? [] : array_diff_key(parent::getEnvPlaceholders(), $this->processedEnvPlaceholders);
152
    }
153
}
154
155
/**
156
 * A container builder preventing using methods that wouldn't have any effect from extensions.
157
 *
158
 * @internal
159
 */
160
class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder
161
{
162
    private string $extensionClass;
163
164
    public function __construct(ExtensionInterface $extension, ?ParameterBagInterface $parameterBag = null)
165
    {
166
        parent::__construct($parameterBag);
167
168
        $this->extensionClass = $extension::class;
169
    }
170
171
    public function addCompilerPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0): static
172
    {
173
        throw new LogicException(\sprintf('You cannot add compiler pass "%s" from extension "%s". Compiler passes must be registered before the container is compiled.', get_debug_type($pass), $this->extensionClass));
174
    }
175
176
    public function registerExtension(ExtensionInterface $extension): void
177
    {
178
        throw new LogicException(\sprintf('You cannot register extension "%s" from "%s". Extensions must be registered before the container is compiled.', get_debug_type($extension), $this->extensionClass));
179
    }
180
181
    public function compile(bool $resolveEnvPlaceholders = false): void
182
    {
183
        throw new LogicException(\sprintf('Cannot compile the container in extension "%s".', $this->extensionClass));
184
    }
185
186
    public function resolveEnvPlaceholders(mixed $value, string|bool|null $format = null, ?array &$usedEnvs = null): mixed
187
    {
188
        if (true !== $format || !\is_string($value)) {
189
            return parent::resolveEnvPlaceholders($value, $format, $usedEnvs);
190
        }
191
192
        $bag = $this->getParameterBag();
193
        $value = $bag->resolveValue($value);
194
195
        if (!$bag instanceof EnvPlaceholderParameterBag) {
196
            return parent::resolveEnvPlaceholders($value, true, $usedEnvs);
197
        }
198
199
        foreach ($bag->getEnvPlaceholders() as $env => $placeholders) {
200
            if (!str_contains($env, ':')) {
201
                continue;
202
            }
203
            foreach ($placeholders as $placeholder) {
204
                if (false !== stripos($value, $placeholder)) {
205
                    throw new RuntimeException(\sprintf('Using a cast in "env(%s)" is incompatible with resolution at compile time in "%s". The logic in the extension should be moved to a compiler pass, or an env parameter with no cast should be used instead.', $env, $this->extensionClass));
206
                }
207
            }
208
        }
209
210
        return parent::resolveEnvPlaceholders($value, true, $usedEnvs);
211
    }
212
}
213