Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3fa5b3a

Browse files
Merge branch '3.3' into 3.4
* 3.3: [DI] Fix merging of env vars in configs
2 parents 50fe6a3 + 1732cc8 commit 3fa5b3a

File tree

5 files changed

+132
-13
lines changed

5 files changed

+132
-13
lines changed

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

Lines changed: 70 additions & 1 deletion
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,10 @@ public function process(ContainerBuilder $container)
4345
// this extension was not called
4446
continue;
4547
}
46-
$config = $container->getParameterBag()->resolveValue($config);
48+
// EnvPlaceholderParameterBag tracks env vars when calling resolveValue().
49+
// Clone so that tracking is done in a dedicated bag.
50+
$resolvingBag = clone $container->getParameterBag();
51+
$config = $resolvingBag->resolveValue($config);
4752

4853
$tmpContainer = new ContainerBuilder($container->getParameterBag());
4954
$tmpContainer->setResourceTracking($container->isTrackingResources());
@@ -58,6 +63,15 @@ public function process(ContainerBuilder $container)
5863

5964
$extension->load($config, $tmpContainer);
6065

66+
if ($resolvingBag instanceof EnvPlaceholderParameterBag) {
67+
// $resolvingBag keeps track of env vars encoutered *before* merging configs
68+
if ($extension instanceof Extension) {
69+
// but we don't want to keep track of env vars that are *overridden* when configs are merged
70+
$resolvingBag = new MergeExtensionConfigurationParameterBag($extension, $resolvingBag);
71+
}
72+
$container->getParameterBag()->mergeEnvPlaceholders($resolvingBag);
73+
}
74+
6175
$container->merge($tmpContainer);
6276
$container->getParameterBag()->add($parameters);
6377
}
@@ -66,3 +80,58 @@ public function process(ContainerBuilder $container)
6680
$container->addAliases($aliases);
6781
}
6882
}
83+
84+
/**
85+
* @internal
86+
*/
87+
class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag
88+
{
89+
private $beforeProcessingEnvPlaceholders;
90+
91+
public function __construct(Extension $extension, parent $resolvingBag)
92+
{
93+
$this->beforeProcessingEnvPlaceholders = $resolvingBag->getEnvPlaceholders();
94+
$config = $this->resolveEnvPlaceholders($extension->getProcessedConfigs());
95+
parent::__construct($this->resolveEnvReferences($config));
96+
}
97+
98+
/**
99+
* {@inheritdoc}
100+
*/
101+
public function getEnvPlaceholders()
102+
{
103+
// contains the list of env vars that are still used after configs have been merged
104+
$envPlaceholders = parent::getEnvPlaceholders();
105+
106+
foreach ($envPlaceholders as $env => $placeholders) {
107+
if (isset($this->beforeProcessingEnvPlaceholders[$env])) {
108+
// for still-used env vars, keep track of their before-processing placeholders
109+
$envPlaceholders[$env] += $this->beforeProcessingEnvPlaceholders[$env];
110+
}
111+
}
112+
113+
return $envPlaceholders;
114+
}
115+
116+
/**
117+
* Replaces-back env placeholders to their original "%env(FOO)%" version.
118+
*/
119+
private function resolveEnvPlaceholders($value)
120+
{
121+
if (is_array($value)) {
122+
foreach ($value as $k => $v) {
123+
$value[$this->resolveEnvPlaceholders($k)] = $this->resolveEnvPlaceholders($v);
124+
}
125+
} elseif (is_string($value)) {
126+
foreach ($this->beforeProcessingEnvPlaceholders as $env => $placeholders) {
127+
foreach ($placeholders as $placeholder) {
128+
if (false !== stripos($value, $placeholder)) {
129+
$value = str_ireplace($placeholder, "%env($env)%", $value);
130+
}
131+
}
132+
}
133+
}
134+
135+
return $value;
136+
}
137+
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 2 additions & 3 deletions
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

Lines changed: 15 additions & 1 deletion
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,19 @@ 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+
/**
100+
* @internal
101+
*/
102+
final public function getProcessedConfigs()
103+
{
104+
try {
105+
return $this->processedConfigs;
106+
} finally {
107+
$this->processedConfigs = array();
108+
}
95109
}
96110

97111
/**

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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 43 additions & 6 deletions
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)