简体   繁体   中英

How to change php dotenv (.env) variables dynamically in laravel or php?

I want something like this:

env('APP_ENV');
setenv('APP_ENV', 'testing');
env('APP_ENV');

Output :

staging
testing

I find one answer How to change variables in the .env file dynamically in Laravel? but here .env is saved permanently, I don't want to save permanently. How phpunit is doing this ? Because I can put in phpunit.xml this :

...
<php>
    <env name="APP_ENV" value="testing"/>
</php>
....

And env('APP_ENV') gives me 'testing'...

putenv() work like a charm :

echo env('APP_ENV');
putenv('APP_ENV=testing');
echo env('APP_ENV');

Output:

staging
testing

.env file is unattached ...

Laravel .env file values are read by Laravel at the application bootstrap time and stored in the PHP $_ENV global array under the respective keys.

You can change any of those Laravel's (or other $_ENV's) values as simple as:

// Assign the key's new value in the $_ENV global array
$_ENV['DB_CONNECTION'] = 'sqlite';

However if Laravel would take your change into account or not depends on the place in the code where you put this assignment.

Development, Staging, Production Environments:

Changing env values dynamically in memory for these environments is a bad idea.

Nevertheless to make this change you have to put your assignements in the Laravel bootstrap file before the framework instantiates LoadEnvironmentVariables class (for Lumen, the instantiation happens in /bootstrap/app.php file, for Laravel you could put your changes in the same file before application instantiation).

Testing Environment

However for testing environment changing env variables can be sometimes useful.

On top of the PHPUnit capability to change env variables at the test loading (as an example see the Laravel's phpunit.xml in your project folder):

<php>
    <env name="DB_CONNECTION" value="sqlite"/>
</php>

you can change the variables in the PHPUnit's setUp function , (that in Laravel also bootstraps the application - note the tests extend the Laravel TestCase ) like the following:

public $savedDBConnection;
public function setUp():void
{
    $this->savedDBConnection = $_ENV['DB_CONNECTION'];
    $_ENV['DB_CONNECTION'] = 'mysql';
    parent::setUp();
}

Caveat

Rather often you may face the unpleasant side effects of such dynamic changes (eg you test with in memory database and temporarily switch to on-disk test DB and some other test's database refresh erases your on-disk test DB data).

To avoid this you have to save the variables before you change them in setUp method (as shown above) and restore them in tearDown method , like this:

public function tearDown():void
{
    $_ENV['DB_CONNECTION'] = $this->savedDBConnection;
    parent::tearDown();
}

Alternative

There is the other sometimes more applicable approach to change your .env variables for testing environment (credits go here .): create the .env.testing file and load it when in tests. Briefly, add the following colde as explained in the comments

// Add in tests/TestCase.php::createApplication() method
// below $app = require __DIR__.'/../bootstrap/app.php' call.
if (file_exists(dirname(__DIR__) . '/.env.testing')) {
    (new \Dotenv\Dotenv(dirname(__DIR__), '.env.testing'))->load();
}

Now it is enough information to decide whether, when or how one needs / can change the env variables dynamically.

Dotenv is immutable, so you cannot change value that is already assigned. ( https://github.com/vlucas/phpdotenv#immutability )

PHPUnit is setting this values during bootstrap, before laravel is started. Laravel dotenv is running in immutable mode, so values already set, are not overridden.

Notice:

Laravel ~5.7:

env('AAA') = getenv('AAA') → To set the value of the variable AAA, simply use putenv('AAA', 'true');

→ env('AAA') will become TRUE

You can check the details of getenv() here https://github.com/laravel/framework/blob/5.7/src/Illuminate/Support/helpers.php

Laravel 5.8~:

From Laravel 5.8 onward, env() no longer uses getenv() anymore.

Therefore, env('AAA') != getenv('AAA')

→ So even though we use putenv('AAA', 'true'); the value of env('AAA') will not change.

Because the behavior of the env() becomes

env('AAA') = Env::getRepository()->get('AAA');

→ To set the value of the variable AAA, use Env::getRepository()->set('AAA','true') → env('AAA') will become TRUE

You can check the details of getenv() of Laravel 5.8 here https://github.com/laravel/framework/blob/5.8/src/Illuminate/Support/helpers.php

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM