'How to change the app environment at run time?

How can I change the app environment at run time?

I have some classes that only bind in the service provider in production. I'd like to assert with a unit test that they are properly bound. For other environment variables, I can set them with the config helper and then simply call resetApplication in tearDown but for some reason the variable set by APP_ENV doesn't change.

dump(app()->environment()); // "testing"

config(['app.env' => 'production']);

dump(app()->environment()); // "testing"

What can I do to get app()->environment() to return production at run time?



Solution 1:[1]

app()->environment() reads directly from the variables specified in your .env file rather than the configuration files.

You could take two approaches to solve your problem.

1. Read the environment variables from the config file rather than the .env file.

dump(config('app.env')); // "testing"

config(['app.env' => 'production']);

dump(config('app.env')); // "production"

2. Change the value of 'env' in the current app instance by changing the value of app()['env'].

dump(app()->environment()); // "testing"

app()['env'] = 'production';

dump(app()->environment()); // "production"

Solution 2:[2]

I've noticed that above answer can be a bit dangerous: You might be overwriting the 'env' key, without truly switching environments. Suddenly your app says that you are in testing, while your DB connection is still set to production.

Normally you just want to really stick to the Laravel best practice of defining ONE environment for each literal environment, but for my use case I needed to temporarily & programmatically switch between multiple environments within a single artisan script.

My solution (works in Laravel 5.8+ with DotEnv3) would be to really reboot the application:

<?php

$basepath = app()->basePath();
$env = app()->basePath('.env.alternative');
$boot = app()->basePath('bootstrap/app.php');

// Overwrite webserver env
(new Dotenv($basepath,'.env.alternative'))->overload();

// Reboot the application
(require $boot)
    ->loadEnvironmentFrom($env)
    ->make(Kernel::class)
    ->bootstrap();

// This returns 'mysql_alternative', as defined in .env.alternative
dd(DB::connection()->getName())

Disclaimer: I've only tested this to the extend of my own code.

Solution 3:[3]

From a testing point of view, then in the setUp() method:

$this->app['env'] = 'production';

But remember, only do this in a test class, specifically testing production runs. Otherwise, do it at the top of the test method, remembering to set it back to testing at the end.

If you adjust the setUp() method, remember your tearDown() method and change it to testing. By altering the $this->app['env'], you are directly changing the Applications environment, which is why it's so important to remember to set it back as it will produce side effects, also only do this in testing .

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jeff Puckett
Solution 2
Solution 3