Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
-------

228b220 [Dotenv] add Dotenv::overload() to allow env vars override
  • Loading branch information
fabpot committed Sep 4, 2018
2 parents 4401f2f + 228b220 commit 8f5229f
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 13 deletions.
5 changes: 5 additions & 0 deletions src/Symfony/Component/Dotenv/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========

4.2.0
-----

* added `Dotenv::overload()` and `$overrideExistingVars` as optional parameter of `Dotenv::populate()`

3.3.0
-----

Expand Down
44 changes: 31 additions & 13 deletions src/Symfony/Component/Dotenv/Dotenv.php
Expand Up @@ -47,33 +47,38 @@ final class Dotenv
*/
public function load(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->doLoad(false, $path, ...$paths);
}

$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['']);

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;
}

Expand Down Expand Up @@ -399,4 +404,17 @@ private function createFormatException($message)
{
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);
}
}
}
47 changes: 47 additions & 0 deletions src/Symfony/Component/Dotenv/Tests/DotenvTest.php
Expand Up @@ -186,6 +186,41 @@ public function testLoad()
$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
*/
Expand Down Expand Up @@ -228,6 +263,18 @@ public function testHttpVarIsPartiallyOverriden()
$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
Expand Down

0 comments on commit 8f5229f

Please sign in to comment.