feature #26859 [Dotenv] add a flag to allow env vars override (fmata)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[Dotenv] add a flag to allow env vars override

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #26846
| License       | MIT
| Doc PR        | symfony/symfony-docs#9568

I choose to use a new parameter in the constructor instead of `populate()` to not add boilerplate code to them who want allow overriding in their current setup. It's just a parameter to add in `Dotenv` creation instead of change or customize the loading of different .env files.

I targeted 4.1 despite the feature freeze because it's a small change but if you don't agree I can change to 4.2.

~~If you accept this PR I will do the doc PR then.~~
doc ready

Commits
-------

228b220495 [Dotenv] add Dotenv::overload() to allow env vars override
This commit is contained in:
Fabien Potencier 2018-09-04 17:26:33 +02:00
commit 8f5229f768
3 changed files with 83 additions and 13 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
4.2.0
-----
* added `Dotenv::overload()` and `$overrideExistingVars` as optional parameter of `Dotenv::populate()`
3.3.0
-----

View File

@ -47,25 +47,30 @@ final class Dotenv
*/
public function load(string $path, string ...$paths): void
{
array_unshift($paths, $path);
$this->doLoad(false, $path, ...$paths);
}
foreach ($paths as $path) {
if (!is_readable($path) || is_dir($path)) {
throw new PathException($path);
}
$this->populate($this->parse(file_get_contents($path), $path));
}
/**
* Loads one or several .env files and enables override existing vars.
*
* @param string $path A file to load
* @param ...string $paths A list of additional files to load
*
* @throws FormatException when a file has a syntax error
* @throws PathException when a file does not exist or is not readable
*/
public function overload(string $path, string ...$paths): void
{
$this->doLoad(true, $path, ...$paths);
}
/**
* Sets values as environment variables (via putenv, $_ENV, and $_SERVER).
*
* Note that existing environment variables are not overridden.
*
* @param array $values An array of env variables
* @param array $values An array of env variables
* @param bool $overrideExistingVars true when existing environment variables must be overridden
*/
public function populate(array $values): void
public function populate(array $values, bool $overrideExistingVars = false): void
{
$loadedVars = array_flip(explode(',', getenv('SYMFONY_DOTENV_VARS')));
unset($loadedVars['']);
@ -73,7 +78,7 @@ final class Dotenv
foreach ($values as $name => $value) {
$notHttpName = 0 !== strpos($name, 'HTTP_');
// don't check existence with getenv() because of thread safety issues
if (!isset($loadedVars[$name]) && (isset($_ENV[$name]) || (isset($_SERVER[$name]) && $notHttpName))) {
if (!isset($loadedVars[$name]) && (!$overrideExistingVars && (isset($_ENV[$name]) || (isset($_SERVER[$name]) && $notHttpName)))) {
continue;
}
@ -399,4 +404,17 @@ final class Dotenv
{
return new FormatException($message, new FormatExceptionContext($this->data, $this->path, $this->lineno, $this->cursor));
}
private function doLoad(bool $overrideExistingVars, string $path, string ...$paths): void
{
array_unshift($paths, $path);
foreach ($paths as $path) {
if (!is_readable($path) || is_dir($path)) {
throw new PathException($path);
}
$this->populate($this->parse(file_get_contents($path), $path), $overrideExistingVars);
}
}
}

View File

@ -186,6 +186,41 @@ class DotenvTest extends TestCase
$this->assertSame('BAZ', $bar);
}
public function testOverload()
{
unset($_ENV['FOO']);
unset($_ENV['BAR']);
unset($_SERVER['FOO']);
unset($_SERVER['BAR']);
putenv('FOO=initial_foo_value');
putenv('BAR=initial_bar_value');
$_ENV['FOO'] = 'initial_foo_value';
$_ENV['BAR'] = 'initial_bar_value';
@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');
$path1 = tempnam($tmpdir, 'sf-');
$path2 = tempnam($tmpdir, 'sf-');
file_put_contents($path1, 'FOO=BAR');
file_put_contents($path2, 'BAR=BAZ');
(new DotEnv())->overload($path1, $path2);
$foo = getenv('FOO');
$bar = getenv('BAR');
putenv('FOO');
putenv('BAR');
unlink($path1);
unlink($path2);
rmdir($tmpdir);
$this->assertSame('BAR', $foo);
$this->assertSame('BAZ', $bar);
}
/**
* @expectedException \Symfony\Component\Dotenv\Exception\PathException
*/
@ -228,6 +263,18 @@ class DotenvTest extends TestCase
$this->assertSame('http_value', $_SERVER['HTTP_TEST_ENV_VAR']);
}
public function testEnvVarIsOverriden()
{
putenv('TEST_ENV_VAR_OVERRIDEN=original_value');
$dotenv = new DotEnv();
$dotenv->populate(array('TEST_ENV_VAR_OVERRIDEN' => 'new_value'), true);
$this->assertSame('new_value', getenv('TEST_ENV_VAR_OVERRIDEN'));
$this->assertSame('new_value', $_ENV['TEST_ENV_VAR_OVERRIDEN']);
$this->assertSame('new_value', $_SERVER['TEST_ENV_VAR_OVERRIDEN']);
}
public function testMemorizingLoadedVarsNamesInSpecialVar()
{
// Special variable not exists