phplaravelenvironment-variablesdotenvphpdotenv

Laravel Environment Variables(without default value passed in method) not working inside Artisan Command file


I am using Laravel 6.x for client's project wherein I have built an API in the Artisan Command for syncing data.

Now client wants the configuration to be discreet, out of main source code and without any fallback values as possible. That means I must define the configs in the .env file and use the env() method without any fallback default values.

This must be possible inside Laravel Artisan command class files, but it is not working as intended when I use the env method in the code as below:

[siteroot]\.env:

APP_ENV=local

[siteroot]\app\Console\Commands\SyncSomeData.php:

use Illuminate\Console\Command;

class SyncSomeData extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'sync:some-data';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        exit(env('APP_ENV','staging1'));
    }
}

This always prints staging1 in console and if I use this instead of the given env method, then it prints nothing:

exit(env('APP_ENV'));

As I know and can trust that Laravel is most secure, there must be way to have env method work without fallback value in the command file, can anyone help fulfilling this ?


Solution

  • There are a couple parts to this answer.

    1. Caching Config

    The issue in this question is only present if you have cached your config on your local environment. If you have ever run php artisan config:cache or php artisan optimize, you have cached your config.

    As a result, laravel will no longer read your .env file and load all values for config and .env from cache.

    It is best practice, to not use env() throughout your application but create config files for these values. This will speed up your application in production. If you want to take advantage of config cache, you CAN NOT use env() anywhere but in your config files.

    In your local environment, the documentation recommends you should not be caching your configuration, since it will be updated frequently. I think this is good advice with one caveat: env(). If you aren't caching your config on local, but are in production, you won't come across errors like the one that was the impetus for this post in local. If you have a ci pipeline and good testing practices than this hurdle is overcome.

    If you stick with cache config in local, every time you update your config files or your .env file you will need to run php artisan config:cache.

    1. Disabling config cache

    By running php artisan config:clear or removing /bootstrap/cache/config.php laravel will no longer to attempt to read config from cache, and .env will work outside of config files. If you do this, as I stated in (1) make sure there is a part of your pipeline that will catch these errors, if you are caching config in production.

    After more discussion with OP, I will make one more thing clear:

    Cache config does not automatically just cache your .env keys so you can access them like config('SOME_CUSTOM_DOT_ENV_KEY'). When using cache config, if you need to access environment variables inside of your application, you must reference them in a config file.

    Again, once config cache is enabled,.env becomes useless, but it is used to build the cache.

    So if you in .env say:

    GOOGLE_API_TOKEN=xxxx
    

    You would maybe create a config file like:

    google.php

    return [
        'api_token' => env('GOOGLE_API_TOKEN', 'some-default-value'),
    ];
    

    And in your application, you will only reference config('google.api_token') and never env('GOOGLE_API_TOKEN').