美文网首页
Laravel中env函数的小坑

Laravel中env函数的小坑

作者: 原装匪兵甲 | 来源:发表于2017-02-23 09:55 被阅读0次

昨天同事在他的项目中遇到了一个技术难题让我协助解决

问题出在env helper 函数上,在Middleware中读取环境变量时取到的竟然是null

dump(env('APP_ENV') === null); // true

据他反映,之前本地开发时功能都是正常的,上线之后才开始出现的问题

查看env 的代码

// Illuminate/Foundation/helpers.php
function env($key, $default = null)
{
    $value = getenv($key);

    if ($value === false) {
        return value($default);
    }

    // 此处省略部分代码

    return $value;
}

可以发现,实际上调用的就是php自带的getenv函数,如果取不到值,那必定是某段执行putenv或者操作$_ENV全局变量的代码没有执行,而且这些环境变量设置操作一定是在初始化的时候进行的

如果对Laravel的Kernel执行顺序比较了解的话,应该知道,在Kernel启动阶段会首先执行一组bootstrappers

//Illuminate/Foundation/Http/Kernel.php
class Kernel implements KernelContract
{
    /**
     * The bootstrap classes for the application.
     *
     * @var array
     */
    protected $bootstrappers = [
        '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',
    ];

其中一个bootstrapper名为DetectEnvironment,而它的嫌疑是最大的,检查这个类

//Illuminate/Foundation/Bootstrap/DetectEnvironment.php
class DetectEnvironment
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        if (! $app->configurationIsCached()) {
            $this->checkForSpecificEnvironmentFile($app);

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

在代码中,我们并没有发现任何执行putenv或者操作$_ENV全局变量的地方,实际上在整个Laravel的框架代码里也没有发现蛛丝马迹,那么这些环境变量是如何保存进来的呢?

如果Laravel框架里没有执行这些代码,我们应该可以推测是框架使用的第三方组件做了这些事情

而事实也确实如此,可以看到在DetectEnvironment类的bootstrap方法里实例化了一个Dotenv类的对象,而Dotenv\Loader正是执行了读取.env文件,执行putenv的操作。

vlucas/phpdotenv Laravel那一套.env文件的玩法就是使用的这个组件,有兴趣的同学可以研究一下

好了,暂时总结一下分析的结果

  • 环境变量的读取函数env是在helpers.php里定义的,执行的是php的getenv
  • 环境变量的存储经过确认是第三方的 vlucas/phpdotenv 来完成的

那么env获取不到值只能说明Dotenv的实例化并没有执行

再仔细看一下DetectEnvironment类,在实例化Dotenv前,先进行了一个 if 判断

if (! $app->configurationIsCached())

而这个方法的作用就是判断 bootstrap/cache/config.php 缓存文件是否存在,如果文件存在则不会执行Dotenv的实例化操作,也就不会往$_ENV里存数据,自然env函数是取不到值的

bootstrap/cache/config.php是执行下面的命令生成的

php artisan config:cache

命令说明如下

Create a cache file for faster configuration loading

这是对config目录下的所有配置文件做的缓存

那么,真相大白了,之前在开发时并没有出现问题,是因为没有执行 config:cache 命令,而项目上线后为了优化访问速度,生成了缓存文件,导致env取值失败

可以疑问还是存在,既然Laravel提供了env函数,为什么还有这个坑呢?是bug吗?如果env不能使用,那该使用什么方式呢?

本着一探究竟的态度,仔细阅读了文档,在 Upgrade Guide 里找到了答案

Caching And Env

If you are using the config:cache command during deployment, you must make sure that you are only calling the env function from within your configuration files, and not from anywhere else in your application.

如果使用了config:cache,env函数只能在config目录下的配置文件的php里使用,不可以在其他地方使用。

一个非常简单的办法就是将

env('APP_ENV')

改为

config('app.env')

config函数会优先读取 bootstrap/cache/config.php 中缓存的配置,如果没有缓存文件,则会直接读取 config 目录下的所有配置文件

想研究这块代码实现的可以看下另一个bootstrapper: LoadConfiguration,它是用来初始化 config service 的,而 config 函数使用的就是这个 service

相关文章

  • Laravel中env函数的小坑

    昨天同事在他的项目中遇到了一个技术难题让我协助解决 问题出在env helper 函数上,在Middleware中...

  • laravel .env文件的坑

    问题描述: 一个apache下部署多个laravel项目,config目录的的配置文件使用env()函数获取配置项...

  • env获取不到数据,应该怎么解决

    不知道大家有没有遇到过,在 Laravel 中(除 app/config 目录下的配置文件中)使用 env 函数读...

  • Laravel6的小坑记录

    题记 记录一下在用Laravel开发过程中遇到的问题,内容仅限基于Laravel6的版本 ENV的配置 .env文...

  • Laravel 文章汇集

    LARAVEL .ENV文件配数组laravel redis操作大全laravel5篇之使用事务laravel多库...

  • laravel5.4实现实时聊天室

    一、修改laravel的广播驱动 修改 .env中: 二、创建laravel事件广播 比如要创建一个发送聊天消息的...

  • 项目多环境配置

    本文的示例代码参考env-springboot&env-laravel 目录 Spring Boot Larave...

  • laravel-队列

    此处用laravel5.8做示例 .env配置 # .env 中 配置连接方式。对一般中小型应用推荐使用 Redi...

  • laravel中env文件的加载

    前言 我们的应用程序几乎都会遇到不同环境需要不同的配置文件,比如最常见的开发环境需要连接开发数据库,生产需要连接生...

  • laravel 在update更新时 Validator验证如何

    [laravel Validator验证坑一]: [laravel Validator验证坑二]:

网友评论

      本文标题:Laravel中env函数的小坑

      本文链接:https://www.haomeiwen.com/subject/tmivwttx.html