Skip to content

Commit

Permalink
[Dotenv] Deprecate useage of \"putenv\"
Browse files Browse the repository at this point in the history
  • Loading branch information
Nyholm authored and fabpot committed Apr 10, 2019
1 parent 1335add commit d2fa94d
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 21 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -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 Dotenv.php
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 README.md
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 Tests/DotenvTest.php
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);
}
}

0 comments on commit d2fa94d

Please sign in to comment.