[TOC]
# 說明
接上一篇,得到`Http`類的一個實例后,程序接下來執行`$response = $http->run();`。其中`run()`方法代碼如下:
```
public function run(Request $request = null): Response
{
//自動創建request對象
$request = $request ?? $this->app->make('request', [], true);
// 將Request類的實例保存到「$instances」數組
$this->app->instance('request', $request);
try {
$response = $this->runWithRequest($request);
} catch (Throwable $e) {
$this->reportException($e);
$response = $this->renderException($request, $e);
}
return $response;
}
```
# 從「request」標識找到要實例化的類
`run()`方法的第一行通過容器類實例`app`調用`make()`方法并傳入`Request`類的標識來實例化`Request`類。具體過程如下分析。
通過`make()`方法首先解析得到`request`標識對應的標識`think\Request`, 進一步遞歸解析,又得到`app\Request`類——這個才是最終要實例化的類。
`app\Request`類對應的文件位于`app`目錄下,代碼如下:
```
namespace app;
class Request extends \think\Request
{
}
```
實際上它啥事也沒干,直接繼承系統的`\think\Request`。當然,如有需要,我們也可以在這里對系統的`Request`類進行改寫重構。
# 調用invokeClass()方法實例化Request類的過程分析
從類的標識解析得到最終需要實例化的類(單例模式下,且該類還不存在實例)之后,程序調用invokeClass()方法,通過PHP的反射類實現類的實例化。由于`\think\Request`類存在`__make()`方法,所以實例化之前首先調用該方法。`__make()`方法代碼如下:
```
public static function __make(App $app)
{
//實例化自身
$request = new static();
// 保存超全局變量$_SERVER
// 參考https://www.php.net/manual/zh/reserved.variables.server.php
$request->server = $_SERVER;
// 跟前面的Http的實例化原理一樣,實例化Env類并保存
$request->env = $app->env;
$request->get = $_GET;
$request->post = $_POST ?: $request->getInputData($request->input);
$request->put = $request->getInputData($request->input);
$request->request = $_REQUEST;
$request->cookie = $_COOKIE;
$request->file = $_FILES ?? [];
// 如果存在方法apache_request_headers則執行之
// apache_request_headers的作用是獲取所有HTTP請求頭
if (function_exists('apache_request_headers') && $result = apache_request_headers()) {
$header = $result;
} else {
$header = [];
$server = $_SERVER;
foreach ($server as $key => $val) {
if (0 === strpos($key, 'HTTP_')) {
$key = str_replace('_', '-', strtolower(substr($key, 5)));
$header[$key] = $val;
}
}
if (isset($server['CONTENT_TYPE'])) {
$header['content-type'] = $server['CONTENT_TYPE'];
}
if (isset($server['CONTENT_LENGTH'])) {
$header['content-length'] = $server['CONTENT_LENGTH'];
}
}
//將數組的中所有KEY轉為小寫
$request->header = array_change_key_case($header);
//__make()方法最終返回Request類的實例
return $request;
}
```
`__make()`方法首先實例化`think\Request`類自身。`think\Request`類構造函數如下:
```
public function __construct()
{
// 保存 php://input
//參考資料:http://www.nowamagic.net/academy/detail/12220520
// php://input 用于讀取POST數據
//(可用于Coentent-Type取值為application/x-www-data-urlencoded、text/json、text/xml,
// 不能用于multipart/form-data類型)
//用$_POST的話,僅在Coentent-Type取值為application/x-www-data-urlencoded
// 和multipart/form-data兩種情況下有用
$this->input = file_get_contents('php://input');
}
```
構造函數讀取了`php://input`保存起來。接著,`__make()`方法保存了一些請求相關的數據,最后返回一個`Request`類實例。最后的最后, `make()`方法也成功得到該實例,整個過程跟`Http`類的實例化類似。該`Request`類實例部分成員變量如圖:

# 保存「Request」類的實例到「$instances」數組
得到`Request`類的實例后,`run()`方法接著將該實例保存到「$instance」數組,以便后面單例模式要用到時可以直接獲取。`$instances`數組的值如圖,`Request`類的實例已保存在里面:
