1. 入口文件先實例化容器,然后再通過容器去獲取到Http對象 (Web管理類),然后執行Http對象中的run方法。
2. 方法內會創建一個Request對象,然后將對象綁定到容器內。然后再到runWithRequest方法,執行應用程序
3. runWithRequest方法內會初始化當前應用,簡單來說就比如加載一下語言包,加載一下應用文件。common.php公共函數文件。helper.php助手函數文件、.env環境變量、運行開始的時間、設置時區、加載中間件等等。然后到dispatchToRoute方法,傳入當前的Request請求對象。
4. dispatchToRoute方法,這里我姑且稱為路由初始化方法。這里主要就是檢測配置文件,是否開啟了路由。如果開啟了路由。就加載路由文件。并且設置一個匿名函數。只有在調用的時候才會加載設置的路由。接著會通過容器獲取route類的實例。并且傳入當前Request對象和路由配置的匿名函數并執行里面的dispatch方法
5. dispatch方法,主要就是路由初始化。判斷路由是否配置。如果沒有配置就直接執行默認控制器和默認方法。如果有的話。就加載一下路由配置,再執行check方法,通過check方法。去檢測是什么路由。然后調用url方法傳入當前的url地址
6. url方法執行并且會返回一個Url基礎類
7. 這里我稱之為url且切割類吧。他主要的作用就是將傳入的Request對象,rule路由規則對象,以及當前的url地址。把url地址解析。這里又去調用了一個parseUrl方法。
8. 這個方法我就不多介紹了。它主要的作用就是分割出來要執行的control和function。
9. 到這里就結束了url的部分。又回到了第五部。去調用url類中的init方法(路由后置操作。中間件).然后通過middleware中的then。到最后執行一個上一步返回的Url類中的run方法
10. run這里主要就是最通過exec再去獲取控制器(controller)和對應的方法(function)的結果。然后創建一個Response對象。最后返回
11. 最后回到了入口文件run方法下面還有一個send方法。源碼就不貼了。他的作用就是輸出
12. 入口文件最后一行。調用了一下Http類的end方法。簡單說就是掛個HttpEnd中間件。然后執行中間件。最后再記錄一下日志。
## 0x01 源碼解析
### 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``);`
`//最終輸出到頁面上我`
```
- thinkphp6執行流程(一)
- php中use關鍵字用法詳解
- Thinkphp6使用騰訊云發送短信步驟
- 路由配置
- Thinkphp6,static靜態資源訪問路徑問題
- ThinkPHP6.0+ 使用Redis 原始用法
- smarty在thinkphp6.0中的最佳實踐
- Thinkphp6.0 搜索器使用方法
- 從已有安裝包(vendor)恢復 composer.json
- tp6with的用法,表間關聯查詢
- thinkphp6.x多對多如何添加中間表限制條件
- thinkphp6 安裝JWT
- 緩存類型
- 請求信息和HTTP頭信息
- 模型事件用法
- 助手函數匯總
- tp6集成Alipay 手機和電腦端支付的方法
- thinkphp6使用jwt
- 6.0session cookie cache
- tp6筆記
- TP6(thinkphp6)隊列與延時隊列
- thinkphp6 command(自定義指令)
- command(自定義指令)
- 本地文件上傳
- 緩存
- 響應
- 公共函數配置
- 七牛云+文件上傳
- thinkphp6:訪問多個redis數據源(thinkphp6.0.5 / php 7.4.9)
- 富文本編輯器wangEditor3
- IP黑名單
- 增刪改查 +文件上傳
- workerman 定時器操作控制器的方法
- 上傳文件到阿里云oss
- 短信或者郵箱驗證碼防刷代碼
- thinkphp6:訪問redis6(thinkphp 6.0.9/php 8.0.14)
- 實現關聯多個id以逗號分開查詢數據
- thinkphp6實現郵箱注冊功能的細節和代碼(點擊鏈接激活方式)
- 用mpdf生成pdf文件(php 8.1.1 / thinkphp v6.0.10LTS )
- 生成帶logo的二維碼(php 8.1.1 / thinkphp v6.0.10LTS )
- mysql數據庫使用事務(php 8.1.1 / thinkphp v6.0.10LTS)
- 一,創建過濾IP的中間件
- 源碼解析請求流程
- 驗證碼生成
- 權限管理
- 自定義異常類
- 事件監聽event-listene
- 安裝與使用think-addons
- 事件與多應用
- Workerman 基本使用
- 查詢用戶列表按拼音字母排序
- 擴展包合集
- 查詢用戶數據,但是可以通過輸入用戶昵稱來搜索用戶同時還要統計用戶的文章和粉絲數
- 根據圖片的minetype類型獲取文件真實拓展名思路
- 到處excel
- 用imagemagick庫生成縮略圖
- 生成zip壓縮包并下載
- API 多版本控制
- 用redis+lua做限流(php 8.1.1 / thinkphp v6.0.10LTS )
- 【thinkphp6源碼分析三】 APP類之父, 容器Container類
- thinkphp6表單重復提交解決辦法
- 小程序授權
- 最簡單的thinkphp6導出Excel
- 根據訪問設備不同訪問不同模塊
- 服務系統
- 前置/后置中間件
- 給接口api做簽名驗證(php 8.1.1 / thinkphp v6.0.10LTS )
- 6實現郵箱注冊功能的細節和代碼(點擊鏈接激活方式)
- 使用前后端分離的驗證碼(thinkphp 6.0.9/php 8.0.14/vue 3.2.26)
- 前后端分離:用jwt+middleware做用戶登錄驗證(php 8.1.1 / thinkphp v6.0.10LTS )
- vue前后端分離多圖上傳
- thinkphp 分組、頁面跳轉與ajax
- thinkphp6 常用方法文檔
- 手冊里沒有的一些用法
- Swagger 3 API 注釋
- PHP 秒級定時任務
- thinkphp6集成gatewayWorker(workerman)實現實時監聽
- thinkphp6按月新增數據表
- 使用redis 實現消息隊列
- api接口 統一結果返回處理類
- 使用swoole+thinkphp6.0+redis 結合開發的登錄模塊
- 給接口api做簽名驗證
- ThinkPHP6.0 + UniApp 實現小程序的 微信登錄
- ThinkPHP6.0 + Vue + ElementUI + axios 的環境安裝到實現 CURD 操作!
- 異常$e
- 參數請求驗證自定義和異常錯誤自定義