2.2 - Laravel - 5.6 - Facade - 关于 facade 加载

larave 在启动的时候会预先加载 facade 已经配置好的映射。以便在后面 facade 调用的时候使用。

启动流程是这样的#

1. 起始页面 index.php 加载了
$app = require_once __DIR__.'/../bootstrap/app.php';

2. 在 bootstrap/app.php 中加载

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

3. 然后在 App\Http\Kernel::class父类中加载了

protected $bootstrappers = [
        ...
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        ...
    ];

4. 这个 RegisterFacades::class 就是 facade 预加载的核心类,只有一个 bootstrap 方法:

public function bootstrap(Application $app)
    {
        //1 第一步
        Facade::clearResolvedInstances();
        //2 第二步
        Facade::setFacadeApplication($app);
        //3 第三步
        AliasLoader::getInstance(array_merge(
            $app->make('config')->get('app.aliases', []),
            $app->make(PackageManifest::class)->aliases()
        ))->register();
    }

bootstrap 方法做了如下的事情:
1. 清空对象类数组实例
1.1 清空处理就是置空。

public static function clearResolvedInstances()
    {
        static::$resolvedInstance = [];
    }

2. 把容器放入 facade 对象,就是 facade 中的 set 方法
在 2.1 上一章的 abstract class Facade 中,我们使用 setFacadeApplication 方法来注入容器对象 (app) 给 facade 类。

public static function setFacadeApplication($app)
{
    static::$app = $app;
}

3. 把 facade 对象从 config 中读取后注册(这个注册就是建立关系)
3.1 获取所有配置的 facade 对应关系

$app->make('config')->get('app.aliases', []),
            $app->make(PackageManifest::class)->aliases()

默认的别名配置是从 app 配置文件下的 aliases 数组 读取的,PackageManifest 是 laravel 5.5 新增的 包自动发现规则,这里我们暂时不考虑,还没研究。

3.2 使用 AliasLoader 的 getInstance 方法注册这些对应关系。

public static function getInstance(array $aliases = [])
    {
        if (is_null(static::$instance)) {
            return static::$instance = new static($aliases);
        }

        $aliases = array_merge(static::$instance->getAliases(), $aliases);

        static::$instance->setAliases($aliases);

        return static::$instance;
    }

3.2.1 第一步 如果当前这个实例为空,重新生成一个
3.2.2 把当前已经有的 alias (别名) 和传入的 alias 合并
3.2.3 返回当前这个 AliasLoader 实例,包含了所有该有的别名映射。

3.3 使用 register () 方法注册

public function register()
    {
        if (! $this->registered) {
            $this->prependToLoaderStack();

            $this->registered = true;
        }
    }

3.3.1 prependToLoaderStack 函数通过使用 php 自动加载方法 spl_autoload_register 把当前对象(当前 AliasLoader 类中有个 load 方法)的 load 方法 放到 SPL __autoload 函数队列的头部。如果 php 运行中触发了没有声明的类,会自动运行 load 这个函数。load 函数通常都要引入(require 或者 include)需要的类,如果没有,就会在队列中继续寻找需要的类。(详细部分放在下一章 2.3)

protected function prependToLoaderStack()
{
    spl_autoload_register([$this, 'load'], true, true);
}

3.3.1.1 然后看下这个 load 函数,主要存在两种情况。

public function load($alias)
    {
        if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
            $this->loadFacade($alias);
            return true;
        }
        if (isset($this->aliases[$alias])) {
            return class_alias($this->aliases[$alias], $alias);
        }
    }

a. 判断 facade 的别名中是否存在启始位置为 Facades\\ 的字段,如果有就要调用 loadFacade() 去加载。
b. 如果没有,这里是核心方法,使用了 php 的 class_alias 方法把别名和真实的类建立关系

比如我们直接使用 Route 时,我们其实是调用的 Illuminate\Support\Facades\Route 类。

说到底,这个整个第三步其实就是使用了 class_alias 给配置项产生一个别名。然后存起来#

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。