[原博客地址](https://blog.csdn.net/qq_42050394/article/details/105498211)
关于请求流程大概是:
入口文件先实例化容器,然后再通过容器去获取到Http对象 (Web管理类),然后执行Http对象中的run方法。
方法内会创建一个Request对象,然后将对象绑定到容器内。然后再到runWithRequest方法,执行应用程序
runWithRequest方法内会初始化当前应用,简单来说就比如加载一下语言包,加载一下应用文件。common.php公共函数文件。helper.php助手函数文件、.env环境变量、运行开始的时间、设置时区、加载中间件等等。然后到dispatchToRoute方法,传入当前的Request请求对象。
dispatchToRoute方法,这里我姑且称为路由初始化方法。这里主要就是检测配置文件,是否开启了路由。如果开启了路由。就加载路由文件。并且设置一个匿名函数。只有在调用的时候才会加载设置的路由。接着会通过容器获取route类的实例。并且传入当前Request对象和路由配置的匿名函数并执行里面的dispatch方法
dispatch方法,主要就是路由初始化。判断路由是否配置。如果没有配置就直接执行默认控制器和默认方法。如果有的话。就加载一下路由配置,再执行check方法,通过check方法。去检测是什么路由。然后调用url方法传入当前的url地址
url方法执行并且会返回一个Url基础类
这里我称之为url且切割类吧。他主要的作用就是将传入的Request对象,rule路由规则对象,以及当前的url地址。把url地址解析。这里又去调用了一个parseUrl方法。
这个方法我就不多介绍了。它主要的作用就是分割出来要执行的control和function。
到这里就结束了url的部分。又回到了第五部。去调用url类中的init方法(路由后置操作。中间件).然后通过middleware中的then。到最后执行一个上一步返回的Url类中的run方法
run这里主要就是最通过exec再去获取控制器(controller)和对应的方法(function)的结果。然后创建一个Response对象。最后返回
最后回到了入口文件run方法下面还有一个send方法。源码就不贴了。他的作用就是输出
入口文件最后一行。调用了一下Http类的end方法。简单说就是挂个HttpEnd中间件。然后执行中间件。最后再记录一下日志。
### 1、public/index.php 入口文件
```
namespace think;
//引入composer
require __DIR__ . '/../vendor/autoload.php';
//通过Ioc容器将HTTP类实例出来
// 执行HTTP应用并响应
$http = (new App())->http;
//执行HTTP类中的run类方法 并返回一个response对象
$response = $http->run();
//执行response对象中的send类方法 该方法是处理并输出http状态码以及页面内容
$response->send();
//执行response对象中的send方法
$http->end($response);
```
### 2、通过\\Think\\App容器获取到Http对象,然后再执行Http对象中的run方法
```
/**
* 执行应用程序
* @access public
* @param Request|null $request
* @return Response
*/
public function run(Request $request = null): Response
{
//判断是否传入Request对象,如果没有则创建
$request = $request ?? $this->app->make('request', [], true);
//将Request绑定到App容器内
$this->app->instance('request', $request);
try {
//runWithRequest方法 作用是执行应用程序
$response = $this->runWithRequest($request);
} catch (Throwable $e) {
//如果捕捉到Throwable异常 则执行reportException方法
//调用Handle::class实例中的report方法。收集异常信息
$this->reportException($e);
//通过调用Handle::class实例中的render方法
//获取异常信息输出流
$response = $this->renderException($request, $e);
}
//return 内容
return $response;
}
```
### 3、\\Think\\Http->runWithRequest() 初始化应用程序,并执行
```
protected function runWithRequest(Request $request)
{
//初始化应用程序
$this->initialize();
// 加载全局中间件
$this->loadMiddleware();
// 设置开启事件机制
$this->app->event->withEvent($this->app->config->get('app.with_event', true));
// 监听HttpRun
$this->app->event->trigger(HttpRun::class);
//这里重点
return $this->app->middleware->pipeline()
->send($request)
->then(function ($request) {
//通过dispatchToRoute方法加载路由
return $this->dispatchToRoute($request);
});
}
```
### 4、\\Think\\Http->dispatchToRoute()
```
protected function dispatchToRoute($request)
{
//通过容器控制反转快速获取config类实例
//并获取配置文件中的with_route 判断是否加载路由
//如果加载则返回一个匿名函数。里面是路由文件内的设置
$withRoute = $this->app->config->get('app.with_route', true) ? function () {
$this->loadRoutes();
} : null;
//执行\Think\Route类中的dispatch方法,并且将获取到的路由
//文件以及当前的Request实例传入到路由中,然后进行路由调度
return $this->app->route->dispatch($request, $withRoute);
}
```
### 5、\\Think\\Route->dispatch() 路由
```
public function dispatch(Request $request, $withRoute = null)
{
//设置传入的Request对象到当前对象的属性上
$this->request = $request;
//同上 这个是设置host
$this->host = $this->request->host(true);
//执行Route init 方法 初始化
$this->init();
//判断的withRoute是否为真
if ($withRoute) {
//执行传入过来的匿名函数,加载路由
$withRoute();
//官方注释的是检测url路由,我这里姑且认为是路由分发吧
//check返回的是think\route\Dispatch 路由调度基础类对象
$dispatch = $this->check();
} else {
//调用think\route\dispatch\Url类
//将当前的url地址传入进去,进行默认的url解析
$dispatch = $this->url($this->path());
}
//执行Dispatch对象中的init方法
//这里用于绑定控制器和方法以及路由后置操作,例如:中间件、绑定模型数据
$dispatch->init($this->app);
//执行路由调度。并返回一个Response对象
return $this->app->middleware->pipeline('route')
->send($request)
->then(function () use ($dispatch) {
return $dispatch->run();
});
}
```
### 6、\\Think\\Route->init() 初始化
```
protected function init()
{
//合并默认配置以及读取到的route.php配置
$this->config = array_merge($this->config, $this->app->config->get('route'));
//判断路由中间件是否存储,如果存在则调用middleware类中的import方法
//注册route中间件
if (!empty($this->config['middleware'])) {
$this->app->middleware->import($this->config['middleware'], 'route');
}
//是否延迟解析
$this->lazy($this->config['url_lazy_route']);
//读取到的 是否合并路由配置项赋值到类变量mergeRuleRegex中
$this->mergeRuleRegex = $this->config['route_rule_merge'];
//获取配置:是否删除url最后的斜线
$this->removeSlash = $this->config['remove_slash'];
//是否去除url最后的斜线
$this->group->removeSlash($this->removeSlash);
}
```
### 7、Think\\Route\\Dispatch->init()
```
public function init(App $app)
{
$this->app = $app;
// 执行路由后置操作
$this->doRouteAfter();
}
```
### 8、Think\\Route\\Dispatch->run()
```
public function run(): Response
{
//判断$this->rule路由规则是否为RuleItem类的实例
//判断当前请求方法,是不是OPTIONS以及
//判断当前路由规则是否为自动注册的OPTIONS路由
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());
}
//创建并返回一个Response对象,调用create静态方法
return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]);
}
//如果上面的不匹配则调用当前Dispatch类中的exec方法
//实例化控制器以及方法
$data = $this->exec();
//最后动态的返回一个Response对象。xml、json等等
return $this->autoResponse($data);
}
```
### 9、\\Think\\Route->check()
```
public function check(): Dispatch
{
//转换PATH_INFO分隔符,拼接url
$url = str_replace($this->config['pathinfo_depr'], '|', $this->path());
//获取是否完全匹配 配置项
$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)) {
// 如果是跨域路由,就将当前的Request请求对象以及url地址和是否完全匹配路由项传入进去
$result = $this->cross->check($this->request, $url, $completeMatch);
}
//如果是域名路由
if (false !== $result) {
//直接返回 $result变量 变量内存储着 RuleGroup对象实例
//路由规则
return $result;
} elseif ($this->config['url_route_must']) {
//判断是否启用了强制路由,如果启用了强制路由
//然后域名路由也匹配不上。就触发一个路由
//找不到的异常类
throw new RouteNotFoundException();
}
//以上都不匹配,则调用url函数,传入当前的url地址
//返回一个Url类实例
return $this->url($url);
}
```
### 10、\\Think\\Route->url()
```
public function url(string $url): UrlDispatch
{
return new UrlDispatch($this->request, $this->group, $url);
}
```
### 11、\\Think\\Route\\dispatch\\Url url切割类(自己给的称呼)
```
//构造函数
public function __construct(Request $request, Rule $rule, $dispatch, array $param = [], int $code = null)
{
//获取传入来的Request对象,存储到类成员变量内
$this->request = $request;
//获取到路由列表,也放到类成员变量内
$this->rule = $rule;
// 调用类中的parseUrl方法 解析URL规则
$dispatch = $this->parseUrl($dispatch);
//调用父类构造函数
parent::__construct($request, $rule, $dispatch, $this->param, $code);
}
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)) {
//切割url,换成配置项中的PATH_INFO分隔符
$bind = str_replace('/', $depr, $bind);
// 如果有域名绑定
$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);
}
//获取到第二个下标 方法 function
// 解析操作
$action = !empty($path) ? array_shift($path) : null;
$var = [];
// 解析额外参数
//类似于 /index.php/Index/Users/Pascc
//这样就会返回一个 三个下标的数组
if ($path) {
//这里将多余的下标,放到var变量内
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;
```
### 12、\\Think\\Route\\dispatch\\controller->init() 绑定控制器和方法
```
public function init(App $app)
{
parent::init($app);
$result = $this->dispatch;
if (is_string($result)) {
$result = explode('/', $result);
}
// 获取控制器名
$controller = strip_tags($result[0] ?: $this->rule->config('default_controller'));
if (strpos($controller, '.')) {
$pos = strrpos($controller, '.');
$this->controller = substr($controller, 0, $pos) . '.' . Str::studly(substr($controller, $pos + 1));
} else {
$this->controller = Str::studly($controller);
}
// 获取操作名
$this->actionName = strip_tags($result[1] ?: $this->rule->config('default_action'));
// 设置当前请求的控制器、操作
$this->request
->setController($this->controller)
->setAction($this->actionName);
}
```
### 13、接下来流程就是回到第5步
最终会返回一个Response对象。流程又回到了第2步过程
### 14、然后会在入口文件中
```
namespace think;
//引入composer
require __DIR__ . '/../vendor/autoload.php';
$http = (new App())->http;
$response = $http->run();
//执行返回的Response对象中的send方法
//执行response对象中的send类方法 该方法是处理并输出http状态码以及页面内容
$response->send();
//执行Http对象中的send方法
$http->end($response);
//最终输出到页面上我
```
- 空白目录
- php语法结构
- 安装与更新
- 开启调试模式及代码跟踪器
- 架构
- 源码分析
- 应用初始化
- 请求流程
- 中间件源码分析
- 请求处理源码分析
- Request源码分析
- 模板编译流程
- 路由与请求流程
- 容器
- 获取目录位置
- 入口文件
- 多应用模式及URL访问
- 依赖注入与容器
- 容器属性及方法
- Container
- App
- facade
- 中间件(middleware)
- 系统服务
- extend 扩展类库
- 笔记
- 配置
- env配置定义及获取
- 配置文件的配置获取
- 单应用模式-(配置)文件目录结构(默认)
- 多应用模式(配置)文件目录结构(配置文件)
- 配置文件
- 应用配置:app.php
- 缓存配置: cache.php
- 数据库配置:database.php
- 路由和URL配置:route.php
- Cookie配置:cookie.php
- Session配置:session.php
- 命令行配置:console.php
- 多语言配置:lang.php
- 日志配置:log.php
- 页面Trace配置:trace.php
- 磁盘配置: filesystem.php
- 中间件配置:middleware.php
- 视图配置:view.php
- 改成用yaconf配置
- 事件
- 例子:省略事件类的demo
- 例子2:完整事件类
- 例子3:事件订阅,监听多个事件
- 解析
- 路由
- 路由定义
- 路由地址
- 变量规则
- MISS路由
- URL生成
- 闭包支持
- 路由参数
- 路由中间件
- 路由分组
- 资源路由
- 注解路由
- 路由绑定
- 域名路由
- 路由缓存
- 跨域路由
- 控制器
- 控制器定义
- 空控制器、空操作
- 空模块处理
- RESTFul资源控制器
- 控制器中间件
- 请求对象Request(url参数)
- 请求信息
- 获取输入变量($_POST、$_GET等)
- 请求类型的获取与伪装
- HTTP头信息
- 伪静态
- 参数绑定
- 请求缓存
- 响应对象Response
- 响应输出
- 响应参数
- 重定向
- 文件下载
- 错误页面的处理办法
- 应用公共文件common.php
- 模型
- 模型定义及常规属性
- 模型数据获取与模型赋值
- 查询
- 数据集
- 增加
- 修改
- 删除
- 条件
- 查询范围scope
- 获取器
- 修改器
- 搜索器
- 软删除
- 模型事件
- 关联预载入
- 模型关联
- 一对一关联
- 一对多关联
- 多对多关联
- 自动时间戳
- 事务
- 数据库
- 查询构造器
- 查询合集
- 子查询
- 聚合查询
- 时间查询
- 视图查询(比join简单)
- 获取查询参数
- 快捷方法
- 动态查询
- 条件查询
- 打印sql语句
- 增
- 删
- 改
- 查
- 链式操作
- 查询表达式
- 分页查询
- 原生查询
- JSON字段
- 链接数据库配置
- 分布式数据库
- 查询事件
- Db获取器
- 事务操作
- 存储过程
- Db数据集
- 数据库驱动
- 视图
- 模板
- 模板配置
- 模板位置
- 模板渲染
- 模板变量与赋值(assign)
- 模板输出替换
- url生成
- 模板详解
- 内置标签
- 三元运算
- 变量输出
- 函数输出
- Request请求参数
- 模板注释及原样输出
- 模板继承
- 模板布局
- 原生PHP
- 模板引擎
- 视图过滤
- 视图驱动
- 验证
- 验证进阶之最终版
- 错误和日志
- 异常处理
- 日志处理
- 调试
- 调试模式
- Trace调试
- SQL调试
- 变量调试
- 远程调试
- 杂项
- 缓存
- Session
- Cookie
- 多语言
- 上传
- 扩展说明
- N+1查询
- TP类库
- 扩展类库
- 数据库迁移工具
- Workerman
- think助手工具库
- 验证码
- Swoole
- request
- app
- Response
- View
- Validate
- Config
- 命令行
- 助手函数
- 升级指导(功能的添加与删除说明)
- siyucms
- 开始
- 添加页面流程
- 列表页加载流程
- 弹出框
- 基础控制器
- 基础模型
- 快速构建
- 表单form构建
- 表格table构建
- MakeBuilder
- 前端组件
- 日期组件
- layer 弹层组件
- Moment.js 日期处理插件
- siyucms模板布局
- 函数即其变量
- 前端页面
- $.operate.方法
- $.modal.方法:弹出层
- $.common.方法:通用方法
- 被cms重写的表格options
- 自定义模板
- 搜索框
- 自定义form表单
- 获取表单搜索参数并组装为url字符串