ThinkPHP6 原始碼閱讀(二):Request 類是如何例項化的

tsin 發表於 2019-08-11
PHP

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\App, 進一步遞迴解析,又得到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類例項部分成員變數如圖:

ThinkPHP6 原始碼閱讀(二):Request類是如何例項化的

儲存「Request」類的例項到「$instance」陣列

得到Request類的例項後,run()方法接著將該例項儲存到「$instance」陣列,以便後面單例模式要用到時可以直接獲取。$instances陣列的值如圖,Request類的例項已儲存在裡面:

ThinkPHP6 原始碼閱讀(二):Request類是如何例項化的

Was mich nicht umbringt, macht mich stärker


相關文章

PHP 三大經典模式初探 PHP

PHP 三大經典模式初探

單例模式單例模式的含義:作為物件的建立模式,單例模式確保某一個類只有一個例項,而且自行例項化並向整個系統全域性地提供這個例項。它不會建立例項副本,而是會向單例類內部儲存的例項返回一個引用。單例模式的三
Mac 搭建 PHP 開發環境 PHP

Mac 搭建 PHP 開發環境

分享背景由於一直在虛擬機器的狀態下開發PHP,嘗試一下mac本地搭建環境.mac本身是自帶Apache+php的,在低版本的mac系統中,mac中的php版本是5.6的版本.本文分享的是在mac的10
ThinkPHP5 使用 Laravel 的建立軟連結命令  storage:link Laravel|PHP

ThinkPHP5 使用 Laravel 的建立軟連結命令 storage:link

V5.1.24+版本開始,你可以通過命令列指令快速生成一條指令,包括指令類檔案1.建立一個自定義命令類檔案php think make:command StorageLinkCommand stora
[深圳][11K-18K] 柏斯速眠招聘  PHP 開發工程師  1 名 PHP

[深圳][11K-18K] 柏斯速眠招聘 PHP 開發工程師 1 名

崗位職責1、與產品、設計密切溝通協作,負責後臺業務功能開發;2、擅長分析解決疑難問題,負責後端穩定性與效能優化;3、參與團隊內部技術分享與方案討論。任職要求1、2 - 4年 PHP 開發經驗;2、精通
PHP 基礎 PHP

PHP 基礎

註釋:// 多行註釋.// 單行註釋.變數命規範(1))以$開頭; 如:$a(2)由字母數字下劃線組成,但是不能以數字開頭;(3)可以使用中文,但是不推薦使用;(4)變數名嚴格區分大小寫;(5)
PHP 反射之動態代理 PHP

PHP 反射之動態代理

反射可以探知類的內部結構 可以用它做hook實現外掛功能,或者做動態代理與反射相關類和物件相關的函式get_object_varsget_class_methodsget_class_varsget
phpStudy 後門如何檢測和修復 PHP

phpStudy 後門如何檢測和修復

原文:http://soft.antted.com/news/8背景一篇《Phpstudy官網於2016年被入侵,犯罪分子篡改軟體並植入後門》讓人觸目驚心,從官網的下載官方安裝包也會有問題,由此可想而
🚀 Hyperf 釋出 v1.0.16 版本 | 企業級的 PHP 微服務協程框架 框架|PHP|微服務

🚀 Hyperf 釋出 v1.0.16 版本 | 企業級的 PHP 微服務協程框架

本週更新主要新增了 協程執行控制 的功能,通過 Hyperf\Utils\Coroutine\Concurrent 可以實現一個程式碼塊內限制同時最多執行的協程數量的功能 ,同時還修復了一些元件的 B
laragon 簡單好用的 PHP 環境 PHP

laragon 簡單好用的 PHP 環境

laragon簡單好用的PHP環境,一個資料夾搞定一切。以前是Mac的開發環境,用homebrew管理一切,也用過homestead環境,在mac上,兩者都不錯。Windows10開發,嘗試過home
JavaScript 和 PHP base64 加密解密 亂碼問題 JavaScript|PHP|加密

JavaScript 和 PHP base64 加密解密 亂碼問題

記錄以下base64加解密base64是通用的加解密方法,JavaScript也有現成的庫可以使用,PHP更是有現成函式可用。1 前端引入一個庫var html_source = base64.enc
PHP 多程式與訊號中斷實現多工常駐記憶體管理【Master/Worker 模型】 PHP

PHP 多程式與訊號中斷實現多工常駐記憶體管理【Master/Worker 模型】

本文章基於pcntl擴充套件做的多程式測試。程式排程策略:父子程式的排程由作業系統來負責,具體先排程子程式還是父程式由系統的排程演算法決定,當然可以在父程式加上延時或是呼叫程式回收函式pcntl_wa
求一個 phpstorm 的舒適護眼主題 PHP

求一個 phpstorm 的舒適護眼主題

求一個phpstorm的舒適護眼主題!!!