Skip to content

[Dotenv] Deprecate useage of "putenv" #31062

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

Merged
merged 1 commit into from
Apr 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions UPGRADE-4.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ Doctrine Bridge
* Passing an `IdReader` to the `DoctrineChoiceLoader` when the query cannot be optimized with single id field has been deprecated, pass `null` instead
* Not passing an `IdReader` to the `DoctrineChoiceLoader` when the query can be optimized with single id field has been deprecated

Dotenv
------

* First parameter of `Dontenv::__construct()` will change from `true` to `false` in Symfony 5.0. A deprecation warning
will be triggered if no parameter is used. Use `$usePutenv=true` to upgrade without breaking changes.

EventDispatcher
---------------

Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/Dotenv/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

4.3.0
-----

* deprecated use of `putenv()` but default. This feature will be opted-in with a constructor argument to `Dotenv`.

4.2.0
-----

Expand Down
26 changes: 24 additions & 2 deletions src/Symfony/Component/Dotenv/Dotenv.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ final class Dotenv
private $data;
private $end;
private $values;
private $usePutenv = true;

/**
* @var bool If we should use `putenv()` to define environment variables
* or not. Since Symfony 5.0 the default value is false
* because `putenv()` is not thread safe.
*/
public function __construct(bool $usePutenv = true)
{
if (0 === \func_num_args()) {
@trigger_error(sprintf('The default value of "$usePutenv" argument of "%s\'s constructor will change from "true" to "false" in Symfony 5.0, you should define its value explicitly.', __METHOD__), E_USER_DEPRECATED);
}

$this->usePutenv = $usePutenv;
}

/**
* Loads one or several .env files.
Expand Down Expand Up @@ -126,7 +141,10 @@ public function populate(array $values, bool $overrideExistingVars = false): voi
continue;
}

putenv("$name=$value");
if ($this->usePutenv) {
putenv("$name=$value");
}

$_ENV[$name] = $value;
if ($notHttpName) {
$_SERVER[$name] = $value;
Expand All @@ -140,7 +158,11 @@ public function populate(array $values, bool $overrideExistingVars = false): voi
if ($updateLoadedVars) {
unset($loadedVars['']);
$loadedVars = implode(',', array_keys($loadedVars));
putenv('SYMFONY_DOTENV_VARS='.$_ENV['SYMFONY_DOTENV_VARS'] = $_SERVER['SYMFONY_DOTENV_VARS'] = $loadedVars);
$_ENV['SYMFONY_DOTENV_VARS'] = $_SERVER['SYMFONY_DOTENV_VARS'] = $loadedVars;

if ($this->usePutenv) {
putenv('SYMFONY_DOTENV_VARS='.$loadedVars);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Dotenv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Dotenv Component
================

Symfony Dotenv parses `.env` files to make environment variables stored in them
accessible via `getenv()`, `$_ENV`, or `$_SERVER`.
accessible via `$_ENV`, `$_SERVER` and optionally `getenv()`.

Resources
---------
Expand Down
53 changes: 35 additions & 18 deletions src/Symfony/Component/Dotenv/Tests/DotenvTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DotenvTest extends TestCase
*/
public function testParseWithFormatError($data, $error)
{
$dotenv = new Dotenv();
$dotenv = new Dotenv(true);

try {
$dotenv->parse($data);
Expand Down Expand Up @@ -62,7 +62,7 @@ public function getEnvDataWithFormatErrors()
*/
public function testParse($data, $expected)
{
$dotenv = new Dotenv();
$dotenv = new Dotenv(true);
$this->assertSame($expected, $dotenv->parse($data));
}

Expand Down Expand Up @@ -193,7 +193,7 @@ public function testLoad()
file_put_contents($path1, 'FOO=BAR');
file_put_contents($path2, 'BAR=BAZ');

(new Dotenv())->load($path1, $path2);
(new Dotenv(true))->load($path1, $path2);

$foo = getenv('FOO');
$bar = getenv('BAR');
Expand Down Expand Up @@ -224,41 +224,41 @@ public function testLoadEnv()
// .env

file_put_contents($path, 'FOO=BAR');
(new DotEnv())->loadEnv($path, 'TEST_APP_ENV');
(new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('BAR', getenv('FOO'));
$this->assertSame('dev', getenv('TEST_APP_ENV'));

// .env.local

$_SERVER['TEST_APP_ENV'] = 'local';
file_put_contents("$path.local", 'FOO=localBAR');
(new DotEnv())->loadEnv($path, 'TEST_APP_ENV');
(new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('localBAR', getenv('FOO'));

// special case for test

$_SERVER['TEST_APP_ENV'] = 'test';
(new DotEnv())->loadEnv($path, 'TEST_APP_ENV');
(new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('BAR', getenv('FOO'));

// .env.dev

unset($_SERVER['TEST_APP_ENV']);
file_put_contents("$path.dev", 'FOO=devBAR');
(new DotEnv())->loadEnv($path, 'TEST_APP_ENV');
(new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('devBAR', getenv('FOO'));

// .env.dev.local

file_put_contents("$path.dev.local", 'FOO=devlocalBAR');
(new DotEnv())->loadEnv($path, 'TEST_APP_ENV');
(new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('devlocalBAR', getenv('FOO'));

// .env.dist

unlink($path);
file_put_contents("$path.dist", 'BAR=distBAR');
(new DotEnv())->loadEnv($path, 'TEST_APP_ENV');
(new Dotenv(true))->loadEnv($path, 'TEST_APP_ENV');
$this->assertSame('distBAR', getenv('BAR'));

putenv('FOO');
Expand Down Expand Up @@ -290,7 +290,7 @@ public function testOverload()
file_put_contents($path1, 'FOO=BAR');
file_put_contents($path2, 'BAR=BAZ');

(new Dotenv())->overload($path1, $path2);
(new Dotenv(true))->overload($path1, $path2);

$foo = getenv('FOO');
$bar = getenv('BAR');
Expand All @@ -310,15 +310,15 @@ public function testOverload()
*/
public function testLoadDirectory()
{
$dotenv = new Dotenv();
$dotenv = new Dotenv(true);
$dotenv->load(__DIR__);
}

public function testServerSuperglobalIsNotOverriden()
{
$originalValue = $_SERVER['argc'];

$dotenv = new Dotenv();
$dotenv = new Dotenv(true);
$dotenv->populate(['argc' => 'new_value']);

$this->assertSame($originalValue, $_SERVER['argc']);
Expand All @@ -329,7 +329,7 @@ public function testEnvVarIsNotOverriden()
putenv('TEST_ENV_VAR=original_value');
$_SERVER['TEST_ENV_VAR'] = 'original_value';

$dotenv = new Dotenv();
$dotenv = new Dotenv(true);
$dotenv->populate(['TEST_ENV_VAR' => 'new_value']);

$this->assertSame('original_value', getenv('TEST_ENV_VAR'));
Expand All @@ -339,7 +339,7 @@ public function testHttpVarIsPartiallyOverriden()
{
$_SERVER['HTTP_TEST_ENV_VAR'] = 'http_value';

$dotenv = new Dotenv();
$dotenv = new Dotenv(true);
$dotenv->populate(['HTTP_TEST_ENV_VAR' => 'env_value']);

$this->assertSame('env_value', getenv('HTTP_TEST_ENV_VAR'));
Expand All @@ -351,7 +351,7 @@ public function testEnvVarIsOverriden()
{
putenv('TEST_ENV_VAR_OVERRIDEN=original_value');

$dotenv = new Dotenv();
$dotenv = new Dotenv(true);
$dotenv->populate(['TEST_ENV_VAR_OVERRIDEN' => 'new_value'], true);

$this->assertSame('new_value', getenv('TEST_ENV_VAR_OVERRIDEN'));
Expand All @@ -373,7 +373,7 @@ public function testMemorizingLoadedVarsNamesInSpecialVar()
unset($_SERVER['DATABASE_URL']);
putenv('DATABASE_URL');

$dotenv = new Dotenv();
$dotenv = new Dotenv(true);
$dotenv->populate(['APP_DEBUG' => '1', 'DATABASE_URL' => 'mysql://root@localhost/db']);

$this->assertSame('APP_DEBUG,DATABASE_URL', getenv('SYMFONY_DOTENV_VARS'));
Expand All @@ -390,7 +390,7 @@ public function testMemorizingLoadedVarsNamesInSpecialVar()
unset($_SERVER['DATABASE_URL']);
putenv('DATABASE_URL');

$dotenv = new Dotenv();
$dotenv = new Dotenv(true);
$dotenv->populate(['APP_DEBUG' => '0', 'DATABASE_URL' => 'mysql://root@localhost/db']);
$dotenv->populate(['DATABASE_URL' => 'sqlite:///somedb.sqlite']);

Expand All @@ -406,12 +406,29 @@ public function testOverridingEnvVarsWithNamesMemorizedInSpecialVar()
putenv('BAZ=baz');
putenv('DOCUMENT_ROOT=/var/www');

$dotenv = new Dotenv();
$dotenv = new Dotenv(true);
$dotenv->populate(['FOO' => 'foo1', 'BAR' => 'bar1', 'BAZ' => 'baz1', 'DOCUMENT_ROOT' => '/boot']);

$this->assertSame('foo1', getenv('FOO'));
$this->assertSame('bar1', getenv('BAR'));
$this->assertSame('baz1', getenv('BAZ'));
$this->assertSame('/var/www', getenv('DOCUMENT_ROOT'));
}

/**
* @group legacy
* @expectedDeprecation The default value of "$usePutenv" argument of "%s's constructor will change from "true" to "false" in Symfony 5.0, you should define its value explicitly.
*/
public function testDeprecationWarning()
{
new Dotenv();
}

public function testNoDeprecationWarning()
{
$dotenv = new Dotenv(true);
$this->assertInstanceOf(Dotenv::class, $dotenv);
$dotenv = new Dotenv(false);
$this->assertInstanceOf(Dotenv::class, $dotenv);
}
}