run()方法
接上一篇,得到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->setCookie($this->app->cookie);
}
從「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()方法
從類的標識解析得到最終需要例項化的類(單例模式下,且該類還不存在例項)之後,程式呼叫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
類的例項已儲存在裡面: