搜索
首页php框架ThinkPHPThinkPHP6加载中间件及多应用解析

一、加载中间件

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

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

    // 加载全局中间件
    $this->loadMiddleware();
    .
    .
    .

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

中间件类的初始化

loadMiddleware 方法:

protected function loadMiddleware(): void
{
    if (is_file($this->app->getBasePath() . 'middleware.php')) {
        $this->app->middleware->import(include $this->app->getBasePath() . 'middleware.php');
    }
}

依然是百用不厌的套路,通过 $this->app->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 = 'global'): void
{
    foreach ($middlewares as $middleware) {
        $this->add($middleware, $type);
    }
}

该方法传入一个中间件的数组和一个中间件类型,默认为 global,关键是里面的 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;
        // 去除重复
        $this->queue[$type]   = array_unique($this->queue[$type], SORT_REGULAR);
    }
}

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

protected function buildMiddleware($middleware, string $type): 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');
    }

    //中间件别名检查
     $alias = $this->app->config->get('middleware.alias', []);

     if (isset($alias[$middleware])) {
        $middleware = $alias[$middleware];
    }

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

详细分析见以上代码注释。最后返回的结果,在 add 方法中,执行 $ this->queue[$type][] = $middleware; 添加到一个队列。最终的解析结果大概是这样的(app/middleware.php 去掉部分中间件的注释):

ThinkPHP6加载中间件及多应用解析

至此,全局中间件就加载完毕。

二、多应用解析

加载完中间件,接下来一步是多应用解析(ThinkPHP 6 开始支持多应用模式)。

if ($this->multi) {
    $this->parseMultiApp();
}

注意到,Http 类的构造函数:

public function __construct(App $app)
{
    $this->app   = $app;
    //多应用解析,通过判断「app」目录下有无「controller」目录,没有就是多应用模式
    $this->multi = is_dir($this->app->getBasePath() . 'controller') ? false : true;
}

可以看到,程序是通过判断「app」目录下有无「controller」目录来决定是否是多应用模式的。

接着看主要方法 parseMultiApp:

protected function parseMultiApp(): void
{
    // 虽然在「Http」的构造函数自动判断过是否开启多应用
    //如果没有controller目录,$this->multi为true,就会来到本方法
    // 接着还要看配置文件是否有配置
    if ($this->app->config->get('app.auto_multi_app', false)) {
        // 自动多应用识别
        $this->bindDomain = false;
        // 获取域名绑定
        $bind = $this->app->config->get('app.domain_bind', []);
        // 如果有域名绑定
        if (!empty($bind)) {
            // 获取当前子域名
            $subDomain = $this->app->request->subDomain();
            $domain    = $this->app->request->host(true);

            //完整域名绑定
            if (isset($bind[$domain])) {
                $appName          = $bind[$domain];
                $this->bindDomain = true;
                //子域名绑定
            } elseif (isset($bind[$subDomain])) {
                $appName          = $bind[$subDomain];
                $this->bindDomain = true;
                //二级泛域名绑定
            } elseif (isset($bind['*'])) {
                $appName          = $bind['*'];
                $this->bindDomain = true;
            }
        }
        //如果没有域名绑定
        if (!$this->bindDomain) {
            //获取别名映射
            $map  = $this->app->config->get('app.app_map', []);
            //获取禁止URL访问目录
            $deny = $this->app->config->get('app.deny_app_list', []);
            //获取当前请求URL的pathinfo信息(含URL后缀)
            // 比如 index/index/index
            $path = $this->app->request->pathinfo();
            // 比如,从index/index/index获取得index
            $name = current(explode('/', $path));
            //解析别名映射
            if (isset($map[$name])) {
                //如果这个别名映射到的是一个闭包
                //这样不知有啥用
                if ($map[$name] instanceof Closure) {
                    $result  = call_user_func_array($map[$name], [$this]);
                    $appName = $result ?: $name;
                    //直接取得应用名
                } else {
                    $appName = $map[$name];
                }
                //$name不为空且$name在$map数组中作为KEY,或者$name是禁止URL方位的目录
            } elseif ($name && (false !== array_search($name, $map) || in_array($name, $deny))) {
                throw new HttpException(404, 'app not exists:' . $name);
            } elseif ($name && isset($map['*'])) {
                $appName = $map['*'];
            } else {
                $appName = $name;
            }

            if ($name) {
                $this->app->request->setRoot('/' . $name);
                $this->app->request->setPathinfo(strpos($path, '/') ? ltrim(strstr($path, '/'), '/') : '');
            }
        }
    } else {
        $appName = $this->name ?: $this->getScriptName();
    }

    $this->loadApp($appName ?: $this->app->config->get('app.default_app', 'index'));
}

可以看到,「pathinfo」信息的第一节会被解析成应用名称,比如 index/index/index/ 中的 index。方法的最后还调用了 loadApp 方法,执行的操作与前面应用的初始化类似,只是加载的文件都在该应用的目录。

跟之前的版本对比,ThinkPHP 6 貌似把原先的模块改造成了多应用,因为多应用情况下,应用名跟之前的模块名都是从 pathinfo 的第一节解析出来的,新的文档也没见到模块的内容了。

相关推荐:最新的10个thinkphp视频教程

以上是ThinkPHP6加载中间件及多应用解析的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:segmentfault。如有侵权,请联系admin@php.cn删除

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境