Skip to content

[Dotenv][WebServerBundle] Override previously loaded variables #23799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed

[Dotenv][WebServerBundle] Override previously loaded variables #23799

wants to merge 4 commits into from

Conversation

voronkovich
Copy link
Contributor

@voronkovich voronkovich commented Aug 6, 2017

Q A
Branch? 3.3
Bug fix? yes
New feature? no
BC breaks? yes
Deprecations? no
Tests pass? yes
Fixed tickets #23723
License MIT

This PR implements @nicolas-grekas's idea about how we could refresh loaded environment variables. See his comment #23723 (comment)

@voronkovich voronkovich changed the title [Dotenv] Override previosly loaded variables [Dotenv] Override previously loaded variables Aug 6, 2017
Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost good, just this missing:

--- a/src/Symfony/Bundle/WebServerBundle/WebServer.php
+++ b/src/Symfony/Bundle/WebServerBundle/WebServer.php
@@ -154,6 +154,11 @@ class WebServer
         $process->setWorkingDirectory($config->getDocumentRoot());
         $process->setTimeout(null);
 
+        if (in_array('APP_ENV', explode(',', getenv('SYMFONY_DOTENV_VARS')))) {
+            $process->setEnv(array('APP_ENV' => false));
+            $process->inheritEnvironmentVariables();
+        }
+
         return $process;
     }

* @param array $values An array of env variables
*/
public function populate($values)
{
$loadedVars = getenv('SYMFONY_DOTENV_VARS') ? explode(',', getenv('SYMFONY_DOTENV_VARS')) : array();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

array_flip(explode(',', getenv('SYMFONY_DOTENV_VARS'))) (no need for the check, the resulting array is good enough)

foreach ($values as $name => $value) {
if (isset($_ENV[$name]) || isset($_SERVER[$name]) || false !== getenv($name)) {
if (!in_array($name, $loadedVars) && (isset($_ENV[$name]) || isset($_SERVER[$name]) || false !== getenv($name))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isset($loadedVars[$name]) || ...

continue;
}

putenv("$name=$value");
$_ENV[$name] = $value;
$_SERVER[$name] = $value;

$loadedVars[] = $name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$loadedVars[$name] = true;

}

$loadedVars = implode(',', $loadedVars);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (1 < count($loadedVars)) { ... } ("1" because of the empty string - or we should remove it before)

@nicolas-grekas nicolas-grekas added this to the 3.4 milestone Aug 6, 2017
@voronkovich voronkovich changed the title [Dotenv] Override previously loaded variables [Dotenv][WebServerBundle] Override previously loaded variables Aug 6, 2017
@voronkovich
Copy link
Contributor Author

@nicolas-grekas, Thanks for the code review! I've fixed all issues you pointed out.
This PR should be merged in master branch, so I think we shouldn't use the Process::inheritEnvironmentVariables (see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Process/Process.php#L1148)

@nicolas-grekas
Copy link
Member

It should be merged in 3.4 to me, even maybe in 3.3 as bug fix?

@voronkovich
Copy link
Contributor Author

@nicolas-grekas, It introduces a BC break:

// This code will work differently for a current version and the new one:
(new Dotenv())->populate([ 'FOO' => 'foo', 'FOO' => 'bar' ]);
// Current version echoes: "foo", new version echoes "bar"
echo getenv('FOO');

IMHO, we don't need this feature in the Symfony 3.x because it doesn't use the Dotenv component.

@nicolas-grekas
Copy link
Member

Not sure to get your example, you can't define an array with two identical keys.
Moreover, Dotenv is for dev only, so we don't care about edge case behavior changes to me.

@voronkovich
Copy link
Contributor Author

@nicolas-grekas, Sorry, example should be:

(new Dotenv())->populate([ 'FOO' => 'foo' ]);
(new Dotenv())->populate([ 'FOO' => 'bar' ]);
echo getenv('FOO');

@nicolas-grekas
Copy link
Member

Ok :)
I still think we should propose this for 3.3 and see what others say about it.

@voronkovich voronkovich changed the base branch from master to 3.3 August 6, 2017 17:53
@voronkovich
Copy link
Contributor Author

@nicolas-grekas, Ok, I've changed the base branch to 3.3.

}

$loadedVars = implode(',', array_keys($loadedVars));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this block of 4 lines should be inside an if ($loadedVars) {...}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas, I think you're right. I've moved that block inside an if statement.

Copy link
Member

@chalasr chalasr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3.3 LGTM

@nicolas-grekas nicolas-grekas requested a review from fabpot August 8, 2017 06:51
@@ -60,20 +60,32 @@ public function load($path/*, ...$paths*/)
/**
* Sets values as environment variables (via putenv, $_ENV, and $_SERVER).
*
* Note that existing environment variables are never overridden.
* Existing environment variables are never overridden, unless they are listed in the SYMFONY_DOTENV_VARS env var.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not document this feature as this is really just a hack to make the internal PHP web server work. Not a feature we want to promote.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fabpot, Ok, I've removed that comment.

@fabpot
Copy link
Member

fabpot commented Aug 22, 2017

Thank you @voronkovich.

fabpot added a commit that referenced this pull request Aug 22, 2017
…bles (voronkovich)

This PR was squashed before being merged into the 3.3 branch (closes #23799).

Discussion
----------

[Dotenv][WebServerBundle] Override previously loaded variables

| Q             | A
| ------------- | ---
| Branch?       | 3.3
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | yes
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #23723
| License       | MIT

This PR implements @nicolas-grekas's idea about how we could refresh loaded environment variables. See his comment #23723 (comment)

Commits
-------

c5a1218 [Dotenv][WebServerBundle] Override previously loaded variables
@fabpot fabpot closed this Aug 22, 2017
@fabpot fabpot mentioned this pull request Aug 28, 2017
@tiger-seo
Copy link

guys, i am grateful for all your hard work, but this change is a breaking change and it is not mentioned :(

@symfony symfony deleted a comment from voronkovich Sep 1, 2017
@tiger-seo
Copy link

to give you more info why it is a BC, please see our env_autoload.php, which is used instead of autoload.php:

<?php

/** @var \Composer\Autoload\ClassLoader $loader */

use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\Dotenv\Exception\PathException;

$loader = require __DIR__ . '/autoload.php';

$dotenv = new Dotenv;

try {
    // existing environment variables are never overridden
    $dotenv->load(__DIR__ . '/config/.env');
} catch (PathException $e) {
}

// existing environment variables are never overridden
$dotenv->load(__DIR__ . '/config/.env.dist');

return $loader;

@chalasr
Copy link
Member

chalasr commented Sep 1, 2017

@tiger-seo Please open a dedicated issue so that we can look at possible alternatives. Comments on closed PR/issues are lost.

@voronkovich
Copy link
Contributor Author

voronkovich commented Sep 1, 2017

@tiger-seo, I think you could try to load the .env.dist earlier than the .env:

// existing environment variables are never overridden
$dotenv->load(__DIR__ . '/config/.env.dist');

try {
    // existing environment variables are never overridden
    $dotenv->load(__DIR__ . '/config/.env');
} catch (PathException $e) {
}

return $loader;

@tiger-seo
Copy link

@voronkovich yes, this is exactly how we've fixed it, but we spent at least 5 man-hours of our team to find(debug) and fix the issue. Symfony promises seamless upgrade for minor releases, but not this time.

@voronkovich
Copy link
Contributor Author

Sorry, @tiger-seo! We thought that the BC break is so small, so nobody would notice it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants