深入理解thinkphp6的路由机制

thinkphp6路由请求基本流程(附源码)
1:入口文件先实例化容器,然后再通过容器去获取到Http对象 (Web管理类),然后执行Http对象中的run方法。
入口文件public/index.php

// [ 应用入口文件 ]
namespace think;
require __DIR__ . '/../vendor/autoload.php';    //引入composer
// 执行HTTP应用并响应
$http = (new App())->http;
$response = $http->run();       //执行HTTP类中的run类方法 并返回一个response对象
$response->send();          //执行response对象中的send类方法  该方法是处理并输出http状态码以及页面内容
$http->end($response);      //执行Http类的end方法

2:run方法内会初始化当前应用,简单来说就比如加载一下语言包,加载一下应用文件,common.php公共函数文件,helper.php助手函数文件、.env环境变量、运行开始的时间、设置时区等等。如果没有传入Request,则自动创建一个Request对象,然后将对象绑定到容器内。然后再到runWithRequest方法,执行应用程序

public function run(Request $request = null): Response
    {
        //初始化
        $this->initialize();
        //自动创建request对象
        $request = $request ?? $this->app->make('request', [], true);   //判断是否传入Request对象,如果没有则自动创建request对象
        $this->app->instance('request', $request);      //将Request绑定到App容器内
        try {
            $response = $this->runWithRequest($request);        //runWithRequest方法执行应用程序
        } catch (Throwable $e) {
            //如果捕捉到Throwable异常 则执行reportException方法
            //调用Handle::class实例中的report方法。收集异常信息
            $this->reportException($e);
            //通过调用Handle::class实例中的render方法
            //获取异常信息输出流
            $response = $this->renderException($request, $e);
        }
        return $response;
    }

3:runWithRequest方法内,会加载加载中间件,监听HttpRun。然后到dispatchToRoute方法,传入当前的Request请求对象。

protected function runWithRequest(Request $request)
    {
        // 加载全局中间件
        $this->loadMiddleware();
        // 监听HttpRun
        $this->app->event->trigger(HttpRun::class);
        return $this->app->middleware->pipeline()
            ->send($request)
            ->then(function ($request) {
                return $this->dispatchToRoute($request);	//加载路由
            });
    }

4:dispatchToRoute方法,应该算是路由初始化方法。主要就是检测配置文件,是否开启了路由。如果开启了路由。就加载路由文件。并且设置一个匿名函数。只有在调用的时候才会加载设置的路由。接着会通过容器获取route类的实例。并且传入当前Request对象和路由配置的匿名函数并执行里面的dispatch方法

protected function dispatchToRoute($request)
    {
        //获取配置文件中的with_route 判断是否加载路由
        //如果加载则返回一个匿名函数。里面是路由文件内的设置,否则返回null
        $withRoute = $this->app->config->get('app.with_route', true) ? function () {
            $this->loadRoutes();
        } : null;
        return $this->app->route->dispatch($request, $withRoute);
    }

5:dispatch方法,主要就是路由初始化。判断路由是否配置。如果没有配置就直接执行默认控制器和默认方法。如果有的话。就加载一下路由配置,再通过check方法检测是什么路由。然后调用url方法传入当前的url地址

public function dispatch(Request $request, $withRoute = true)
    {
        $this->request = $request;          //设置传入的Request对象到当前对象的属性上
        $this->host    = $this->request->host(true);        //设置host
        $this->init();          //初始化
        //判断的withRoute是否为真
        if ($withRoute) {
            //加载路由
            if ($withRoute instanceof Closure) {
                $withRoute();           //执行传入过来的匿名函数,加载路由
            }
            $dispatch = $this->check();     //check返回的是think\route\Dispatch 路由调度基础类对象
        } else {
            $dispatch = $this->url($this->path());      //将当前的url地址传入进去,进行默认的url解析
        }
        $dispatch->init($this->app);    //这里用于绑定控制器和方法以及路由后置操作,例如:中间件、绑定模型数据
        //执行路由调度。并返回一个Response对象
        return $this->app->middleware->pipeline('route')
            ->send($request)
            ->then(function () use ($dispatch) {
                return $dispatch->run();
            });
    }

初始化路由的方法

public function check()
    {
        // 自动检测域名路由
        $url = str_replace($this->config['pathinfo_depr'], '|', $this->path());     //转换PATH_INFO分隔符,拼接url

        $completeMatch = $this->config['route_complete_match'];     //获取是否完全匹配 配置项
        //调用checkDomain检测是否为域名路由如果不是则返回false
        //如果是域名路由,则返回一个Domain对象。并且执行对象中的check方法
        //并把当前的Request请求对象以及url地址和是否完全匹配路由项传入进去
        $result = $this->checkDomain()->check($this->request, $url, $completeMatch);
        //判断result是否为false 也就是不是域名路由
        //再判断是否为跨域路由
        if (false === $result && !empty($this->cross)) {
            // 检测跨域路由
            $result = $this->cross->check($this->request, $url, $completeMatch);
        }
        //如果是域名路由
        if (false !== $result) {
            return $result;
        } elseif ($this->config['url_route_must']) {
            //判断是否启用了强制路由,如果启用了强制路由
            //然后域名路由也匹配不上。就触发一个路由
            //找不到的异常类
            throw new RouteNotFoundException();
        }
        return $this->url($url);
    }

6:url方法执行并且会返回一个Url基础类。主要的作用就是将传入的Request对象,rule路由规则对象,以及当前的url地址。把url地址解析。这里又去调用了一个parseUrl方法。

public function __construct(Request $request, Rule $rule, $dispatch)
    {
        $this->request = $request;  //获取传入来的Request对象,存储到类成员变量内
        $this->rule    = $rule;     //获取到路由列表,也放到类成员变量内
        // 解析默认的URL规则
        $dispatch = $this->parseUrl($dispatch);

        parent::__construct($request, $rule, $dispatch, $this->param);
    }

7:parseUrl方法。它主要的作用就是分割出来要执行的control和function。

protected function parseUrl(string $url): array
    {
        $depr = $this->rule->config('pathinfo_depr');       //获取到分隔符
        $bind = $this->rule->getRouter()->getDomainBind();          //获取当前域名
        //如果域名不为空,并且正则匹配的到
        if ($bind && preg_match('/^[a-z]/is', $bind)) {
            $bind = str_replace('/', $depr, $bind);     //切割url,换成配置项中的PATH_INFO分隔符
            // 如果有域名绑定
            $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr);// 如果有域名绑定
        }
        //调用rule类中的parseUrlPath方法,切割pathinfo参数
        //如果url中有参数 返回一个demo吧  ['Index','Demo']
        //第一个为控制器、第二个为方法
        $path = $this->rule->parseUrlPath($url);
        //如果切割的pathinfo为空,则直接返回一个[null,null] 这样的一个空数组
        if (empty($path)) {
            return [null, null];
        }

        // 解析控制器
        $controller = !empty($path) ? array_shift($path) : null;
        //正则匹配,如果匹配不到。就弹出一个HttpException异常
        if ($controller && !preg_match('/^[A-Za-z0-9][\w|\.]*$/', $controller)) {
            throw new HttpException(404, 'controller not exists:' . $controller);
        }

        // 解析操作
        $action = !empty($path) ? array_shift($path) : null;
        $var    = [];

        // 解析额外参数
        if ($path) {
            preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
                $var[$match[1]] = strip_tags($match[2]);
            }, implode('|', $path));
        }

        $panDomain = $this->request->panDomain();
        if ($panDomain && $key = array_search('*', $var)) {
            // 泛域名赋值
            $var[$key] = $panDomain;
        }

        // 设置当前请求的参数
        $this->param = $var;

        // 封装路由
        $route = [$controller, $action];

        if ($this->hasDefinedRoute($route)) {
            throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url));
        }

        return $route;
    }

8:返回到第五步。去调用url类中的init方法(路由后置操作。中间件).然后通过middleware中的then。到最后执行一个上一步返回的Url类中的run方法

protected function init()
    {
        //判断路由中间件是否存储,如果存在则调用middleware类中的import方法
        if (!empty($this->config['middleware'])) {
            $this->app->middleware->import($this->config['middleware'], 'route');
        }
        //是否延迟解析
        $this->lazy($this->config['url_lazy_route']);
        $this->mergeRuleRegex = $this->config['route_rule_merge'];  //读取到的 是否合并路由配置项赋值到类变量mergeRuleRegex中
        $this->removeSlash    = $this->config['remove_slash'];  //获取配置:是否删除url最后的斜线

        $this->group->removeSlash($this->removeSlash);  //是否去除url最后的斜线
    }

9:run这里主要就是最通过exec再去获取控制器(controller)和对应的方法(function)的结果。然后创建一个Response对象。最后返回

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('', 'html', 204)->header(['Allow' => implode(', ', $allow)]);
        }

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

10:最后回到了入口文件run方法下面还有一个send方法,主要作用就是输出
11:入口文件最后一行。调用了一下Http类的end方法。简单说就是挂个HttpEnd中间件。然后执行中间件。最后再记录一下日志。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThinkPHP6 中,路由配置主要有两种方式:注解路由和配置文件路由。 #### 1. 注解路由 注解路由是一种基于注释的路由方式,可以在控制器方法上添加注释来定义路由规则。例如: ```php // 注解路由示例 namespace app\controller; use think\annotation\Route; class Index { /** * @Route("/") */ public function index() { return 'Hello, ThinkPHP6!'; } /** * @Route("/hello/:name") */ public function hello($name) { return 'Hello, ' . $name . '!'; } } ``` 在上面的示例中,我们使用了 `think\annotation\Route` 注解,通过 `@Route` 注释来定义路由规则。例如,在 `index()` 方法上添加了 `@Route("/")` 注释,表示将 `/` 路径映射到 `index()` 方法上;在 `hello($name)` 方法上添加了 `@Route("/hello/:name")` 注释,表示将 `/hello/:name` 路径映射到 `hello($name)` 方法上,并将 `:name` 参数作为方法的参数传递。 #### 2. 配置文件路由 配置文件路由是一种基于配置文件的路由方式,可以在应用的 `route` 目录下创建 `route.php` 文件,来定义路由规则。例如: ```php // route/route.php use think\facade\Route; Route::get('/', 'index/index'); Route::get('/hello/:name', 'index/hello'); ``` 在上面的示例中,我们通过 `use think\facade\Route` 引入了 `Route` 门面类,并使用 `Route::get()` 方法来定义路由规则。例如,`Route::get('/', 'index/index')` 表示将 `/` 路径映射到 `app\controller\Index` 控制器的 `index()` 方法上。`Route::get('/hello/:name', 'index/hello')` 则表示将 `/hello/:name` 路径映射到 `app\controller\Index` 控制器的 `hello($name)` 方法上,并将 `:name` 参数作为方法的参数传递。 以上是 ThinkPHP6 中路由配置的基本介绍,如果需要更多的路由配置方式,可以参考官方文档:[路由](https://www.kancloud.cn/manual/thinkphp6_0/1037479)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值