ThinkPHP6 源码分析之请求处理

请求处理

return $this->app->route->dispatch($request, $withRoute);

这里就是处理整个业务的核心,之前的事件注册,路由注册,中间件注册,都会在这里执行。可能会涉及很多细节,但在这里不会细说,主要看一下整个分发的流程。看一下 dispatch 方法的代码

public function dispatch(Request $request, $withRoute = null)
{
    $this->request = $request;
    // 1
    $this->host    = $this->request->host(true);
   // 2
    $this->init();
   // 3
    if ($withRoute) {
        $checkCallback = function () use ($request, $withRoute) {
            $withRoute();
            return $this->check();
        };
        if ($this->config['route_check_cache']) {
            $dispatch = $this->cache
                ->tag('route_cache')
                ->remember($this->getRouteCacheKey($request), $checkCallback);
        } else {
            $dispatch = $checkCallback();
        }
    } else {
        $dispatch = $this->url($this->path());
    }
    // 4
    $dispatch->init($this->app);
    $this->app->middleware->add(function () use ($dispatch) {
        try {
            $response = $dispatch->run();
        } catch (HttpResponseException $exception) {
            $response = $exception->getResponse();
        }
        return $response;
    });

    return $this->app->middleware->dispatch($request);
}

按照1 ~ 4 步骤进行说明

  • 获取域名不含端口号
  • 初始化
  • route的初始化
  • 加载配置文件 route.php
  • 路由是否延迟解析
  • 设置路由缓存
  • 如果生成了路由缓存文件就加载路由缓存文件 => runtime 目录下的 route.php
  • 是否开启路由
  • 开启路由缓存 ,加载路由文件并且检测 URL,设置路由缓存 key 的还会将对应路由缓存起来。
    注意的是,remember 方法会执行闭包,所以存储的并不是闭包,而是 dispatch\url 对象
    获取路由缓存的 key,这个是在配置文件设置的,还必须是设置闭包。
  • 如果没有开启路由 直接解析 URL
  • dispatch 初始化
  • 因为 $dispatch instanceof dispatch\Controller 所以主要初始化工作在 Controller 里面
  • 将调度追加到中间件 middleware queue 队列中,所以这一步最近才会执行
  • 中间件调度执行

未启用路由

当前所在 think\Route 类中,再往下讲之前,先来看一下 url 这个方法。

public function url(string $url): UrlDispatch
{
    return new UrlDispatch($this->request, $this->group, $url);
}

这段代码很重要,因为请求在没有启用路由的情况是由它处理。$this->group 属性呢就是在当前类实例化的时候设置的 think\route\Domain 域名路由类。$url 是从 pathinfo 信息中获取的。了解了这些信息之后,直接看到步骤 4,因为上面的步骤最终目的都是获得这个 dispatch 对象。

启用路由的情况

启动路由的情况会产生另外一个 Dispatch 对象 think\route\dispatch\Callback,为什么会产生两个不同对象,具体细节在之后的路由解析中会详细介绍,先跳过细节,看看整个过程是如何处理的。下面会分别介绍两个对象的处理。

DisPatch 的初始化

上述两种情况下,获取控制和模块的方法是完全不一样。
前者是直接解析 URL 来获取,就是框架以前传统的方式 module/controller/index,然后解析出来。
后者是经过路由解析之后,才会获取相应模块的控制器,这部分有一定性能消耗。
所以对比来看,未启用路由性能比较高,但是还是推荐启用路由,这样比较易于管理。

Dispatch 执行

因为 Dispatch run 作为闭包被加入到中间的队列中之后,由中间件 Dispatch。关于中间执行可以参照上篇,这里需要知道的是 Dispatch 是被加到中间件队列末尾,是在最后执行的就可以了。
主要来看 $dispatch->run(),上文说的两个对象都是继承 Dispatch 对象的,所以到里面看看 run 方法。

public function run(): Response
{
    if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
        $rules = $this->rule->getRouter()->getRule($this->rule->getRule());
        $allow = [];
        foreach ($rules as $item) {
            $allow[] = strtoupper($item->getMethod());
        }

        return Response::create('', '', 204)->header(['Allow' => implode(', ', $allow)]);
    }

    $option = $this->rule->getOption();

    // 数据自动验证
    if (isset($option['validate'])) {
        $this->autoValidate($option['validate']);
    }

    $data = $this->exec();

    return $this->autoResponse($data);
}

启用路由和未启用路由完全是两种过程,启用理由设计到路由解析的过程,暂且搁置,后面详细说明。下面的过程是指的未启用路由的过程。

  • 通过 URL 获取访问路径,例如 index/index
  • 对于这样的 URL 默认会访问配置文件 route.php 的 controller_layer 所设置的目录下的类文件。框架默认设置的是 controller。创建框架的时候应该就可以看到了。
  • 对于控制器而言可以设置控制器的中间件,对于控制器中间件作用范围应该是执行方法之前,控制器初始化后。
  • 设置空控制器,默认是 Error,这个似乎和以前的版本是一样的。

这两种方式的区别,路由访问带来了一定的自由目录组织的能力,当然性能会有所损耗,Url 访问可能限制相对而言不是那么自由,比如对于初始化框架你只能限定在 controller 目录下创建类使用。当然这个是可以改变的,使用框架在初始化的先后循序上做一下改变,以应对框架的在 URL 解析上的规则,这个将会在下一篇事件机制上作出解答。

原文转载于thinkphp6源码分析之请求处理

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

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