Skip to content

Commit 1c4af6b

Browse files
[DI] Fix merging of env vars in configs
1 parent a014222 commit 1c4af6b

File tree

5 files changed

+118
-13
lines changed

5 files changed

+118
-13
lines changed

src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php

+59-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313

1414
use Symfony\Component\DependencyInjection\ContainerBuilder;
1515
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
16+
use Symfony\Component\DependencyInjection\Extension\Extension;
1617
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
18+
use Symfony\Component\DependencyInjection\Parameterbag\EnvPlaceholderParameterBag;
1719

1820
/**
1921
* Merges extension configs into the container builder.
@@ -43,7 +45,8 @@ public function process(ContainerBuilder $container)
4345
// this extension was not called
4446
continue;
4547
}
46-
$config = $container->getParameterBag()->resolveValue($config);
48+
$resolvingBag = clone $container->getParameterBag();
49+
$config = $resolvingBag->resolveValue($config);
4750

4851
$tmpContainer = new ContainerBuilder($container->getParameterBag());
4952
$tmpContainer->setResourceTracking($container->isTrackingResources());
@@ -58,6 +61,11 @@ public function process(ContainerBuilder $container)
5861

5962
$extension->load($config, $tmpContainer);
6063

64+
if ($extension instanceof Extension && $resolvingBag instanceof EnvPlaceholderParameterBag) {
65+
$resolvingBag = new MergeExtensionConfigurationParameterBag($extension->getProcessedConfigs(), $resolvingBag);
66+
$container->getParameterBag()->mergeEnvPlaceholders($resolvingBag);
67+
}
68+
6169
$container->merge($tmpContainer);
6270
$container->getParameterBag()->add($parameters);
6371
}
@@ -66,3 +74,53 @@ public function process(ContainerBuilder $container)
6674
$container->addAliases($aliases);
6775
}
6876
}
77+
78+
/**
79+
* @internal
80+
*/
81+
class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag
82+
{
83+
private $envPlaceholders;
84+
85+
public function __construct(array $config, parent $resolvingBag)
86+
{
87+
$this->envPlaceholders = $resolvingBag->getEnvPlaceholders();
88+
$config = $this->resolveEnvPlaceholders($config);
89+
parent::__construct($this->resolveEnvReferences($config));
90+
}
91+
92+
/**
93+
* {@inheritdoc}
94+
*/
95+
public function getEnvPlaceholders()
96+
{
97+
$envPlaceholders = parent::getEnvPlaceholders();
98+
99+
foreach ($envPlaceholders as $env => $placeholders) {
100+
if (isset($this->envPlaceholders[$env])) {
101+
$envPlaceholders[$env] += $this->envPlaceholders[$env];
102+
}
103+
}
104+
105+
return $envPlaceholders;
106+
}
107+
108+
private function resolveEnvPlaceholders($value)
109+
{
110+
if (is_array($value)) {
111+
foreach ($value as $k => $v) {
112+
$value[$this->resolveEnvPlaceholders($k)] = $this->resolveEnvPlaceholders($v);
113+
}
114+
} elseif (is_string($value)) {
115+
foreach ($this->envPlaceholders as $env => $placeholders) {
116+
foreach ($placeholders as $placeholder) {
117+
if (false !== stripos($value, $placeholder)) {
118+
$value = str_ireplace($placeholder, "%env($env)%", $value);
119+
}
120+
}
121+
}
122+
}
123+
124+
return $value;
125+
}
126+
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -729,10 +729,9 @@ public function compile(/*$resolveEnvPlaceholders = false*/)
729729
$bag = $this->getParameterBag();
730730

731731
if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) {
732-
$bag->resolveEnvReferences();
733-
$this->parameterBag = new ParameterBag($bag->all());
732+
$this->parameterBag = new ParameterBag($bag->resolveEnvReferences($bag->all()));
734733
$this->envPlaceholders = $bag->getEnvPlaceholders();
735-
$this->parameterBag = $bag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true));
734+
$this->parameterBag = $bag = new ParameterBag($this->resolveEnvPlaceholders($this->parameterBag->all(), true));
736735
}
737736

738737
$compiler->compile($this);

src/Symfony/Component/DependencyInjection/Extension/Extension.php

+12-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
*/
2626
abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface
2727
{
28+
private $processedConfigs = array();
29+
2830
/**
2931
* {@inheritdoc}
3032
*/
@@ -91,7 +93,16 @@ final protected function processConfiguration(ConfigurationInterface $configurat
9193
{
9294
$processor = new Processor();
9395

94-
return $processor->processConfiguration($configuration, $configs);
96+
return $this->processedConfigs[] = $processor->processConfiguration($configuration, $configs);
97+
}
98+
99+
final public function getProcessedConfigs()
100+
{
101+
try {
102+
return $this->processedConfigs;
103+
} finally {
104+
$this->processedConfigs = array();
105+
}
95106
}
96107

97108
/**

src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,11 @@ public function resolve()
106106
/**
107107
* Replaces "%env(FOO)%" references by their placeholder, keeping regular "%parameters%" references as is.
108108
*/
109-
public function resolveEnvReferences()
109+
public function resolveEnvReferences(array $value)
110110
{
111111
$this->resolveEnvReferences = true;
112112
try {
113-
$this->resolve();
113+
return $this->resolveValue($value);
114114
} finally {
115115
$this->resolveEnvReferences = false;
116116
}

src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php

+43-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Config\Resource\FileResource;
1818
use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass;
1919
use Symfony\Component\DependencyInjection\ContainerBuilder;
20+
use Symfony\Component\DependencyInjection\Extension\Extension;
2021
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
2122

2223
class MergeExtensionConfigurationPassTest extends TestCase
@@ -55,13 +56,10 @@ public function testExpressionLanguageProviderForwarding()
5556

5657
public function testExtensionConfigurationIsTrackedByDefault()
5758
{
58-
$extension = $this->getMockBuilder('Symfony\\Component\\DependencyInjection\\Extension\\Extension')->getMock();
59-
$extension->expects($this->once())
59+
$extension = $this->getMockBuilder(FooExtension::class)->setMethods(array('getConfiguration'))->getMock();
60+
$extension->expects($this->exactly(2))
6061
->method('getConfiguration')
6162
->will($this->returnValue(new FooConfiguration()));
62-
$extension->expects($this->any())
63-
->method('getAlias')
64-
->will($this->returnValue('foo'));
6563

6664
$container = new ContainerBuilder(new ParameterBag());
6765
$container->registerExtension($extension);
@@ -72,12 +70,51 @@ public function testExtensionConfigurationIsTrackedByDefault()
7270

7371
$this->assertContains(new FileResource(__FILE__), $container->getResources(), '', false, false);
7472
}
73+
74+
public function testOverriddenEnvsAreMerged()
75+
{
76+
$container = new ContainerBuilder();
77+
$container->registerExtension(new FooExtension());
78+
$container->prependExtensionConfig('foo', array('bar' => '%env(FOO)%'));
79+
$container->prependExtensionConfig('foo', array('bar' => '%env(BAR)%'));
80+
81+
$pass = new MergeExtensionConfigurationPass();
82+
$pass->process($container);
83+
84+
$this->assertSame(array('FOO'), array_keys($container->getParameterBag()->getEnvPlaceholders()));
85+
}
7586
}
7687

7788
class FooConfiguration implements ConfigurationInterface
7889
{
7990
public function getConfigTreeBuilder()
8091
{
81-
return new TreeBuilder();
92+
$treeBuilder = new TreeBuilder();
93+
$rootNode = $treeBuilder->root('foo');
94+
$rootNode
95+
->children()
96+
->scalarNode('bar')->end()
97+
->end();
98+
99+
return $treeBuilder;
100+
}
101+
}
102+
103+
class FooExtension extends Extension
104+
{
105+
public function getAlias()
106+
{
107+
return 'foo';
108+
}
109+
110+
public function getConfiguration(array $config, ContainerBuilder $container)
111+
{
112+
return new FooConfiguration();
113+
}
114+
115+
public function load(array $configs, ContainerBuilder $container)
116+
{
117+
$configuration = $this->getConfiguration($configs, $container);
118+
$config = $this->processConfiguration($configuration, $configs);
82119
}
83120
}

0 commit comments

Comments
 (0)