8000 feature #52638 [Dotenv] Add `SYMFONY_DOTENV_PATH`, consumed by `debug… · symfony/symfony@b9c61b6 · GitHub
[go: up one dir, main page]

Skip to content

Commit b9c61b6

Browse files
feature #52638 [Dotenv] Add SYMFONY_DOTENV_PATH, consumed by debug:dotenv for custom .env path (GromNaN)
This PR was merged into the 7.1 branch. Discussion ---------- [Dotenv] Add `SYMFONY_DOTENV_PATH`, consumed by `debug:dotenv` for custom `.env` path | Q | A | ------------- | --- | Branch? | 7.1 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix #47880 | License | MIT Continuation of #47901 Introduce `SYMFONY_DOTENV_PATH` set by DotEnv class and read by the `debug:dotenv` command to contextualize the debug info with the file that was actually parsed. The custom path can be set in many ways: - Doing a call to `(new Dotenv())->bootEnv(dirname(__DIR__).'my/custom/path/to/.env');` - In `composer.json`: `"extra": { "runtime": { "dotenv_path": "my/custom/path/to/.env" }` - With the env var: `$_SERVER['APP_RUNTIME_OPTIONS'] = ['dotenv_path' => 'my/custom/path/to/.env'];` The dotenv file can be outside of the `project_dir`. Commits ------- 52b6416 DotEnv debug command aware of custom dotenv_path
2 parents 8168608 + 52b6416 commit b9c61b6

File tree

5 files changed

+85
-25
lines changed

5 files changed

+85
-25
lines changed

src/Symfony/Component/Dotenv/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.1
5+
---
6+
7+
* Add `SYMFONY_DOTENV_PATH` variable with the path to the `.env` file loaded by `Dotenv::loadEnv()` or `Dotenv::bootEnv()`
8+
49
6.2
510
---
611

src/Symfony/Component/Dotenv/Command/DebugCommand.php

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,23 @@ protected function execute(InputInterface $input, OutputInterface $output): int
7070
return 1;
7171
}
7272

73-
$envFiles = $this->getEnvFiles();
74-
$availableFiles = array_filter($envFiles, fn (string $file) => is_file($this->getFilePath($file)));
73+
$filePath = $_SERVER['SYMFONY_DOTENV_PATH'] ?? $this->projectDirectory.\DIRECTORY_SEPARATOR.'.env';
7574

76-
if (\in_array('.env.local.php', $availableFiles, true)) {
77-
$io->warning('Due to existing dump file (.env.local.php) all other dotenv files are skipped.');
75+
$envFiles = $this->getEnvFiles($filePath);
76+
$availableFiles = array_filter($envFiles, fn (string $file) => is_file($file));
77+
78+
if (\in_array(sprintf('%s.local.php', $filePath), $availableFiles, true)) {
79+
$io->warning(sprintf('Due to existing dump file (%s.local.php) all other dotenv files are skipped.', $this->getRelativeName($filePath)));
7880
}
7981

80-
if (is_file($this->getFilePath('.env')) && is_file($this->getFilePath('.env.dist'))) {
81-
$io->warning('The file .env.dist gets skipped due to the existence of .env.');
82+
if (is_file($filePath) && is_file(sprintf('%s.dist', $filePath))) {
83+
$io->warning(sprintf('The file %s.dist gets skipped due to the existence of %1$s.', $this->getRelativeName($filePath)));
8284
}
8385

8486
$io->section('Scanned Files (in descending priority)');
85-
$io->listing(array_map(static fn (string $envFile) => \in_array($envFile, $availableFiles, true)
86-
? sprintf('<fg=green>✓</> %s', $envFile)
87-
: sprintf('<fg=red>⨯</> %s', $envFile), $envFiles));
87+
$io->listing(array_map(fn (string $envFile) => \in_array($envFile, $availableFiles, true)
88+
? sprintf('<fg=green>✓</> %s', $this->getRelativeName($envFile))
89+
: sprintf('<fg=red>⨯</> %s', $this->getRelativeName($envFile)), $envFiles));
8890

8991
$nameFilter = $input->getArgument('filter');
9092
$variables = $this->getVariables($availableFiles, $nameFilter);
@@ -93,7 +95,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
9395

9496
if ($variables || null === $nameFilter) {
9597
$io->table(
96-
array_merge(['Variable', 'Value'], $availableFiles),
98+
array_merge(['Variable', 'Value'], array_map($this->getRelativeName(...), $availableFiles)),
9799
$this->getVariables($availableFiles, $nameFilter)
98100
);
99101

@@ -153,36 +155,38 @@ private function getAvailableVars(): array
153155
return $vars;
154156
}
155157

156-
private function getEnvFiles(): array
158+
private function getEnvFiles(string $filePath): array
157159
{
158160
$files = [
159-
'.env.local.php',
160-
sprintf('.env.%s.local', $this->kernelEnvironment),
161-
sprintf('.env.%s', $this->kernelEnvironment),
161+
sprintf('%s.local.php', $filePath),
162+
sprintf('%s.%s.local', $filePath, $this->kernelEnvironment),
163+
sprintf('%s.%s', $filePath, $this->kernelEnvironment),
162164
];
163165

164166
if ('test' !== $this->kernelEnvironment) {
165-
$files[] = '.env.local';
167+
$files[] = sprintf('%s.local', $filePath);
166168
}
167169

168-
if (!is_file($this->getFilePath('.env')) && is_file($this->getFilePath('.env.dist'))) {
169-
$files[] = '.env.dist';
170+
if (!is_file($filePath) && is_file(sprintf('%s.dist', $filePath))) {
171+
$files[] = sprintf('%s.dist', $filePath);
170172
} else {
171-
$files[] = '.env';
173+
$files[] = $filePath;
172174
}
173175

174176
return $files;
175177
}
176178

177-
private function getFilePath(string $file): string
179+
private function getRelativeName(string $filePath): string
178180
{
179-
return $this->projectDirectory.\DIRECTORY_SEPARATOR.$file;
181+
if (str_starts_with($filePath, $this->projectDirectory)) {
182+
return substr($filePath, \strlen($this->projectDirectory) + 1);
183+
}
184+
185+
return basename($filePath);
180186
}
181187

182-
private function loadValues(string $file): array
188+
private function loadValues(string $filePath): array
183189
{
184-
$filePath = $this->getFilePath($file);
185-
186190
if (str_ends_with($filePath, '.php')) {
187191
return include $filePath;
188192
}

src/Symfony/Component/Dotenv/Command/DotenvDumpCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ private function loadEnv(string $dotenvPath, string $env, array $config): array
107107
try {
108108
$dotenv->loadEnv($dotenvPath, null, 'dev', $testEnvs);
109109
unset($_ENV['SYMFONY_DOTENV_VARS']);
110+
unset($_ENV['SYMFONY_DOTENV_PATH']);
110111

111112
return $_ENV;
112113
} finally {

src/Symfony/Component/Dotenv/Dotenv.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ public function load(string $path, string ...$extraPaths): void
100100
*/
101101
public function loadEnv(string $path, string $envKey = null, string $defaultEnv = 'dev', array $testEnvs = ['test'], bool $overrideExistingVars = false): void
102102
{
103+
$this->populatePath($path);
104+
103105
$k = $envKey ?? $this->envKey;
104106

105107
if (is_file($path) || !is_file($p = "$path.dist")) {
@@ -144,6 +146,7 @@ public function bootEnv(string $path, string $defaultEnv = 'dev', array $testEnv
144146
$k = $this->envKey;
145147

146148
if (\is_array($env) && ($overrideExistingVars || !isset($env[$k]) || ($_SERVER[$k] ?? $_ENV[$k] ?? $env[$k]) === $env[$k])) {
149+
$this->populatePath($path);
147150
$this->populate($env, $overrideExistingVars);
148151
} else {
149152
$this->loadEnv($path, $k, $defaultEnv, $testEnvs, $overrideExistingVars);
@@ -556,4 +559,13 @@ private function doLoad(bool $overrideExistingVars, array $paths): void
556559
$this->populate($this->parse(file_get_contents($path), $path), $overrideExistingVars);
557560
}
558561
}
562+
563+
private function populatePath(string $path): void
564+
{
565+
$_ENV['SYMFONY_DOTENV_PATH'] = $_SERVER['SYMFONY_DOTENV_PATH'] = $path;
566+
567+
if ($this->usePutenv) {
568+
putenv('SYMFONY_DOTENV_PATH='.$path);
569+
}
570+
}
559571
}

src/Symfony/Component/Dotenv/Tests/Command/DebugCommandTest.php

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,28 @@ public function testScenario2InProdEnv()
150150
$this->assertStringContainsString('TEST 1234 1234 1234 0000', $output);
151151
}
152152

153+
public function testScenario2WithCustomPath()
154+
{
155+
$output = $this->executeCommand(__DIR__.'/Fixtures', 'prod', [], __DIR__.'/Fixtures/Scenario2/.env');
156+
157+
// Scanned Files
158+
$this->assertStringContainsString('✓ Scenario2/.env.local.php', $output);
159+
$this->assertStringContainsString('⨯ Scenario2/.env.prod.local', $output);
160+
$this->assertStringContainsString('✓ Scenario2/.env.prod', $output);
161+
$this->assertStringContainsString('⨯ Scenario2/.env.local', $output);
162+
$this->assertStringContainsString('✓ Scenario2/.env.dist', $output);
163+
164+
// Skipped Files
165+
$this->assertStringNotContainsString('.env'.\PHP_EOL, $output);
166+
$this->assertStringNotContainsString('.env.dev', $output);
167+
$this->assertStringNotContainsString('.env.test', $output);
168+
169+
// Variables
170+
$this->assertStringContainsString('Variable Value Scenario2/.env.local.php Scenario2/.env.prod Scenario2/.env.dist', $output);
171+
$this->assertStringContainsString('FOO BaR BaR BaR n/a', $output);
172+
$this->assertStringContainsString('TEST 1234 1234 1234 0000', $output);
173+
}
174+
153175
public function testWarningOnEnvAndEnvDistFile()
154176
{
155177
$output = $this->executeCommand(__DIR__.'/Fixtures/Scenario3', 'dev');
@@ -158,6 +180,14 @@ public function testWarningOnEnvAndEnvDistFile()
158180
$this->assertStringContainsString('[WARNING] The file .env.dist gets skipped', $output);
159181
}
160182

183+
public function testWarningOnEnvAndEnvDistFileWithCustomPath()
184+
{
185+
$output = $this->executeCommand(__DIR__.'/Fixtures', 'dev', dotenvPath: __DIR__.'/Fixtures/Scenario3/.env');
186+
187+
// Warning
188+
$this->assertStringContainsString('[WARNING] The file Scenario3/.env.dist gets skipped', $output);
189+
}
190+
161191
public function testWarningOnPhpEnvFile()
162192
{
163193
$output = $this->executeCommand(__DIR__.'/Fixtures/Scenario2', 'prod');
@@ -166,6 +196,14 @@ public function testWarningOnPhpEnvFile()
166196
$this->assertStringContainsString('[WARNING] Due to existing dump file (.env.local.php)', $output);
167197
}
168198

199+
public function testWarningOnPhpEnvFileWithCustomPath()
200+
{
201+
$output = $this->executeCommand(__DIR__.'/Fixtures', 'prod', dotenvPath: __DIR__.'/Fixtures/Scenario2/.env');
202+
203+
// Warning
204+
$this->assertStringContainsString('[WARNING] Due to existing dump file (Scenario2/.env.local.php)', $output);
205+
}
206+
169207
public function testScenario1InDevEnvWithNameFilter()
170208
{
171209
$output = $this->executeCommand(__DIR__.'/Fixtures/Scenario1', 'dev', ['filter' => 'FoO']);
@@ -252,10 +290,10 @@ public function testCompletion()
252290
$this->assertSame(['FOO', 'TEST'], $tester->complete(['']));
253291
}
254292

255-
private function executeCommand(string $projectDirectory, string $env, array $input = []): string
293+
private function executeCommand(string $projectDirectory, string $env, array $input = [], string $dotenvPath = null): string
256294
{
257295
$_SERVER['TEST_ENV_KEY'] = $env;
258-
(new Dotenv('TEST_ENV_KEY'))->bootEnv($projectDirectory.'/.env');
296+
(new Dotenv('TEST_ENV_KEY'))->bootEnv($dotenvPath ?? $projectDirectory.'/.env');
259297

260298
$command = new DebugCommand($env, $projectDirectory);
261299
$command->setHelperSet(new HelperSet([new FormatterHelper()]));

0 commit comments

Comments
 (0)
0