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

Skip to content

Commit 84f5bdc

Browse files
bug #53631 [DependencyInjection] Fix loading all env vars from secrets when only a subset is needed (nicolas-grekas)
This PR was merged into the 6.4 branch. Discussion ---------- [DependencyInjection] Fix loading all env vars from secrets when only a subset is needed | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix #53429 | License | MIT As spotted in the linked issue, we have a significant performance issue when loading env vars. The issue is that when one env var is needed but not defined, we still decrypt all existing env vars. This also means we have a scalability issue when the number of env vars increases. Since this happens usually on every single requests, this can burn the CPU. The solution implemented in this PR is to use lazy strings to load env vars so that we don't decrypt them unless they are actually needed. Commits ------- 1d6f795 [DependencyInjection] Fix loading all env vars from secrets when only a subset is needed
2 parents 7aab885 + 1d6f795 commit 84f5bdc

File tree

5 files changed

+42
-7
lines changed

5 files changed

+42
-7
lines changed

src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\FrameworkBundle\Secrets;
1313

1414
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
15+
use Symfony\Component\String\LazyString;
1516
use Symfony\Component\VarExporter\VarExporter;
1617

1718
/**
@@ -169,7 +170,14 @@ public function list(bool $reveal = false): array
169170

170171
public function loadEnvVars(): array
171172
{
172-
return $this->list(true);
173+
$envs = [];
174+
$reveal = $this->reveal(...);
175+
176+
foreach ($this->list() as $name => $value) {
177+
$envs[$name] = LazyString::fromCallable($reveal, $name);
178+
}
179+
180+
return $envs;
173181
}
174182

175183
private function loadKeys(): void

src/Symfony/Component/DependencyInjection/EnvVarLoaderInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
interface EnvVarLoaderInterface
2020
{
2121
/**
22-
* @return string[] Key/value pairs that can be accessed using the regular "%env()%" syntax
22+
* @return array<string|\Stringable> Key/value pairs that can be accessed using the regular "%env()%" syntax
2323
*/
2424
public function loadEnvVars(): array;
2525
}

src/Symfony/Component/DependencyInjection/EnvVarProcessor.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,16 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed
164164
if (false !== $i || 'string' !== $prefix) {
165165
$env = $getEnv($name);
166166
} elseif ('' === ($env = $_ENV[$name] ?? (str_starts_with($name, 'HTTP_') ? null : ($_SERVER[$name] ?? null)))
167-
|| (false !== $env && false === ($env = $env ?? getenv($name) ?? false)) // null is a possible value because of thread safety issues
167+
|| (false !== $env && false === $env ??= getenv($name) ?? false) // null is a possible value because of thread safety issues
168168
) {
169-
foreach ($this->loadedVars as $vars) {
170-
if (false !== ($env = ($vars[$name] ?? $env)) && '' !== $env) {
169+
foreach ($this->loadedVars as $i => $vars) {
170+
if (false === $env = $vars[$name] ?? $env) {
171+
continue;
172+
}
173+
if ($env instanceof \Stringable) {
174+
$this->loadedVars[$i][$name] = $env = (string) $env;
175+
}
176+
if ('' !== ($env ?? '')) {
171177
break;
172178
}
173179
}
@@ -185,7 +191,13 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed
185191
continue;
186192
}
187193
$this->loadedVars[] = $vars = $loader->loadEnvVars();
188-
if (false !== ($env = ($vars[$name] ?? $env)) && '' !== $env) {
194+
if (false === $env = $vars[$name] ?? $env) {
195+
continue;
196+
}
197+
if ($env instanceof \Stringable) {
198+
$this->loadedVars[array_key_last($this->loadedVars)][$name] = $env = (string) $env;
199+
}
200+
if ('' !== ($env ?? '')) {
189201
$ended = false;
190202
break;
191203
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,12 @@ public function loadEnvVars(): array
808808
return [
809809
'FOO_ENV_LOADER' => '123',
810810
'BAZ_ENV_LOADER' => '',
811+
'LAZY_ENV_LOADER' => new class() {
812+
public function __toString()
813+
{
814+
return '';
815+
}
816+
},
811817
];
812818
}
813819
};
@@ -819,6 +825,12 @@ public function loadEnvVars(): array
819825
'FOO_ENV_LOADER' => '234',
820826
'BAR_ENV_LOADER' => '456',
821827
'BAZ_ENV_LOADER' => '567',
828+
'LAZY_ENV_LOADER' => new class() {
829+
public function __toString()
830+
{
831+
return '678';
832+
}
833+
},
822834
];
823835
}
824836
};
@@ -841,6 +853,9 @@ public function loadEnvVars(): array
841853
$result = $processor->getEnv('string', 'FOO_ENV_LOADER', function () {});
842854
$this->assertSame('123', $result); // check twice
843855

856+
$result = $processor->getEnv('string', 'LAZY_ENV_LOADER', function () {});
857+
$this->assertSame('678', $result);
858+
844859
unset($_ENV['BAZ_ENV_LOADER']);
845860
unset($_ENV['BUZ_ENV_LOADER']);
846861
}

src/Symfony/Component/String/LazyString.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static function fromCallable(callable|array $callback, mixed ...$argument
3939
$callback[1] ??= '__invoke';
4040
}
4141
$value = $callback(...$arguments);
42-
$callback = self::getPrettyName($callback);
42+
$callback = !\is_scalar($value) && !$value instanceof \Stringable ? self::getPrettyName($callback) : 'callable';
4343
$arguments = null;
4444
}
4545

0 commit comments

Comments
 (0)