[原博客地址](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字符串