diff --git a/src/Symfony/Component/Dotenv/CHANGELOG.md b/src/Symfony/Component/Dotenv/CHANGELOG.md index 3cf07eecc86eb..ea9935fdb9adc 100644 --- a/src/Symfony/Component/Dotenv/CHANGELOG.md +++ b/src/Symfony/Component/Dotenv/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.2 +--- + + * Add new `filter` argument to `debug:dotenv` command to be able to filter certain variable + 5.4 --- diff --git a/src/Symfony/Component/Dotenv/Command/DebugCommand.php b/src/Symfony/Component/Dotenv/Command/DebugCommand.php index 8827460caf432..9d51855e9359f 100644 --- a/src/Symfony/Component/Dotenv/Command/DebugCommand.php +++ b/src/Symfony/Component/Dotenv/Command/DebugCommand.php @@ -13,6 +13,7 @@ use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -47,6 +48,25 @@ public function __construct(string $kernelEnvironment, string $projectDirectory) parent::__construct(); } + protected function configure(): void + { + $this + ->setDefinition([ + new InputArgument('filter', InputArgument::OPTIONAL, 'The name of an environment variable or a filter.', null, $this->getAvailableVars(...)), + ]) + ->setHelp(<<<'EOT' +The %command.full_name% command displays all the environment variables configured by dotenv: + + php %command.full_name% + +To get specific variable, specify its full or partial name: + + php %command.full_name% FOO_BAR + +EOT + ); + } + protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -78,25 +98,36 @@ protected function execute(InputInterface $input, OutputInterface $output): int : sprintf('⨯ %s', $envFile); }, $envFiles)); + $nameFilter = $input->getArgument('filter'); + $variables = $this->getVariables($availableFiles, $nameFilter); + $io->section('Variables'); - $io->table( - array_merge(['Variable', 'Value'], $availableFiles), - $this->getVariables($availableFiles) - ); - $io->comment('Note real values might be different between web and CLI.'); + if ($variables || null === $nameFilter) { + $io->table( + array_merge(['Variable', 'Value'], $availableFiles), + $this->getVariables($availableFiles, $nameFilter) + ); + + $io->comment('Note that values might be different between web and CLI.'); + } else { + $io->warning(sprintf('No variables match the given filter "%s".', $nameFilter)); + } return 0; } - private function getVariables(array $envFiles): array + private function getVariables(array $envFiles, ?string $nameFilter): array { - $vars = explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? ''); - sort($vars); + $vars = $this->getAvailableVars(); $output = []; $fileValues = []; foreach ($vars as $var) { + if (null !== $nameFilter && 0 !== stripos($var, $nameFilter)) { + continue; + } + $realValue = $_SERVER[$var]; $varDetails = [$var, $realValue]; foreach ($envFiles as $envFile) { @@ -113,6 +144,14 @@ private function getVariables(array $envFiles): array return $output; } + private function getAvailableVars(): array + { + $vars = explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? ''); + sort($vars); + + return $vars; + } + private function getEnvFiles(): array { $files = [ diff --git a/src/Symfony/Component/Dotenv/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Dotenv/Tests/Command/DebugCommandTest.php index b3b089e4559c9..906c992924c16 100644 --- a/src/Symfony/Component/Dotenv/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Component/Dotenv/Tests/Command/DebugCommandTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Helper\FormatterHelper; use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Tester\CommandCompletionTester; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Dotenv\Command\DebugCommand; use Symfony\Component\Dotenv\Dotenv; @@ -138,7 +139,88 @@ public function testWarningOnPhpEnvFile() $this->assertStringContainsString('[WARNING] Due to existing dump file (.env.local.php)', $output); } - private function executeCommand(string $projectDirectory, string $env): string + public function testScenario1InDevEnvWithNameFilter() + { + $output = $this->executeCommand(__DIR__.'/Fixtures/Scenario1', 'dev', ['filter' => 'FoO']); + + // Scanned Files + $this->assertStringContainsString('⨯ .env.local.php', $output); + $this->assertStringContainsString('⨯ .env.dev.local', $output); + $this->assertStringContainsString('⨯ .env.dev', $output); + $this->assertStringContainsString('✓ .env.local', $output); + $this->assertStringContainsString('✓ .env'.\PHP_EOL, $output); + + // Skipped Files + $this->assertStringNotContainsString('.env.prod', $output); + $this->assertStringNotContainsString('.env.test', $output); + $this->assertStringNotContainsString('.env.dist', $output); + + // Variables + $this->assertStringContainsString('Variable Value .env.local .env', $output); + $this->assertStringContainsString('FOO baz baz bar', $output); + $this->assertStringNotContainsString('TEST123 true n/a true', $output); + } + + public function testScenario1InProdEnvWithMissingNameFilter() + { + $output = $this->executeCommand(__DIR__.'/Fixtures/Scenario1', 'prod', ['filter' => 'unknown']); + + // Scanned Files + $this->assertStringContainsString('⨯ .env.local.php', $output); + $this->assertStringContainsString('✓ .env.prod.local', $output); + $this->assertStringContainsString('⨯ .env.prod', $output); + $this->assertStringContainsString('✓ .env.local', $output); + $this->assertStringContainsString('✓ .env'.\PHP_EOL, $output); + + // Skipped Files + $this->assertStringNotContainsString('.env.dev', $output); + $this->assertStringNotContainsString('.env.test', $output); + $this->assertStringNotContainsString('.env.dist', $output); + + // Variables + $this->assertStringContainsString('[WARNING] No variables match the given filter "unknown".', $output); + $this->assertStringNotContainsString('Variable Value .env.prod.local .env.local .env', $output); + $this->assertStringNotContainsString('FOO baz n/a baz bar', $output); + $this->assertStringNotContainsString('HELLO world world n/a n/a', $output); + $this->assertStringNotContainsString('TEST123 true n/a n/a true', $output); + } + + public function testScenario2InProdEnvWithNameFilterPrefix() + { + $output = $this->executeCommand(__DIR__.'/Fixtures/Scenario2', 'prod', ['filter' => 'tes']); + + // Scanned Files + $this->assertStringContainsString('✓ .env.local.php', $output); + $this->assertStringContainsString('⨯ .env.prod.local', $output); + $this->assertStringContainsString('✓ .env.prod', $output); + $this->assertStringContainsString('⨯ .env.local', $output); + $this->assertStringContainsString('✓ .env.dist', $output); + + // Skipped Files + $this->assertStringNotContainsString('.env'.\PHP_EOL, $output); + $this->assertStringNotContainsString('.env.dev', $output); + $this->assertStringNotContainsString('.env.test', $output); + + // Variables + $this->assertStringContainsString('Variable Value .env.local.php .env.prod .env.dist', $output); + $this->assertStringNotContainsString('FOO BaR BaR BaR n/a', $output); + $this->assertStringContainsString('TEST 1234 1234 1234 0000', $output); + } + + public function testCompletion() + { + $env = 'prod'; + $projectDirectory = __DIR__.'/Fixtures/Scenario2'; + + $_SERVER['TEST_ENV_KEY'] = $env; + (new Dotenv('TEST_ENV_KEY'))->bootEnv($projectDirectory.'/.env'); + + $command = new DebugCommand($env, $projectDirectory); + $tester = new CommandCompletionTester($command); + $this->assertSame(['FOO', 'HELLO', 'TEST', 'TEST123'], $tester->complete([''])); + } + + private function executeCommand(string $projectDirectory, string $env, array $input = []): string { $_SERVER['TEST_ENV_KEY'] = $env; (new Dotenv('TEST_ENV_KEY'))->bootEnv($projectDirectory.'/.env'); @@ -146,7 +228,7 @@ private function executeCommand(string $projectDirectory, string $env): string $command = new DebugCommand($env, $projectDirectory); $command->setHelperSet(new HelperSet([new FormatterHelper()])); $tester = new CommandTester($command); - $tester->execute([]); + $tester->execute($input); return $tester->getDisplay(); }