diff --git a/UPGRADE-7.1.md b/UPGRADE-7.1.md
index ac54aed0571bd..89113e16ac815 100644
--- a/UPGRADE-7.1.md
+++ b/UPGRADE-7.1.md
@@ -70,6 +70,7 @@ FrameworkBundle
* Mark classes `ConfigBuilderCacheWarmer`, `Router`, `SerializerCacheWarmer`, `TranslationsCacheWarmer`, `Translator` and `ValidatorCacheWarmer` as `final`
* Deprecate the `router.cache_dir` config option, the Router will always use the `kernel.build_dir` parameter
+ * Reset env vars when resetting the container
Intl
----
diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
index df0d692ebdf7a..4b0475167c04b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
+++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
@@ -15,6 +15,7 @@ CHANGELOG
* Attach the workflow's configuration to the `workflow` tag
* Add the `allowed_recipients` option for mailer to allow some users to receive
emails even if `recipients` is defined.
+ * Reset env vars when resetting the container
7.0
---
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.php
index a21d282702e13..8192f2f065c6f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.php
@@ -13,6 +13,7 @@
use Symfony\Bundle\FrameworkBundle\Secrets\DotenvVault;
use Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault;
+use Symfony\Component\DependencyInjection\StaticEnvVarLoader;
return static function (ContainerConfigurator $container) {
$container->services()
@@ -21,6 +22,9 @@
abstract_arg('Secret dir, set in FrameworkExtension'),
service('secrets.decryption_key')->ignoreOnInvalid(),
])
+
+ ->set('secrets.env_var_loader', StaticEnvVarLoader::class)
+ ->args([service('secrets.vault')])
->tag('container.env_var_loader')
->set('secrets.decryption_key')
diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md
index 0520a7add3791..54095a8d37ae5 100644
--- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md
+++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md
@@ -14,6 +14,7 @@ CHANGELOG
* [BC BREAK] When used in the `prependExtension()` method, the `ContainerConfigurator::import()` method now prepends the configuration instead of appending it
* Cast env vars to null or bool when referencing them using `#[Autowire(env: '...')]` depending on the signature of the corresponding parameter
* Add `#[AutowireInline]` attribute to allow service definition at the class level
+ * Add `StaticEnvVarLoader`
7.0
---
diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php
index 4e37fe9e43573..b0c9710a70af8 100644
--- a/src/Symfony/Component/DependencyInjection/Container.php
+++ b/src/Symfony/Component/DependencyInjection/Container.php
@@ -287,6 +287,8 @@ public function reset(): void
continue;
}
}
+
+ $this->envCache = [];
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
index 57392807f75ab..20b1d25c8323d 100644
--- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
+++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
@@ -14,15 +14,18 @@
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Contracts\Service\ResetInterface;
/**
* @author Nicolas Grekas
*/
-class EnvVarProcessor implements EnvVarProcessorInterface
+class EnvVarProcessor implements EnvVarProcessorInterface, ResetInterface
{
private ContainerInterface $container;
/** @var \Traversable */
private \Traversable $loaders;
+ /** @var \Traversable */
+ private \Traversable $originalLoaders;
private array $loadedVars = [];
/**
@@ -31,7 +34,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
public function __construct(ContainerInterface $container, ?\Traversable $loaders = null)
{
$this->container = $container;
- $this->loaders = $loaders ?? new \ArrayIterator();
+ $this->originalLoaders = $this->loaders = $loaders ?? new \ArrayIterator();
}
public static function getProvidedTypes(): array
@@ -366,4 +369,10 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed
throw new RuntimeException(sprintf('Unsupported env var prefix "%s" for env name "%s".', $prefix, $name));
}
+
+ public function reset(): void
+ {
+ $this->loadedVars = [];
+ $this->loaders = $this->originalLoaders;
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/StaticEnvVarLoader.php b/src/Symfony/Component/DependencyInjection/StaticEnvVarLoader.php
new file mode 100644
index 0000000000000..be1ada586e031
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/StaticEnvVarLoader.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection;
+
+class StaticEnvVarLoader implements EnvVarLoaderInterface
+{
+ private array $envVars;
+
+ public function __construct(private EnvVarLoaderInterface $envVarLoader)
+ {
+ }
+
+ public function loadEnvVars(): array
+ {
+ return $this->envVars ??= $this->envVarLoader->loadEnvVars();
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
index 54b036d80f053..cab51e4324d9d 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
@@ -136,6 +136,68 @@ public function testGetEnvBool($value, $processed)
$this->assertSame($processed, $result);
}
+ public function testGetEnvCachesEnv()
+ {
+ $_ENV['FOO'] = '';
+
+ $GLOBALS['ENV_FOO'] = 'value';
+
+ $loaders = function () {
+ yield new class() implements EnvVarLoaderInterface {
+ public function loadEnvVars(): array
+ {
+ return ['FOO' => $GLOBALS['ENV_FOO']];
+ }
+ };
+ };
+
+ $processor = new EnvVarProcessor(new Container(), new RewindableGenerator($loaders, 1));
+
+ $noop = function () {};
+
+ $result = $processor->getEnv('string', 'FOO', $noop);
+ $this->assertSame('value', $result);
+
+ $GLOBALS['ENV_FOO'] = 'new value';
+
+ $result = $processor->getEnv('string', 'FOO', $noop);
+ $this->assertSame('value', $result);
+
+ unset($_ENV['FOO'], $GLOBALS['ENV_FOO']);
+ }
+
+ public function testReset()
+ {
+ $_ENV['FOO'] = '';
+
+ $GLOBALS['ENV_FOO'] = 'value';
+
+ $loaders = function () {
+ yield new class() implements EnvVarLoaderInterface {
+ public function loadEnvVars(): array
+ {
+ return ['FOO' => $GLOBALS['ENV_FOO']];
+ }
+ };
+ };
+
+ $processor = new EnvVarProcessor(new Container(), new RewindableGenerator($loaders, 1));
+
+ $noop = function () {};
+
+ $result = $processor->getEnv('string', 'FOO', $noop);
+ $this->assertSame('value', $result);
+
+ $GLOBALS['ENV_FOO'] = 'new value';
+
+ $processor->reset();
+
+ $result = $processor->getEnv('string', 'FOO', $noop);
+ $this->assertSame('new value', $result);
+
+ unset($_ENV['FOO'], $GLOBALS['ENV_FOO']);
+ }
+
/**
* @dataProvider validBools
*/
@@ -625,7 +687,7 @@ public static function validNullables()
['null', 'null'],
['Null', 'Null'],
['NULL', 'NULL'],
- ];
+ ];
}
public function testRequireMissingFile()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/StaticEnvVarLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/StaticEnvVarLoaderTest.php
new file mode 100644
index 0000000000000..8c63a2d234ace
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/StaticEnvVarLoaderTest.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
+use Symfony\Component\DependencyInjection\StaticEnvVarLoader;
+
+class StaticEnvVarLoaderTest extends TestCase
+{
+ public function testLoadEnvVarsCachesInnerLoaderEnvVars()
+ {
+ $innerLoader = new class(['FOO' => 'BAR']) implements EnvVarLoaderInterface {
+ /** @param array */
+ public function __construct(public array $envVars = [])
+ {
+ }
+
+ public function loadEnvVars(): array
+ {
+ return $this->envVars;
+ }
+ };
+
+ $loader = new StaticEnvVarLoader($innerLoader);
+ $this->assertSame(['FOO' => 'BAR'], $loader->loadEnvVars());
+
+ $innerLoader->envVars = ['BAR' => 'BAZ'];
+ $this->assertSame(['FOO' => 'BAR'], $loader->loadEnvVars());
+ }
+}