Skip to content

Commit 9cd3847

Browse files
feature #1070 Fix dump-env command when .env files reference other env vars (nicolas-grekas)
This PR was merged into the 2.x branch. Discussion ---------- Fix dump-env command when .env files reference other env vars At the moment, running `composer dump-env` on a 7.4 project generates a broken `APP_SHARE_DIR`. The reason is that since symfony/recipes#1465 we define that var as `"$APP_PROJECT_DIR/var/share"` Yet, when the command runs, all env vars are emptied to make the result context-free. This fixes it by hooking into calls to the `getenv()` function. Commits ------- ed41273 Fix dump-env command when .env files reference other env vars
2 parents 2b99ec7 + ed41273 commit 9cd3847

File tree

2 files changed

+69
-12
lines changed

2 files changed

+69
-12
lines changed

src/Command/DumpEnvCommand.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
5555
}
5656

5757
$path = $this->options->get('root-dir').'/'.($runtime['dotenv_path'] ?? '.env');
58+
$GLOBALS['SYMFONY_DOTENV_VARS'] = [];
5859

5960
if (!$env || !$input->getOption('empty')) {
6061
$vars = $this->loadEnv($path, $env, $runtime);
@@ -66,6 +67,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6667
}
6768

6869
$vars = var_export($vars, true);
70+
71+
foreach ($GLOBALS['SYMFONY_DOTENV_VARS'] as $k => $v) {
72+
$k = var_export($k, true);
73+
$vars = str_replace($v, "'.(\$_ENV[{$k}] ?? ".(str_starts_with($k, "'HTTP_") ? '' : "\$_SERVER[{$k}] ?? ")."'').'", $vars);
74+
}
75+
unset($GLOBALS['SYMFONY_DOTENV_VARS']);
76+
$vars = strtr($vars, [
77+
"''.(" => '(',
78+
").''.(" => ').(',
79+
").''" => ')',
80+
]);
81+
6982
$vars = <<<EOF
7083
<?php
7184
@@ -124,3 +137,14 @@ private function loadEnv(string $path, ?string $env, array $runtime): array
124137
return $env;
125138
}
126139
}
140+
141+
namespace Symfony\Component\Dotenv;
142+
143+
function getenv(?string $name = null, bool $local_only = false): string|array|false
144+
{
145+
if (null === $name) {
146+
return \getenv($name, $local_only);
147+
}
148+
149+
return $GLOBALS['SYMFONY_DOTENV_VARS'][$name] ??= md5(random_bytes(10));
150+
}

tests/Command/DumpEnvCommandTest.php

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ public function testFileIsCreated()
2929
@unlink($envLocal);
3030

3131
$envContent = <<<EOF
32-
APP_ENV=dev
33-
APP_SECRET=abcdefgh123456789
34-
EOF;
32+
APP_ENV=dev
33+
APP_SECRET=abcdefgh123456789
34+
EOF;
3535
file_put_contents($env, $envContent);
3636

3737
$command = $this->createCommandDumpEnv();
@@ -60,9 +60,9 @@ public function testEmptyOptionMustIgnoreContent()
6060
@unlink($envLocal);
6161

6262
$envContent = <<<EOF
63-
APP_ENV=dev
64-
APP_SECRET=abcdefgh123456789
65-
EOF;
63+
APP_ENV=dev
64+
APP_SECRET=abcdefgh123456789
65+
EOF;
6666
file_put_contents($env, $envContent);
6767

6868
$command = $this->createCommandDumpEnv();
@@ -94,9 +94,9 @@ public function testEnvCanBeReferenced()
9494
@unlink($envLocal);
9595

9696
$envContent = <<<'EOF'
97-
BAR=$FOO
98-
FOO=123
99-
EOF;
97+
BAR=$FOO
98+
FOO=123
99+
EOF;
100100
file_put_contents($env, $envContent);
101101

102102
$_SERVER['FOO'] = 'Foo';
@@ -179,9 +179,9 @@ public function testLoadLocalEnvWhenTestEnvIsNotEqual()
179179

180180
file_put_contents($env, 'APP_ENV=dev');
181181
file_put_contents($envLocal, <<<EOF
182-
APP_ENV=test
183-
APP_SECRET=abcdefgh123456789
184-
EOF
182+
APP_ENV=test
183+
APP_SECRET=abcdefgh123456789
184+
EOF
185185
);
186186

187187
$command = $this->createCommandDumpEnv(['runtime' => ['test_envs' => []]]);
@@ -202,6 +202,39 @@ public function testLoadLocalEnvWhenTestEnvIsNotEqual()
202202
unlink($envLocalPhp);
203203
}
204204

205+
public function testEnvVarReferenceInDumpedFile()
206+
{
207+
@mkdir(FLEX_TEST_DIR);
208+
$env = FLEX_TEST_DIR.'/.env';
209+
$envLocal = FLEX_TEST_DIR.'/.env.local.php';
210+
211+
@unlink($env);
212+
@unlink($envLocal);
213+
214+
$envContent = <<<'EOF'
215+
APP_ENV=prod
216+
APP_SHARE_DIR=$APP_PROJECT_DIR/var/share
217+
EOF;
218+
file_put_contents($env, $envContent);
219+
220+
$command = $this->createCommandDumpEnv();
221+
$command->execute(['env' => 'prod']);
222+
223+
$dumpedContent = file_get_contents($envLocal);
224+
$this->assertStringContainsString("\$_ENV['APP_PROJECT_DIR']", $dumpedContent);
225+
$this->assertStringContainsString("\$_SERVER['APP_PROJECT_DIR']", $dumpedContent);
226+
227+
$_ENV['APP_PROJECT_DIR'] = '/path/to/project';
228+
$vars = require $envLocal;
229+
$this->assertSame([
230+
'APP_ENV' => 'prod',
231+
'APP_SHARE_DIR' => '/path/to/project/var/share',
232+
], $vars);
233+
234+
unlink($env);
235+
unlink($envLocal);
236+
}
237+
205238
private function createCommandDumpEnv(array $options = [])
206239
{
207240
$command = new DumpEnvCommand(

0 commit comments

Comments
 (0)