Skip to content

Commit

Permalink
[Dotenv][Runtime] Add $overrideExistingVars to bootEnv() and loadEnv(…
Browse files Browse the repository at this point in the history
…) and dotenv_overload to SymfonyRuntime
  • Loading branch information
fancyweb committed Oct 26, 2021
1 parent bb0e32e commit 6fcefaa
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CHANGELOG

* Add `dotenv:dump` command to compile the contents of the .env files into a PHP-optimized file called `.env.local.php`
* Add `debug:dotenv` command to list all dotenv files with variables and values
* Add `$overrideExistingVars` on `Dotenv::bootEnv()` and `Dotenv::loadEnv()`

5.1.0
-----
Expand Down
22 changes: 10 additions & 12 deletions Dotenv.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,22 +106,22 @@ public function load(string $path, string ...$extraPaths): void
* @throws FormatException when a file has a syntax error
* @throws PathException when a file does not exist or is not readable
*/
public function loadEnv(string $path, string $envKey = null, string $defaultEnv = 'dev', array $testEnvs = ['test']): void
public function loadEnv(string $path, string $envKey = null, string $defaultEnv = 'dev', array $testEnvs = ['test'], bool $overrideExistingVars = false): void
{
$k = $envKey ?? $this->envKey;

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

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

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

Expand All @@ -130,11 +130,11 @@ public function loadEnv(string $path, string $envKey = null, string $defaultEnv
}

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

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

Expand All @@ -145,16 +145,16 @@ public function loadEnv(string $path, string $envKey = null, string $defaultEnv
*
* See method loadEnv() for rules related to .env files.
*/
public function bootEnv(string $path, string $defaultEnv = 'dev', array $testEnvs = ['test']): void
public function bootEnv(string $path, string $defaultEnv = 'dev', array $testEnvs = ['test'], bool $overrideExistingVars = false): void
{
$p = $path.'.local.php';
$env = is_file($p) ? include $p : null;
$k = $this->envKey;

if (\is_array($env) && (!isset($env[$k]) || ($_SERVER[$k] ?? $_ENV[$k] ?? $env[$k]) === $env[$k])) {
$this->populate($env);
$this->populate($env, $overrideExistingVars);
} else {
$this->loadEnv($path, $k, $defaultEnv, $testEnvs);
$this->loadEnv($path, $k, $defaultEnv, $testEnvs, $overrideExistingVars);
}

$_SERVER += $_ENV;
Expand Down Expand Up @@ -227,8 +227,6 @@ public function populate(array $values, bool $overrideExistingVars = false): voi
* @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)
*
* @return array
*
* @throws FormatException when a file has a syntax error
*/
public function parse(string $data, string $path = '.env'): array
Expand Down
85 changes: 78 additions & 7 deletions Tests/DotenvTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,61 +236,117 @@ public function testLoadEnv()
putenv('SYMFONY_DOTENV_VARS');
putenv('FOO');
putenv('TEST_APP_ENV');

$_ENV['EXISTING_KEY'] = $_SERVER['EXISTING_KEY'] = 'EXISTING_VALUE';
putenv('EXISTING_KEY=EXISTING_VALUE');
};

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

$path = tempnam($tmpdir, 'sf-');

// .env
file_put_contents($path, 'FOO=BAR');
file_put_contents($path, "FOO=BAR\nEXISTING_KEY=NEW_VALUE");

$resetContext();
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('BAR', getenv('FOO'));
$this->assertSame('dev', getenv('TEST_APP_ENV'));
$this->assertSame('EXISTING_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('EXISTING_VALUE', $_ENV['EXISTING_KEY']);

$resetContext();
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV', 'dev', ['test'], true);
$this->assertSame('BAR', getenv('FOO'));
$this->assertSame('dev', getenv('TEST_APP_ENV'));
$this->assertSame('NEW_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('NEW_VALUE', $_ENV['EXISTING_KEY']);

// .env.local
file_put_contents("$path.local", 'FOO=localBAR');
file_put_contents("$path.local", "FOO=localBAR\nEXISTING_KEY=localNEW_VALUE");

$resetContext();
$_SERVER['TEST_APP_ENV'] = 'local';
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('localBAR', getenv('FOO'));
$this->assertSame('EXISTING_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('EXISTING_VALUE', $_ENV['EXISTING_KEY']);

$resetContext();
$_SERVER['TEST_APP_ENV'] = 'local';
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV', 'dev', ['test'], true);
$this->assertSame('localBAR', getenv('FOO'));
$this->assertSame('localNEW_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('localNEW_VALUE', $_ENV['EXISTING_KEY']);

// special case for test
$resetContext();
$_SERVER['TEST_APP_ENV'] = 'test';
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('BAR', getenv('FOO'));
$this->assertSame('EXISTING_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('EXISTING_VALUE', $_ENV['EXISTING_KEY']);

$resetContext();
$_SERVER['TEST_APP_ENV'] = 'test';
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV', 'dev', ['test'], true);
$this->assertSame('BAR', getenv('FOO'));
$this->assertSame('NEW_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('NEW_VALUE', $_ENV['EXISTING_KEY']);

// .env.dev
file_put_contents("$path.dev", 'FOO=devBAR');
file_put_contents("$path.dev", "FOO=devBAR\nEXISTING_KEY=devNEW_VALUE");

$resetContext();
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('devBAR', getenv('FOO'));
$this->assertSame('EXISTING_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('EXISTING_VALUE', $_ENV['EXISTING_KEY']);

$resetContext();
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV', 'dev', ['test'], true);
$this->assertSame('devBAR', getenv('FOO'));
$this->assertSame('devNEW_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('devNEW_VALUE', $_ENV['EXISTING_KEY']);

// .env.dev.local
file_put_contents("$path.dev.local", 'FOO=devlocalBAR');
file_put_contents("$path.dev.local", "FOO=devlocalBAR\nEXISTING_KEY=devlocalNEW_VALUE");

$resetContext();
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('devlocalBAR', getenv('FOO'));
$this->assertSame('EXISTING_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('EXISTING_VALUE', $_ENV['EXISTING_KEY']);

$resetContext();
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV', 'dev', ['test'], true);
$this->assertSame('devlocalBAR', getenv('FOO'));
$this->assertSame('devlocalNEW_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('devlocalNEW_VALUE', $_ENV['EXISTING_KEY']);
unlink("$path.local");
unlink("$path.dev");
unlink("$path.dev.local");

// .env.dist
file_put_contents("$path.dist", 'FOO=distBAR');
file_put_contents("$path.dist", "FOO=distBAR\nEXISTING_KEY=distNEW_VALUE");

$resetContext();
unlink($path);
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('distBAR', getenv('FOO'));
$this->assertSame('EXISTING_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('EXISTING_VALUE', $_ENV['EXISTING_KEY']);

$resetContext();
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV', 'dev', ['test'], true);
$this->assertSame('distBAR', getenv('FOO'));
$this->assertSame('distNEW_VALUE', getenv('EXISTING_KEY'));
$this->assertSame('distNEW_VALUE', $_ENV['EXISTING_KEY']);
unlink("$path.dist");

$resetContext();
unset($_ENV['EXISTING_KEY'], $_SERVER['EXISTING_KEY']);
putenv('EXISTING_KEY');
rmdir($tmpdir);
}

Expand Down Expand Up @@ -490,22 +546,37 @@ public function testBootEnv()
unset($_SERVER['TEST_APP_ENV'], $_ENV['TEST_APP_ENV']);
unset($_SERVER['TEST_APP_DEBUG'], $_ENV['TEST_APP_DEBUG']);
unset($_SERVER['FOO'], $_ENV['FOO']);

$_ENV['EXISTING_KEY'] = $_SERVER['EXISTING_KEY'] = 'EXISTING_VALUE';
};

@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');
$path = tempnam($tmpdir, 'sf-');

file_put_contents($path, 'FOO=BAR');
file_put_contents($path, "FOO=BAR\nEXISTING_KEY=NEW_VALUE");
$resetContext();
(new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path);
$this->assertSame('BAR', $_SERVER['FOO']);
$this->assertSame('EXISTING_VALUE', $_SERVER['EXISTING_KEY']);

$resetContext();
(new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path, 'dev', ['test'], true);
$this->assertSame('BAR', $_SERVER['FOO']);
$this->assertSame('NEW_VALUE', $_SERVER['EXISTING_KEY']);
unlink($path);

file_put_contents($path.'.local.php', '<?php return ["TEST_APP_ENV" => "dev", "FOO" => "BAR"];');
file_put_contents($path.'.local.php', '<?php return ["TEST_APP_ENV" => "dev", "FOO" => "BAR", "EXISTING_KEY" => "localphpNEW_VALUE"];');
$resetContext();
(new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path);
$this->assertSame('BAR', $_SERVER['FOO']);
$this->assertSame('1', $_SERVER['TEST_APP_DEBUG']);
$this->assertSame('EXISTING_VALUE', $_SERVER['EXISTING_KEY']);

$resetContext();
(new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path, 'dev', ['test'], true);
$this->assertSame('BAR', $_SERVER['FOO']);
$this->assertSame('1', $_SERVER['TEST_APP_DEBUG']);
$this->assertSame('localphpNEW_VALUE', $_SERVER['EXISTING_KEY']);
unlink($path.'.local.php');

$resetContext();
Expand Down

0 comments on commit 6fcefaa

Please sign in to comment.