Laravel 之道第九章:env 配置文件加载
7

接着上一章所说,这一章介绍 .env 文件的引导加载。

  • 如图,Illuminate\Foundation\Http\Kernel 类属性 $bootstrappers 中第一个引导启动类

file

Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables 类,从字面意义不难看出,就是有关 env 配置文件加载

正式介绍

如图,从 Illuminate\Foundation\Application 容器的 bootstrapWith 方法开始:

file

上一章我讲过,Laravel 的配置加载其实就是实例化相关类,并调用相关类的 bootstrap 方法,在这一章这个相关类就是 Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables

接下来我们进入 Illuminate\Foundation\Bootstrap\LoadEnvironmentVariablesbootstrap 中看一下

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

    $this->checkForSpecificEnvironmentFile($app);

    try {
        (new Dotenv($app->environmentPath(), $app->environmentFile()))->load();  // 核心
    } catch (InvalidPathException $e) {
        //
    } catch (InvalidFileException $e) {
        die('The environment file is invalid: '.$e->getMessage());
    }
}

从这个方法的代码上,不难看出,程序先检测有没有配置缓存,配置缓存就是 bootstrap/cache/config.php ,如果有这个文件,则退出加载,否则继续;接着检测特殊配置文件。

关于检测特殊配置文件的内容,我在这简单一说,不贴出代码了,这块不是重点:

检测特殊配置,首先是检测当前执行环境是 web 访问还是 cli 执行,如果是 cli 执行,如果命令行中含有 --env ,则设置配置文件路径,此功能结束;之后,通过 env 函数读取 APP_ENV 键,看是否有返回值,如果没有返回值,则结束检测,若果有返回值,将系统默认的 .env 文件路径与返回值拼接,设置到 env 配置路径中。

我现在执行的环境,用的的 web 访问,且 env('APP_ENV') 无返回值,即 .env 配置文件的路径为系统默认路径

下面我们看一下 env 配置加载核心代码 (new Dotenv($app->environmentPath(), $app->environmentFile()))->load(); 这段代码指,实例化 Dotenv.env 文件所在绝对路径以及文件名当做参数传入,然后调用 load 方法

如图,实例化 Dotenv 时的构造程序

file

$this->getFilePath($path, $file) 作用就是将路径和文件名拼接起来,构成完整的 .env 文件的绝对路径;然后实例化 Loader 类,将完整的 .env 文件路径传入,外加一个 true;取得 Loader 类的实例对象后,赋值给当前对象的 loader 属性

下面我们看一下 Loader 类的构造函数

public function __construct($filePath, $immutable = false)
{
    $this->filePath = $filePath;
    $this->immutable = $immutable;
}

不难看出,就是一个简单的属性赋值运算。

好了,整个 Dotenv 类的实例化结束,下面看一下它的 load 方法

public function load()
{
    return $this->loadData();
}

调用了 loadData 方法,继续看

protected function loadData($overload = false)
{
    return $this->loader->setImmutable(!$overload)->load();
}

调用了 Loader 类下的 setImmutable 方法,并传入 false,看一下这个 setImmutable 方法

public function setImmutable($immutable = false)
{
    $this->immutable = $immutable;

    return $this;
}

false 赋值给 immutable 属性,返回了 Loader 类对象

那么,我们就看一下 Loader 类对象下的 load 方法

public function load()
{
    $this->ensureFileIsReadable();

    $filePath = $this->filePath;
    $lines = $this->readLinesFromFile($filePath);
    foreach ($lines as $line) {
        if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
            $this->setEnvironmentVariable($line);
        }
    }

    return $lines;
}

呼!走了这么长的路,重要到了。。。

  • $this->ensureFileIsReadable(); 作用是检测 .env 文件是否存在,是否可读,可以时通过,不可以时,抛出异常,结束程序;
  • $lines = $this->readLinesFromFile($filePath); 作用是根据 .env 配置文件的换行符,将每一行的字符串叠加到一个索引数组中,并返回改数组,我们看一下这个方法
protected function readLinesFromFile($filePath)
{
    // Read file into an array of lines with auto-detected line endings
    $autodetect = ini_get('auto_detect_line_endings');
    ini_set('auto_detect_line_endings', '1');
    $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    ini_set('auto_detect_line_endings', $autodetect);

    return $lines;
}

其中 PHP 的配置项 auto_detect_line_endings ,看下图

file

意思是说,当调用 file 方法的时候,自动检测换行符。因为有性能损失,所以,先 ini_get 一下,用完,再设置回去。

关于 file 方法,看这里

最后给你看一下,读取完 .env 文件变量情况

file

我们回到 load 方法继续

foreach ($lines as $line) {
    if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
        $this->setEnvironmentVariable($line);
    }
}

这段循环值,将 $lines 数组每个元素进行解析,并赋值到系统环境变量中。

详解:

!$this->isComment($line) 指如果当前元素第一个字符有值并且是 # 代表 false ,不执行 setEnvironmentVariable 方法,这相当于是一个注释;具体看代码

protected function isComment($line)
{
    $line = ltrim($line);

    return isset($line[0]) && $line[0] === '#';  // 在这里哦,返回 boolean
}

$this->looksLikeSetter($line) 指在当前元素中有没有 = 号,有则返回 true ,看代码

protected function looksLikeSetter($line)
{
    return strpos($line, '=') !== false;
}

通过上面两个判断,就是说如果当前元素开头不是 #,且含有 = 则,执行 setEnvironmentVariable 方法

我们看一下 setEnvironmentVariable 方法

public function setEnvironmentVariable($name, $value = null)
{
    list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);

    $this->variableNames[] = $name;

    // Don't overwrite existing environment variables if we're immutable
    // Ruby's dotenv does this with `ENV[key] ||= value`.
    if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
        return;
    }

    // If PHP is running as an Apache module and an existing
    // Apache environment variable exists, overwrite it
    if (function_exists('apache_getenv') && function_exists('apache_setenv') && apache_getenv($name)) {
        apache_setenv($name, $value);
    }

    if (function_exists('putenv')) {
        putenv("$name=$value");
    }

    $_ENV[$name] = $value;
    $_SERVER[$name] = $value;
}

未调用 normaliseEnvironmentVariable 前,name 就是 $lines 一个元素(包含键和值的字符串), valuenull。调用 normaliseEnvironmentVariable 之后,程序会根据元素中的 = 进行键值分割,分别赋值给 $name$value

关于 normaliseEnvironmentVariable 方法,实现了元素以 = 分解键值对功能,识别值中加密串和分解值中后面的 # 注释的能力,识别 键和值 中变量的能力,最终解析出需要的内容,分别赋值给 $name$value

之后检测 PHP 内置是否存在一些函数,有则调用,并将获取的键和值作为参数传入,下面看一下 putenv 函数

if (function_exists('putenv')) {
    putenv("$name=$value");
}

我执行的环境中是有这个函数的,这是官方对于这个函数讲解

最后,进行了 $_ENV$_SERVER 赋值

$_ENV[$name] = $value;
$_SERVER[$name] = $value;

文章末尾贴一张,加载完 .env 的变量图

file

写在文章尾

通过上面的学习,我们知道了。 .env 文件是支持注释的,注释的开头是 # ,仅能支持一行,另一行也以 # 开头。还可以下载当前的行尾。如下

file

下一章讲解 config 配置文件的加载

我们是一群被时空压迫的孩子。 ---- 爱因斯坦

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 5

楼主可以先看我的 laravel 系列文章

1个月前
yuanshang

@leoyang 你好厉害,都写这么多了。我还在开始的地方慢慢爬。。。后面还有这么多,想想任重道远呀,慢慢学,慢慢写吧。。。

1个月前

@yuanshang 我当时写的时候也是很累,没有参考的文章,只能自己扣代码。你可以先看看我的文章,过一遍大致流程,然后再自己认真读代码,写博客,这样快很多

1个月前

@leoyang 感觉都是大神

1个月前
yuanshang

@leoyang 嗯嗯,好的

1个月前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!