laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.21k stars 10.9k forks source link

[5.0] Environment variables sometimes return null #8191

Closed tylercubell closed 9 years ago

tylercubell commented 9 years ago

If you make requests fast enough some or all environment variables return null.

To reproduce:

  1. Install fresh L5.
  2. Edit /app/Http/Controllers/WelcomeController.php
  3. Add dd(env('APP_ENV')); in the index function.
  4. Make a bunch of requests in quick succession, i.e. hold down F5 for 2 seconds in a browser.

This bug occurs for me using WAMP 2.5 x64 (latest) on Windows 7.

GrahamCampbell commented 9 years ago

Duplicate. This is a known issue with php. We've discussed it here before. Feel free to search for the issue.

tylercubell commented 9 years ago

For anyone else wondering why this happens:

https://github.com/laravel/framework/commit/866a8cfc58430830069bd24feacf2d81e99385a2#commitcomment-9767647

https://github.com/laravel/framework/issues/7354

https://github.com/laravel/framework/pull/8187

tylercubell commented 9 years ago

Is there anyone working on a fix at PHP? I can't rely on the whole .env system if it's going to randomly crap out on me.

Is the only workaround to hard-code values into config files right now?

crynobone commented 9 years ago

Another workaround is to use php artisan config:cache for your production environment.

tylercubell commented 9 years ago

The artisan command didn't work for me. The same behavior still happens.

crynobone commented 9 years ago

The artisan command didn't work for me. The same behavior still happens.

You mean dd(env('APP_ENV')); behaviour? Obviously that expected which why I said "for your production environment". https://github.com/laravel/framework/blob/5.0/src/Illuminate/Foundation/Bootstrap/DetectEnvironment.php#L28

tylercubell commented 9 years ago

So to be clear:

1) In, say, /app/config/app.php add:

    'env' => env('APP_ENV')

2) Cache the config files using php artisan config:cache

3) Instead of calling env('APP_ENV'); use \Config::get('app.env'); to avoid the issue.

4) Use config:cache for every deployment.

This is like death by 1001 papercuts, just one more thing to worry about. Why was this feature added before the PHP bug was fixed? I don't get it...

crynobone commented 9 years ago

I don't get why would you need to cache APP_ENV, you cache other value such as DB_USERNAME, QUEUE_DRIVER etc. This way your app remain working all the time as this code is already resolved in the cached config.php.

Why was this feature added

http://12factor.net/config

Why not use PHP (just like Laravel 4)?

Well, if that the case, you need a new "duplicate" configuration for your none PHP app, if you want to use Python or Go for some of the background/queue process.

tylercubell commented 9 years ago

I don't need to, APP_ENV was just an example. I'm caching other things.

I'm not asking why the feature was added, I'm asking why now. Why before the PHP bug is fixed?

llioor commented 8 years ago

Hey @tylercubell, Is there any news about this issue? Right now I'm setting the "production values" as default in my config files like: (db_password, *_production_password_)

It is still happening in 5.2 or someone solve the issue? Thanks.

P4Thi0ut commented 8 years ago

Hi, I'm encountering the same issue here, no clear solution yet ? I still try to believe that Laravel is a great framework, having to worry about such issue is kind of unexpected and displeasing.

toby1kenobi commented 8 years ago

Please can the docs be updated, to reflect the requirement to use config:cache?! I just lost a few hours on trying to understand why env() was behaving unpredictably, which seems particularly futile now I discover it's been discussed here a few times.

GrahamCampbell commented 8 years ago

This is documented in our upgrading guide. If there are any other places you'd like to see this in the docs, we'd be very grateful if you could send a PR please. :)

toby1kenobi commented 8 years ago

Thanks for the quick response! So wait, is the docs site in Gitbhub, is that what you mean?

This page https://laravel.com/docs/5.2/configuration#determining-the-current-environment makes it sound like using .env would be a great thing to do, but even in dev I'm seeing it toggle between the values I want and null (where, presumably, default values don't exist)

llioor commented 8 years ago

From reading "Do something with dotenv finally #13906" I understand that there is not a real solution for it on Apache and what you need to do is to run: "php artisan config:cache" in commend. I think it's a MUST part in starting laravel project docs.

Thank you @GrahamCampbell for sharing with us the commend.

aruncnt commented 8 years ago

same issue with me

JJJJJJJerk commented 8 years ago

upgrade my php to 7.0.10 function env get null value

GrahamCampbell commented 8 years ago

This is a known issue with PHP. Make sure you run php artisan config:cache and never read env variables from outside the config files.

JJJJJJJerk commented 8 years ago

run php artisan config:clear will fix the env null value drama

GrahamCampbell commented 8 years ago

Not clear, but cache.

hugobluefuse commented 7 years ago

Hi,

when calling \Config::get('app.env'); I'm still getting local, although I changed the env to production. env(APP_ENV) returns null.

I'm using a thread safe php installation. Do I need to get the unsafe version?

PS: Workaround - get App:environment() from within App and pass it to the view

decadence commented 7 years ago

php artisan config:cache works for me on Windows 10, PHP 5.6, Apache 2.4. Is this bug for PHP, Windows or Apache?

llioor commented 7 years ago

Hi @Decadence , It is not a PHP/Windows/Apache BUG, it is a ".env" known bug which can be solved with: php artisan config:cache as you said.

Good luck.

milon commented 7 years ago

I am facing the same issue. And the sad part is, php artisan config:cache can't fix the issue. In my development environment(Mac OS sierra, php 7.0.12, laravel 5.3) if works fine, but in my production server(Ubuntu 14.04.4, php 7.0.11, laravel 5.3), it is not working. Any solution?

llioor commented 7 years ago

@milon yes... try to do php artisan config:cache

It will do php artisan cache:clear automatically.

milon commented 7 years ago

@llioor php artisan config:cache first calls the config:clear commad.

    public function fire()
    {
        $this->call('config:clear');

        $config = $this->getFreshConfiguration();

        $this->files->put(
            $this->laravel->getCachedConfigPath(), '<?php return '.var_export($config, true).';'.PHP_EOL
        );

        $this->info('Configuration cached successfully!');
    }

Anyway, I tried it as well. No luck.

liuyunzhuge commented 7 years ago

How can I solve this problem with lumen ?

milon commented 7 years ago

@liuyunzhuge in lumen you have to load the config file in bootstrap/app.php file like this-

$app->configure('config_file_name');

if you already do that, then comment the line and clear cache. then uncomment it again. I don't know why, but it worked for me.

hoangdangal commented 7 years ago

run this : php artisan config:clear

Jayc452 commented 7 years ago

I recently had this issue. it was fixed after I restarted the server.

Here is another solution I got from S.O

php artisan config:clear did the trick.

php artisan config:clear php artisan cache:clear service apache2 restart (just in case)

http://stackoverflow.com/questions/39046560/laravel-5-2-envapp-env-does-not-work-in-production

dmarman commented 7 years ago

What we should do if we are trying to use env() outside the config files?

In my middleware I have something like this:

public function handle($request, Closure $next)
    {
        if($request->header('api-token') != env('WATAPE_KEY')){
            abort(400);
        }

        return $next($request);
    }

Should I add my custom variables inside of a config file somehow and then call them with another function that is not env()?

UPDATE:

Yes, that solves the problem.

I created config/watape.php

<?php

return  [

    'api_key' => env('WATAPE_KEY'),

];

and now my middleware looks like this:

public function handle($request, Closure $next)
    {
        if($request->header('api-token') != config('watape.api_key')){
            abort(400);
        }

        return $next($request);
    }

Finally clear your cache and rebuild it again. I find this cleaner than calling directly env()

zackify commented 7 years ago

As someone not using laravel that often, it would have been nice for the first comment to mention how you should never use env variables directly and instead reference them in config files.

brexis commented 7 years ago

You should avoid calling env()in your application. In some environments like testing, the env() function returns null. A good practice on laravel is to put variables in a config file and to use config() function instead of env() https://laravel.com/docs/5.4/configuration#configuration-caching. You can also cache your config files with php artisan config:cache to speed up your application.

thinkspill commented 7 years ago

I ran into this issue when configuring Monolog in bootstrap/app.php. I was wanting to use different logging configurations depending on APP_ENV. Is there a recommended way to do this? Can Monolog be configured further down the line once APP_ENV is available to check?

BSN4 commented 7 years ago

don't use php artisan serve

caneco commented 7 years ago

What you can do is (re)load the .env content. This will allow you to have access even during production and with your cached config file.

echo 'APP_NAME='.env('APP_NAME').PHP_EOL; // outputs `null`
with(new \Dotenv\Dotenv(base_path()))->load();
echo 'APP_NAME='.env('APP_NAME').PHP_EOL; // outputs your APP_NAME
imbrish commented 6 years ago

Doing php artisan config:cache is the way to go in production, however this was bugging me a lot in local development, and thus I came up with a way to patch it.

app/Http/Kernel.php

protected function bootstrappers()
{
    // Remove original LoadEnvironmentVariables bootstrapper and prepend our custom version.
    return array_merge([
        \App\Library\Core\LoadEnvironmentVariables::class,
    ], array_diff($this->bootstrappers, [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
    ]));
}

app/Library/Core/LoadEnvironmentVariables.php

namespace App\Library\Core;

use Dotenv\Dotenv;
use Dotenv\Exception\InvalidPathException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables as OriginalLoadEnvironmentVariables;

class LoadEnvironmentVariables extends OriginalLoadEnvironmentVariables
{
    public function bootstrap(Application $app)
    {
        if ($app->configurationIsCached()) {
            return;
        }

        $this->checkForSpecificEnvironmentFile($app);

        try {
            (new Dotenv($app->environmentPath(), $app->environmentFile()))->overload();
        } catch (InvalidPathException $e) {
            //
        }
    }
}

app/Library/Utilities/helpers.php

function get_env($key, $default = null)
{
    // First try to obtain value from superglobals and only if that fails fall back to getenv.
    if (array_key_exists($key, $_ENV)) {
        $value = $_ENV[$key];
    }
    else if (array_key_exists($key, $_SERVER)) {
        $value = $_SERVER[$key];
    }
    else {
        $value = getenv($key);
    }

    // If variable does not exist return default value.
    if ($value === false) {
        return value($default);
    }

    // Finally parse and return the value.
    switch (strtolower($value)) {
        case 'true':
        case '(true)':
            return true;
        case 'false':
        case '(false)':
            return false;
        case 'empty':
        case '(empty)':
            return '';
        case 'null':
        case '(null)':
            return;
    }

    if (strlen($value) > 1 && Str::startsWith($value, '"') && Str::endsWith($value, '"')) {
        return substr($value, 1, -1);
    }

    return $value;
}

Then it is all about replacing default env helper with our brand new get_env.

It's good enough for my needs. I used it for some time now and haven't had any problems with env since. It works on my local dev machine on windows, but staging server didn't complain either.

@GrahamCampbell do you think it is worth a pr? Of course if problem remains, last checked on 5.4.

tylercubell commented 6 years ago

It's now been over two years and nothing has changed. I think this whole environment variable issue is representative of Laravel and its official packages (especially Cashier and Spark). It looks elegant at first glance but when you scratch the surface you find these types of ridiculous design decisions.

Why can't Laravel simply parse environment variables from a text file by itself? Why does it rely on an over-engineered solution that produces unstable results? I'm sorry but this is just plain stupid. Let the minority of users who care about server-level environment variables and understand the caveats opt-in instead of causing problems for the rest of us. Problem solved.

Cost > Benefit. You can clearly see that by all the StackOverflow questions and comments on this thread by now.

brexis commented 6 years ago

@imbrish did you try to clear the cache? php artisan cache:clear On development, you should avoid using php artisan config:cache.

imbrish commented 6 years ago

@brexis how does it help to solve the env problem?

alsilva86 commented 6 years ago

This is a shame for laravel, nobody giving a clear solution only clear and config cache suggestions

llioor commented 6 years ago

@alsilva86 what's wrong with this solution? php artisan config:cache It will clear your old config and then will re-cache it.

alsilva86 commented 6 years ago

Yes, to a lot of you who couldnt understand Let me give you this tip, when editing /var/www/html/app/config.php

'key' => env('APP_KEY','base64:qIaPsvTOUeK2RCOmEjG4xGr2YkeRz84HjXy9uXQ65cY='),

'cipher' => 'AES-256-CBC',

Dont use just the key. Its a Key, value field

2017-12-12 11:09 GMT-02:00 llioor notifications@github.com:

@alsilva86 https://github.com/alsilva86 what's wrong with this solution? php artisan config:cache It will clear your old config and then will re-cache it.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/laravel/framework/issues/8191#issuecomment-351046494, or mute the thread https://github.com/notifications/unsubscribe-auth/ALM6UwfE0OPE-LEZkLRokYelfHjR8Jloks5s_nsKgaJpZM4D1kgJ .

--

Andre Luiz / IT and Trading alsillva86@gmail.com / +55 11 984129269

[image: Linkedin] http://br.linkedin.com/in/bahuan[image: skype] http://alosilva86

bionicmaster commented 6 years ago

@llioor what's wrong with this solution? that doesn't apply at all to lumen, where this is happening too, we can't do config:cache or something else like that

iamvinny commented 6 years ago

The only thing that worked for me (on production) was

php artisan config:clear
imbrish commented 6 years ago

@iamvinny you seem to be talking about cached config not being updated when .env file is changed, then config:clear will help. But because of other issues you should keep you config cached in production, thus it's better to do config:cache which will clear and re-cache the config.

However many here experience different problem - when config is not cached multiple concurrent requests may cause env variables to be blank for some of these requests.

vcats commented 6 years ago

run php artisan config:clear if you don't want to use the config cache else use run php artisan config:cache

fkomaralp commented 6 years ago

Code from vlucas/phpdotenv repository

$dotenv = new \Dotenv\Dotenv("/var/www/html/", '.env'); $dotenv->load();

If I load my local .env file with this method my code is worked. After remove this line my code is getting an error, because env function is not working propaply.

You need to specify this in boot function on laravel. You can create a middleware function with:

php artisan make:middleware LoadEnv

and after that you need to add the code to handlemethod.

Or you can use the AppBefore middleware's handlemethod. Finished code is

<?php

namespace App\Http\Middleware;

use Closure;

class LoadEnv
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $dotenv = new \Dotenv\Dotenv("/var/www/html/", '.env');
        $dotenv->load();

        return $next($request);
    }
}

And you need to add middleware to Http\Kernel.php file

protected $middleware = [
        LoadEnv::class,
kjdion84 commented 6 years ago

dotenv is so garbage.

I still don't understand why you can't just do if (file_exists('env.php')) and then have those values be an array, which gets called before the actual config files, and maps them accordingly using a helper function. You could even have your own env() helper function which checks to see if env array values are set and if they are use them, otherwise use the default second parameter.

Why use a library for something that can literally be less than 10 lines of code?

gutitrombotto commented 6 years ago

Hi! I had the same problem; when I enable cache (artisan config:cache) the env() helper function does not work. So I have created my own function to parse the .env file and i have replaced every call to env() function by my own function. I don't know if this is not a good practice but this has resolved my problem. If you want, i can share my simple code. I only use Dotenv library.