Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Dotenv] Handle dynamic variables in multiple .env files#48636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
valx76 wants to merge6 commits intosymfony:7.4
base:7.4
Choose a base branch
Loading
fromvalx76:fix_multi_env_dynamic_var
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 70 additions & 16 deletionssrc/Symfony/Component/Dotenv/Dotenv.php
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,7 +80,13 @@ public function usePutenv(bool $usePutenv = true): static
*/
public function load(string $path, string ...$extraPaths): void
{
$this->doLoad(false, \func_get_args());
$needsAutoValueResolution = 1 === \func_num_args();

$this->doLoad(false, $needsAutoValueResolution, \func_get_args());

if (!$needsAutoValueResolution) {
$this->resolveAllVariables();
}
}

/**
Expand All@@ -99,33 +105,45 @@ public function load(string $path, string ...$extraPaths): void
*/
public function loadEnv(string $path, string $envKey = null, string $defaultEnv = 'dev', array $testEnvs = ['test'], bool $overrideExistingVars = false): void
{
// FIXME - Keep 'false' for loadEnv() or try to know if we will have multiple files to load?
// -> The environment is grabbed on the first doLoad() call so we cannot check conditions (except by loading the files multiple times..)
$needsAutoValueResolution = false;

$k = $envKey ?? $this->envKey;

if (is_file($path) || !is_file($p = "$path.dist")) {
$this->doLoad($overrideExistingVars, [$path]);
$this->doLoad($overrideExistingVars,$needsAutoValueResolution,[$path]);
} else {
$this->doLoad($overrideExistingVars, [$p]);
$this->doLoad($overrideExistingVars,$needsAutoValueResolution,[$p]);
}

if (null === $env = $_SERVER[$k] ?? $_ENV[$k] ?? null) {
$this->populate([$k => $env = $defaultEnv], $overrideExistingVars);
}

if (!\in_array($env, $testEnvs, true) && is_file($p = "$path.local")) {
$this->doLoad($overrideExistingVars, [$p]);
$this->doLoad($overrideExistingVars,$needsAutoValueResolution,[$p]);
$env = $_SERVER[$k] ?? $_ENV[$k] ?? $env;
}

if ('local' === $env) {
if (!$needsAutoValueResolution) {
$this->resolveAllVariables();
}

return;
}

if (is_file($p = "$path.$env")) {
$this->doLoad($overrideExistingVars, [$p]);
$this->doLoad($overrideExistingVars,$needsAutoValueResolution,[$p]);
}

if (is_file($p = "$path.$env.local")) {
$this->doLoad($overrideExistingVars, [$p]);
$this->doLoad($overrideExistingVars, $needsAutoValueResolution, [$p]);
}

if (!$needsAutoValueResolution) {
$this->resolveAllVariables();
}
}

Expand DownExpand Up@@ -166,7 +184,13 @@ public function bootEnv(string $path, string $defaultEnv = 'dev', array $testEnv
*/
public function overload(string $path, string ...$extraPaths): void
{
$this->doLoad(true, \func_get_args());
$needsAutoValueResolution = 1 === \func_num_args();

$this->doLoad(true, $needsAutoValueResolution, \func_get_args());

if (!$needsAutoValueResolution) {
$this->resolveAllVariables();
}
}

/**
Expand DownExpand Up@@ -219,12 +243,13 @@ public function populate(array $values, bool $overrideExistingVars = false): voi
/**
* Parses the contents of an .env file.
*
* @param string $data The data to be parsed
* @param string $path The original file name where data where stored (used for more meaningful error messages)
* @param string $data The data to be parsed
* @param string $path The original file name where data where stored (used for more meaningful error messages)
* @param bool $needsValueResolution true when the value resolution needs to be done automatically
*
* @throws FormatException when a file has a syntax error
*/
public function parse(string $data, string $path = '.env'): array
public function parse(string $data, string $path = '.env', bool $needsValueResolution = true): array
{
$this->path = $path;
$this->data = str_replace(["\r\n", "\r"], "\n", $data);
Expand All@@ -245,7 +270,7 @@ public function parse(string $data, string $path = '.env'): array
break;

case self::STATE_VALUE:
$this->values[$name] = $this->lexValue();
$this->values[$name] = $this->lexValue($needsValueResolution);
$state = self::STATE_VARNAME;
break;
}
Expand DownExpand Up@@ -291,7 +316,7 @@ private function lexVarname(): string
return $matches[2];
}

private function lexValue(): string
private function lexValue(bool $needsValueResolution): string
{
if (preg_match('/[ \t]*+(?:#.*)?$/Am', $this->data, $matches, 0, $this->cursor)) {
$this->moveCursor($matches[0]);
Expand DownExpand Up@@ -340,7 +365,11 @@ private function lexValue(): string
++$this->cursor;
$value = str_replace(['\\"', '\r', '\n'], ['"', "\r", "\n"], $value);
$resolvedValue = $value;
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);

if ($needsValueResolution) {
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
}

$resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars);
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);
$v .= $resolvedValue;
Expand All@@ -363,7 +392,11 @@ private function lexValue(): string
}
$value = rtrim($value);
$resolvedValue = $value;
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);

if ($needsValueResolution) {
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
}

$resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars);
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);

Expand DownExpand Up@@ -545,14 +578,35 @@ private function createFormatException(string $message): FormatException
return new FormatException($message, new FormatExceptionContext($this->data, $this->path, $this->lineno, $this->cursor));
}

private function doLoad(bool $overrideExistingVars, array $paths): void
private function doLoad(bool $overrideExistingVars,bool $needsValueResolution,array $paths): void
{
foreach ($paths as $path) {
if (!is_readable($path) || is_dir($path)) {
throw new PathException($path);
}

$this->populate($this->parse(file_get_contents($path), $path), $overrideExistingVars);
$this->populate($this->parse(file_get_contents($path), $path, $needsValueResolution), $overrideExistingVars);
}
}

private function resolveAllVariables(): void
{
$resolvedVars = [];
$loadedVars = array_flip(explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? $_ENV['SYMFONY_DOTENV_VARS'] ?? ''));
unset($loadedVars['']);

foreach ($_ENV as $name => $value) {
if ('SYMFONY_DOTENV_VARS' === $name) {
continue;
}

$resolvedValue = $this->resolveVariables($value, $loadedVars);

if ($resolvedValue !== $value) {
$resolvedVars[$name] = $resolvedValue;
}
}

$this->populate($resolvedVars, true);
}
}
126 changes: 126 additions & 0 deletionssrc/Symfony/Component/Dotenv/Tests/DotenvTest.php
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -224,6 +224,53 @@ public function testLoad()
$this->assertSame('BAZ', $bar);
}

public function testLoadDynamicVar()
{
unset($_ENV['VAR_STATIC1']);
unset($_ENV['VAR_STATIC2']);
unset($_ENV['VAR_DYNAMIC1']);
unset($_ENV['VAR_DYNAMIC2']);
unset($_ENV['VAR_DYNAMIC3']);
unset($_ENV['VAR_DYNAMIC_FAIL']);
unset($_SERVER['VAR_STATIC1']);
unset($_SERVER['VAR_STATIC2']);
unset($_SERVER['VAR_DYNAMIC1']);
unset($_SERVER['VAR_DYNAMIC2']);
unset($_SERVER['VAR_DYNAMIC3']);
unset($_SERVER['VAR_DYNAMIC_FAIL']);

@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');

$path1 = tempnam($tmpdir, 'sf-');
$path2 = tempnam($tmpdir, 'sf-');
$path3 = tempnam($tmpdir, 'sf-');

file_put_contents($path1, "VAR_STATIC1=env\nVAR_DYNAMIC1=profile:\${VAR_STATIC1}\nVAR_DYNAMIC_FAIL=\$VAR_DYNAMIC1");
file_put_contents($path2, 'VAR_STATIC1=local');
file_put_contents($path3, "VAR_STATIC2=file3\nVAR_DYNAMIC2=\$VAR_STATIC1\nVAR_DYNAMIC3=\$VAR_STATIC2");

(new Dotenv())->usePutenv()->load($path1, $path2, $path3);

$static1 = getenv('VAR_STATIC1');
$static2 = getenv('VAR_STATIC2');
$dynamic1 = getenv('VAR_DYNAMIC1');
$dynamic2 = getenv('VAR_DYNAMIC2');
$dynamic3 = getenv('VAR_DYNAMIC3');
$dynamicFail = getenv('VAR_DYNAMIC_FAIL');

unlink($path1);
unlink($path2);
unlink($path3);
rmdir($tmpdir);

$this->assertSame('local', $static1);
$this->assertSame('file3', $static2);
$this->assertSame('profile:local', $dynamic1);
$this->assertSame('local', $dynamic2);
$this->assertSame('file3', $dynamic3);
$this->assertSame('profile:${VAR_STATIC1}', $dynamicFail);
}

public function testLoadEnv()
{
$resetContext = static function (): void {
Expand DownExpand Up@@ -350,6 +397,38 @@ public function testLoadEnv()
rmdir($tmpdir);
}

public function testLoadEnvDynamicVar()
{
unset($_ENV['VAR_STATIC1']);
unset($_ENV['VAR_DYNAMIC1']);
unset($_ENV['VAR_DYNAMIC_FAIL']);
unset($_SERVER['VAR_STATIC1']);
unset($_SERVER['VAR_DYNAMIC1']);
unset($_SERVER['VAR_DYNAMIC_FAIL']);

@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');

$pathEnv = tempnam($tmpdir, 'sf-');
$pathLocalEnv = "$pathEnv.local";

file_put_contents($pathEnv, "VAR_STATIC1=env\nVAR_DYNAMIC1=profile:\${VAR_STATIC1}\nVAR_DYNAMIC_FAIL=\$VAR_DYNAMIC1");
file_put_contents($pathLocalEnv, 'VAR_STATIC1=local');

(new Dotenv())->usePutenv()->loadEnv($pathEnv, 'TEST_APP_ENV');

$static1 = getenv('VAR_STATIC1');
$dynamic1 = getenv('VAR_DYNAMIC1');
$dynamicFail = getenv('VAR_DYNAMIC_FAIL');

unlink($pathEnv);
unlink($pathLocalEnv);
rmdir($tmpdir);

$this->assertSame('local', $static1);
$this->assertSame('profile:local', $dynamic1);
$this->assertSame('profile:${VAR_STATIC1}', $dynamicFail);
}

public function testOverload()
{
unset($_ENV['FOO']);
Expand DownExpand Up@@ -385,6 +464,53 @@ public function testOverload()
$this->assertSame('BAZ', $bar);
}

public function testOverloadDynamicVar()
{
unset($_ENV['VAR_STATIC1']);
unset($_ENV['VAR_STATIC2']);
unset($_ENV['VAR_DYNAMIC1']);
unset($_ENV['VAR_DYNAMIC2']);
unset($_ENV['VAR_DYNAMIC3']);
unset($_ENV['VAR_DYNAMIC_FAIL']);
unset($_SERVER['VAR_STATIC1']);
unset($_SERVER['VAR_STATIC2']);
unset($_SERVER['VAR_DYNAMIC1']);
unset($_SERVER['VAR_DYNAMIC2']);
unset($_SERVER['VAR_DYNAMIC3']);
unset($_SERVER['VAR_DYNAMIC_FAIL']);

@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');

$path1 = tempnam($tmpdir, 'sf-');
$path2 = tempnam($tmpdir, 'sf-');
$path3 = tempnam($tmpdir, 'sf-');

file_put_contents($path1, "VAR_STATIC1=env\nVAR_DYNAMIC1=profile:\${VAR_STATIC1}\nVAR_DYNAMIC_FAIL=\$VAR_DYNAMIC1");
file_put_contents($path2, 'VAR_STATIC1=local');
file_put_contents($path3, "VAR_STATIC2=file3\nVAR_DYNAMIC2=\$VAR_STATIC1\nVAR_DYNAMIC3=\$VAR_STATIC2");

(new Dotenv())->usePutenv()->overload($path1, $path2, $path3);

$static1 = getenv('VAR_STATIC1');
$static2 = getenv('VAR_STATIC2');
$dynamic1 = getenv('VAR_DYNAMIC1');
$dynamic2 = getenv('VAR_DYNAMIC2');
$dynamic3 = getenv('VAR_DYNAMIC3');
$dynamicFail = getenv('VAR_DYNAMIC_FAIL');

unlink($path1);
unlink($path2);
unlink($path3);
rmdir($tmpdir);

$this->assertSame('local', $static1);
$this->assertSame('file3', $static2);
$this->assertSame('profile:local', $dynamic1);
$this->assertSame('local', $dynamic2);
$this->assertSame('file3', $dynamic3);
$this->assertSame('profile:${VAR_STATIC1}', $dynamicFail);
}

public function testLoadDirectory()
{
$this->expectException(PathException::class);
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp