ThinkPHP6 源码阅读(四):加载中间件

说明

上一篇分析了应用的初始化,也就是对Http类的run()方法里面调用的runWithRequest ()方法的第一行代码$this->initialize()的展开分析。让我们再看一眼runWithRequest ()方法的前几行:

protected function runWithRequest(Request $request)
{
    $this->initialize();

    // 加载全局中间件
    if (is_file($this->app->getBasePath() . 'middleware.php')) {
        $this->app->middleware->import(include $this->app->getBasePath() . 'middleware.php');
    }
.
.
.

应用初始化后,接下来开始处理中间件。

中间件类的初始化

依然是百用不厌的套路,通过$this->app->middleware来实例化中间件并获取其实例。从Middleware类中可看到,该类中有一个__make方法——在ThinkPHP的容器实现中,该方法会最先被调用。__make方法代码如下:

 public static function __make(App $app, Config $config)
{
    return (new static($config->get('middleware')))->setApp($app);
}

该方法有两个依赖类「App」和「Config」,通过ThinkPHP的容器,我们不费吹灰之力——仅仅在方法的参数添加类型约束的参数——就获得了两个方法所依赖的实例,$config$app
该方法首先传入「middleware」配置,实例化自身,然后调用setApp方法。setApp方法很简单:

public function setApp(App $app)
{
    $this->app = $app;
    return $this;
}

仅仅是将$app实例保存起来,最后返回Middleware类自身的一个实例。

执行完__make方法后,整个实例化Middleware类的流程回到make方法内,最终返回Middleware类的实例。

导入中间件

通过$this->app->middleware得到Middleware类的实例后,接着程序调用import方法,传入从「app」目录下的「middleware.php」文件中读取的数据。该文件的原始内容如下(原来全部注释掉的):

return [
    // 全局请求缓存
    // \think\middleware\CheckRequestCache::class,
    // 多语言加载
     \think\middleware\LoadLangPack::class,
    // Session初始化
    // \think\middleware\SessionInit::class,
    // 页面Trace调试
     \think\middleware\TraceDebug::class,
];

这里为了研究中间件是如何加载的,先去掉两个注释,也就是添加两个中间件。接下来看import方法:

public function import(array $middlewares = [], string $type = 'route'): void
{
    foreach ($middlewares as $middleware) {
        $this->add($middleware, $type);
    }
}

该方法传入一个中间件的数组和一个中间件类型,默认为route,关键是里面的add方法。跳到add方法:

public function add($middleware, string $type = 'route'): void
{
    if (is_null($middleware)) {
        return;
    }

    $middleware = $this->buildMiddleware($middleware, $type);

    if ($middleware) {
        $this->queue[$type][] = $middleware;
    }
}

实际上真正干活的是buildMiddleware方法,直接前往:

protected function buildMiddleware($middleware, string $type = 'route'): array
{
    // 是否是数组
    if (is_array($middleware)) {
        // 列出中间件及其参数
        // 这里说明我们可以给中间件传入参数,且形式为 [中间件, 参数]
        list($middleware, $param) = $middleware;
    }
    // 是否是一个闭包
    // 说明中间件可以是一个闭包
    if ($middleware instanceof \Closure) {
        //返回闭包和参数
        return [$middleware, $param ?? null];
    }
    // 排除了上面几种类型,且不是字符串,抛出错误
    if (!is_string($middleware)) {
        throw new InvalidArgumentException('The middleware is invalid');
    }

    //检查「$config」成员变量中是否有别名,有则解析出来
    if (isset($this->config[$middleware])) {
        $middleware = $this->config[$middleware];
    }

    //如果中间件有包含中间件(说明中间件可以嵌套)
    //再走一遍「import」递归解析
    if (is_array($middleware)) {
        $this->import($middleware, $type);
        return [];
    }
    //返回解析结果
    return [[$middleware, 'handle'], $param ?? null];
}

详细分析见以上代码注释。最后返回的结果,在add方法中,执行$this->queue[$type][] = $middleware;添加到一个队列。最终的解析结果大概是这样的:
ThinkPHP6 源码阅读(四):穿越中间件
至此,全局中间件就加载完毕。

Was mich nicht umbringt, macht mich stärker

讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!