diff --git a/src/Symfony/Component/Dotenv/CHANGELOG.md b/src/Symfony/Component/Dotenv/CHANGELOG.md
index e004ed8d75d67..29fa3681bf557 100644
--- a/src/Symfony/Component/Dotenv/CHANGELOG.md
+++ b/src/Symfony/Component/Dotenv/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+5.4
+---
+
+ * Add `dotenv:dump` command to compile the contents of the .env files into a PHP-optimized file called `.env.local.php`
+
5.1.0
-----
diff --git a/src/Symfony/Component/Dotenv/Command/DotenvDumpCommand.php b/src/Symfony/Component/Dotenv/Command/DotenvDumpCommand.php
new file mode 100644
index 0000000000000..e044199433f0f
--- /dev/null
+++ b/src/Symfony/Component/Dotenv/Command/DotenvDumpCommand.php
@@ -0,0 +1,113 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Dotenv\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
+use Symfony\Component\Dotenv\Dotenv;
+
+/**
+ * A console command to compile .env files into a PHP-optimized file called .env.local.php.
+ *
+ * @internal
+ */
+#[Autoconfigure(bind: ['$dotenvPath' => '%kernel.project_dir%/.env', '$defaultEnv' => '%kernel.environment%'])]
+final class DotenvDumpCommand extends Command
+{
+ protected static $defaultName = 'dotenv:dump';
+ protected static $defaultDescription = 'Compiles .env files to .env.local.php';
+
+ private $dotenvPath;
+ private $defaultEnv;
+
+ public function __construct(string $dotenvPath, string $defaultEnv = null)
+ {
+ $this->dotenvPath = $dotenvPath;
+ $this->defaultEnv = $defaultEnv;
+
+ parent::__construct();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setDefinition([
+ new InputArgument('env', null === $this->defaultEnv ? InputArgument::REQUIRED : InputArgument::OPTIONAL, 'The application environment to dump .env files for - e.g. "prod".'),
+ ])
+ ->addOption('empty', null, InputOption::VALUE_NONE, 'Ignore the content of .env files')
+ ->setHelp(<<<'EOT'
+The %command.name% command compiles .env files into a PHP-optimized file called .env.local.php.
+
+ %command.full_name%
+EOT
+ )
+ ;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $env = $input->getArgument('env') ?? $this->defaultEnv;
+
+ if ($input->getOption('empty')) {
+ $vars = ['APP_ENV' => $env];
+ } else {
+ $vars = $this->loadEnv($env);
+ $env = $vars['APP_ENV'];
+ }
+
+ $vars = var_export($vars, true);
+ $vars = <<dotenvPath.'.local.php', $vars, \LOCK_EX);
+
+ $output->writeln(sprintf('Successfully dumped .env files in .env.local.php> for the %s> environment.', $env));
+
+ return 0;
+ }
+
+ private function loadEnv(string $env): array
+ {
+ $dotenv = new Dotenv();
+ $composerFile = \dirname($this->dotenvPath).'/composer.json';
+ $testEnvs = (is_file($composerFile) ? json_decode(file_get_contents($composerFile), true) : [])['extra']['runtime']['test_envs'] ?? ['test'];
+
+ $globalsBackup = [$_SERVER, $_ENV];
+ unset($_SERVER['APP_ENV']);
+ $_ENV = ['APP_ENV' => $env];
+ $_SERVER['SYMFONY_DOTENV_VARS'] = implode(',', array_keys($_SERVER));
+
+ try {
+ $dotenv->loadEnv($this->dotenvPath, null, 'dev', $testEnvs);
+ unset($_ENV['SYMFONY_DOTENV_VARS']);
+
+ return $_ENV;
+ } finally {
+ [$_SERVER, $_ENV] = $globalsBackup;
+ }
+ }
+}
diff --git a/src/Symfony/Component/Dotenv/Tests/Command/DotenvDumpCommandTest.php b/src/Symfony/Component/Dotenv/Tests/Command/DotenvDumpCommandTest.php
new file mode 100644
index 0000000000000..c1f3e959f9199
--- /dev/null
+++ b/src/Symfony/Component/Dotenv/Tests/Command/DotenvDumpCommandTest.php
@@ -0,0 +1,102 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Dotenv\Tests\Command;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Tester\CommandTester;
+use Symfony\Component\Dotenv\Command\DotenvDumpCommand;
+
+class DotenvDumpCommandTest extends TestCase
+{
+ protected function setUp(): void
+ {
+ file_put_contents(__DIR__.'/.env', <<createCommand();
+ $command->execute([
+ 'env' => 'test',
+ ]);
+
+ $this->assertFileExists(__DIR__.'/.env.local.php');
+
+ $vars = require __DIR__.'/.env.local.php';
+ $this->assertSame([
+ 'APP_ENV' => 'test',
+ 'APP_SECRET' => 'abc123',
+ ], $vars);
+ }
+
+ public function testExecuteEmpty()
+ {
+ $command = $this->createCommand();
+ $command->execute([
+ 'env' => 'test',
+ '--empty' => true,
+ ]);
+
+ $this->assertFileExists(__DIR__.'/.env.local.php');
+
+ $vars = require __DIR__.'/.env.local.php';
+ $this->assertSame(['APP_ENV' => 'test'], $vars);
+ }
+
+ public function testExecuteTestEnvs()
+ {
+ file_put_contents(__DIR__.'/composer.json', <<createCommand();
+ $command->execute([
+ 'env' => 'test',
+ ]);
+
+ $this->assertFileExists(__DIR__.'/.env.local.php');
+
+ $vars = require __DIR__.'/.env.local.php';
+ $this->assertSame([
+ 'APP_ENV' => 'test',
+ 'APP_SECRET' => 'abc123',
+ 'APP_LOCAL' => 'yes',
+ ], $vars);
+ }
+
+ private function createCommand(): CommandTester
+ {
+ $application = new Application();
+ $application->add(new DotenvDumpCommand(__DIR__.'/.env'));
+
+ return new CommandTester($application->find('dotenv:dump'));
+ }
+}
diff --git a/src/Symfony/Component/Dotenv/composer.json b/src/Symfony/Component/Dotenv/composer.json
index 9bd6cda177f1c..8ef8831988bee 100644
--- a/src/Symfony/Component/Dotenv/composer.json
+++ b/src/Symfony/Component/Dotenv/composer.json
@@ -20,6 +20,7 @@
"symfony/deprecation-contracts": "^2.1"
},
"require-dev": {
+ "symfony/console": "^4.4|^5.0|^6.0",
"symfony/process": "^4.4|^5.0|^6.0"
},
"autoload": {