Skip to content

Commit 30f362d

Browse files
[DI] Fix resolving env vars when compiling a ContainerBuilder
1 parent 1732cc8 commit 30f362d

File tree

5 files changed

+118
-36
lines changed

5 files changed

+118
-36
lines changed

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,15 @@ public function __construct(Extension $extension, parent $resolvingBag)
9292
{
9393
$this->beforeProcessingEnvPlaceholders = $resolvingBag->getEnvPlaceholders();
9494
$config = $this->resolveEnvPlaceholders($extension->getProcessedConfigs());
95-
parent::__construct($this->resolveEnvReferences($config));
95+
parent::__construct($this->resolveValue($config));
96+
}
97+
98+
/**
99+
* {@inheritdoc}
100+
*/
101+
public function get($name)
102+
{
103+
return 0 === strpos($name, 'env(') && ')' === substr($name, -1) && 'env()' !== $name ? parent::get($name) : '';
96104
}
97105

98106
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\DependencyInjection\Definition;
15+
16+
/**
17+
* Replaces env var placeholders by their current values.
18+
*/
19+
class ResolveEnvPlaceholdersPass extends AbstractRecursivePass
20+
{
21+
protected function processValue($value, $isRoot = false)
22+
{
23+
if (is_string($value)) {
24+
return $this->container->resolveEnvPlaceholders($value, true);
25+
}
26+
if ($value instanceof Definition) {
27+
$changes = $value->getChanges();
28+
if (isset($changes['class'])) {
29+
$value->setClass($this->container->resolveEnvPlaceholders($value->getClass(), true));
30+
}
31+
if (isset($changes['file'])) {
32+
$value->setFile($this->container->resolveEnvPlaceholders($value->getFile(), true));
33+
}
34+
}
35+
36+
$value = parent::processValue($value, $isRoot);
37+
38+
if ($value && is_array($value)) {
39+
$value = array_combine($this->container->resolveEnvPlaceholders(array_keys($value), true), $value);
40+
}
41+
42+
return $value;
43+
}
44+
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

+39-9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\DependencyInjection\Compiler\Compiler;
1919
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
2020
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
21+
use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass;
2122
use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
2223
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
2324
use Symfony\Component\DependencyInjection\Exception\LogicException;
@@ -729,9 +730,7 @@ public function compile(/*$resolveEnvPlaceholders = false*/)
729730
$bag = $this->getParameterBag();
730731

731732
if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) {
732-
$this->parameterBag = new ParameterBag($bag->resolveEnvReferences($bag->all()));
733-
$this->envPlaceholders = $bag->getEnvPlaceholders();
734-
$this->parameterBag = $bag = new ParameterBag($this->resolveEnvPlaceholders($this->parameterBag->all(), true));
733+
$compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000);
735734
}
736735

737736
$compiler->compile($this);
@@ -744,11 +743,15 @@ public function compile(/*$resolveEnvPlaceholders = false*/)
744743

745744
$this->extensionConfigs = array();
746745

747-
parent::compile();
748-
749746
if ($bag instanceof EnvPlaceholderParameterBag) {
747+
if ($resolveEnvPlaceholders) {
748+
$this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true));
749+
}
750+
750751
$this->envPlaceholders = $bag->getEnvPlaceholders();
751752
}
753+
754+
parent::compile();
752755
}
753756

754757
/**
@@ -1313,12 +1316,19 @@ public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs
13131316
foreach ($envPlaceholders as $env => $placeholders) {
13141317
foreach ($placeholders as $placeholder) {
13151318
if (false !== stripos($value, $placeholder)) {
1316-
if (true !== $format) {
1319+
if (true === $format) {
1320+
$resolved = $bag->escapeValue($this->getEnv($env));
1321+
} else {
13171322
$resolved = sprintf($format, $env);
1318-
} elseif ($placeholder === $resolved = $bag->escapeValue($this->getEnv($env))) {
1319-
$resolved = $bag->all()[strtolower("env($env)")];
13201323
}
1321-
$value = str_ireplace($placeholder, $resolved, $value);
1324+
if ($placeholder === $value) {
1325+
$value = $resolved;
1326+
} else {
1327+
if (!is_string($resolved) && !is_numeric($resolved)) {
1328+
throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type %s inside string value "%s".', $env, gettype($resolved), $value));
1329+
}
1330+
$value = str_ireplace($placeholder, $resolved, $value);
1331+
}
13221332
$usedEnvs[$env] = $env;
13231333
$this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1;
13241334
}
@@ -1393,6 +1403,26 @@ public static function getServiceConditionals($value)
13931403
return $services;
13941404
}
13951405

1406+
/**
1407+
* {@inheritdoc}
1408+
*/
1409+
protected function getEnv($name)
1410+
{
1411+
$value = parent::getEnv($name);
1412+
1413+
if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag) {
1414+
foreach ($this->getParameterBag()->getEnvPlaceholders() as $env => $placeholders) {
1415+
if (isset($placeholders[$value])) {
1416+
$bag = new ParameterBag($this->getParameterBag()->all());
1417+
1418+
return $bag->unescapeValue($bag->get("env($name)"));
1419+
}
1420+
}
1421+
}
1422+
1423+
return $value;
1424+
}
1425+
13961426
/**
13971427
* Retrieves the currently set proxy instantiator or instantiates one.
13981428
*

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

-26
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
class EnvPlaceholderParameterBag extends ParameterBag
2121
{
2222
private $envPlaceholders = array();
23-
private $resolveEnvReferences = false;
2423

2524
/**
2625
* {@inheritdoc}
@@ -102,29 +101,4 @@ public function resolve()
102101
}
103102
}
104103
}
105-
106-
/**
107-
* Replaces "%env(FOO)%" references by their placeholder, keeping regular "%parameters%" references as is.
108-
*/
109-
public function resolveEnvReferences(array $value)
110-
{
111-
$this->resolveEnvReferences = true;
112-
try {
113-
return $this->resolveValue($value);
114-
} finally {
115-
$this->resolveEnvReferences = false;
116-
}
117-
}
118-
119-
/**
120-
* {@inheritdoc}
121-
*/
122-
public function resolveString($value, array $resolving = array())
123-
{
124-
if ($this->resolveEnvReferences) {
125-
return preg_replace_callback('/%%|%(env\([^%\s]+\))%/', function ($match) { return isset($match[1]) ? $this->get($match[1]) : '%%'; }, $value);
126-
}
127-
128-
return parent::resolveString($value, $resolving);
129-
}
130104
}

src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -623,19 +623,37 @@ public function testCompileWithResolveEnv()
623623

624624
$container = new ContainerBuilder();
625625
$container->setParameter('env(FOO)', 'Foo');
626+
$container->setParameter('env(DUMMY_ENV_VAR)', 'GHI');
626627
$container->setParameter('bar', '%% %env(DUMMY_ENV_VAR)% %env(DUMMY_SERVER_VAR)% %env(HTTP_DUMMY_VAR)%');
627628
$container->setParameter('foo', '%env(FOO)%');
628629
$container->setParameter('baz', '%foo%');
629630
$container->setParameter('env(HTTP_DUMMY_VAR)', '123');
631+
$container->register('teatime', 'stdClass')
632+
->setProperty('foo', '%env(DUMMY_ENV_VAR)%')
633+
;
630634
$container->compile(true);
631635

632636
$this->assertSame('% du%%y ABC 123', $container->getParameter('bar'));
633637
$this->assertSame('Foo', $container->getParameter('baz'));
638+
$this->assertSame('du%%y', $container->get('teatime')->foo);
634639

635640
unset($_SERVER['DUMMY_SERVER_VAR'], $_SERVER['HTTP_DUMMY_VAR']);
636641
putenv('DUMMY_ENV_VAR');
637642
}
638643

644+
/**
645+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
646+
* @expectedExceptionMessage A string value must be composed of strings and/or numbers, but found parameter "env(ARRAY)" of type array inside string value "ABC %env(ARRAY)%".
647+
*/
648+
public function testCompileWithArrayResolveEnv()
649+
{
650+
$bag = new TestingEnvPlaceholderParameterBag();
651+
$container = new ContainerBuilder($bag);
652+
$container->setParameter('foo', '%env(ARRAY)%');
653+
$container->setParameter('bar', 'ABC %env(ARRAY)%');
654+
$container->compile(true);
655+
}
656+
639657
/**
640658
* @expectedException \Symfony\Component\DependencyInjection\Exception\EnvNotFoundException
641659
* @expectedExceptionMessage Environment variable not found: "FOO".
@@ -1127,3 +1145,11 @@ public function __construct(A $a)
11271145
{
11281146
}
11291147
}
1148+
1149+
class TestingEnvPlaceholderParameterBag extends EnvPlaceholderParameterBag
1150+
{
1151+
public function get($name)
1152+
{
1153+
return 'env(array)' === strtolower($name) ? array(123) : parent::get($name);
1154+
}
1155+
}

0 commit comments

Comments
 (0)