Search code examples
laravellaravel-5phpdotenv

Laravel 5.1 not using correct .env values


I'm using Laravel 5.1.44, and even though I have both a .env.testing file setup with different values, as well as App::environment() returning 'testing', Laravel still seems to only be using values from the main .env file.

Is there something else I should be double-checking to make sure I didn't screw up or forget to set something?


Solution

  • The ability to use environment specific .env files (e.g. .env.testing) was not available until Laravel 5.2.13. Even then, it only works if you are not using config caching.

    The easiest option is to just add all your testing specific environment variables to your phpunit.xml file. As you've mentioned, you've already got a few in there, it should be no issue to add any additional ones you need.

    If you really want the environment specific .env file functionality in Laravel 5.1, you can try the following. Note, this is untested, and just an assumption of what should work based off of looking at the source code.

    The .env file is loaded in a bootstrap class called DetectEnvironment. The idea is you create your own bootstrap class that modifies the filename of the .env file that DetectEnvironment will load, and then you update your Kernel files so that your new bootstrap class is called, and called before the DetectEnvironment bootstrapper.

    So, first, create a new bootstrapper class (e.g. app\Bootstrap\CustomEnvironment.php):

    <?php
    namespace App\Bootstrap;
    
    use Illuminate\Contracts\Foundation\Application;
    
    class CustomEnvironment
    {
        /**
         * Bootstrap the given application.
         *
         * @param  \Illuminate\Contracts\Foundation\Application  $app
         * @return void
         */
        public function bootstrap(Application $app)
        {
            // APP_ENV must already be set.
            // We must know which environment file to look for.
            if (! env('APP_ENV')) {
                return;
            }
    
            // Build the file name to look for (e.g. .env.testing)
            $file = $app->environmentFile().'.'.env('APP_ENV');
    
            // If the file exists, set the App to load from that file.
            // The actual loading will take place in DetectEnvironment
            if (file_exists($app->environmentPath().'/'.$file)) {
                $app->loadEnvironmentFrom($file);
            }
        }
    }
    

    Now that you have a bootstrap class to update the .env file to be loaded, you need to add it to your Kernel|s to make sure it is called before the DetectEnvironment bootstrap class.

    Add the following property to your app/Console/Kernel.php file:

    /**
     * The bootstrap classes for the application.
     *
     * @var array
     */
    protected $bootstrappers = [
        'App\Bootstrap\CustomEnvironment',
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        'Illuminate\Foundation\Bootstrap\LoadConfiguration',
        'Illuminate\Foundation\Bootstrap\ConfigureLogging',
        'Illuminate\Foundation\Bootstrap\HandleExceptions',
        'Illuminate\Foundation\Bootstrap\RegisterFacades',
        'Illuminate\Foundation\Bootstrap\SetRequestForConsole',
        'Illuminate\Foundation\Bootstrap\RegisterProviders',
        'Illuminate\Foundation\Bootstrap\BootProviders',
    ];
    

    Add the following property to your app/Http/Kernel.php file:

    /**
     * The bootstrap classes for the application.
     *
     * @var array
     */
    protected $bootstrappers = [
        'App\Bootstrap\CustomEnvironment',
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        'Illuminate\Foundation\Bootstrap\LoadConfiguration',
        'Illuminate\Foundation\Bootstrap\ConfigureLogging',
        'Illuminate\Foundation\Bootstrap\HandleExceptions',
        'Illuminate\Foundation\Bootstrap\RegisterFacades',
        'Illuminate\Foundation\Bootstrap\RegisterProviders',
        'Illuminate\Foundation\Bootstrap\BootProviders',
    ];
    

    Note, they may look similar at first glance, but the bootstrappers are different between the Console and Http kernels (Console has one more bootstrapper). Don't just copy one of the above and add it to both files.

    Edit

    Another simpler way would be to setup a before listener for the DetectEnvironment bootstrapper, and set the file to load in an event closure.

    So, for example, in your bootstrap\app.php file, add the following code before the return $app; statement:

    $app->beforeBootstrapping(
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        function($app) {
            // APP_ENV must already be set.
            // We must know which environment file to look for.
            if (! env('APP_ENV')) {
                return;
            }
    
            // Build the file name to look for (e.g. .env.testing)
            $file = $app->environmentFile().'.'.env('APP_ENV');
    
            // If the file exists, set the App to load from that file.
            // The actual loading will take place in DetectEnvironment
            if (file_exists($app->environmentPath().'/'.$file)) {
                $app->loadEnvironmentFrom($file);
            }
        }
    );
    

    Using the beforeBootstrapping() method, the Closure in the second parameter will be executed immediately before the bootstrapper in the first parameter is called. Now you don't have to worry about Kernels or extra classes or anything.