ThinkPHP6 原始碼閱讀(九):傳送響應和收尾工作

tsin發表於2019-08-19

說明

前面所有分析,都是從$response = $http->run();展開的,經歷了漫漫長路,run方法終於執行完畢,返回一個Response物件,程式又回到入口檔案:

.
.
.
$response = $http->run();

$response->send();

$http->end($response);

接下來是執行$response->send();send方法:

public function send(): void
{
    // 處理輸出資料
    $data = $this->getContent();
    // 如果還沒有傳送響應頭且$this->header不為空
    if (!headers_sent() && !empty($this->header)) {
        // 傳送狀態碼
        http_response_code($this->code);
        // 傳送頭部資訊
        foreach ($this->header as $name => $val) {
            header($name . (!is_null($val) ? ':' . $val : ''));
        }
    }
    // 儲存cookie
    $this->cookie->save();
    // 輸出資料
    $this->sendData($data);
    // 參考:http://www.laruence.com/2011/04/13/1991.html
    if (function_exists('fastcgi_finish_request')) {
        // 提高頁面響應
        fastcgi_finish_request();
    }
}

過程比較簡單:傳送狀態碼、傳送響應頭,然後傳送響應內容。

收尾工作

接著是執行:$http->end($response);,展開如下:

public function end(Response $response): void
{
    //觸發HttpEnd事件
    $this->app->event->trigger('HttpEnd', $response);

    // 寫入日誌
    $this->app->log->save();
    // 寫入Session
    $this->app->session->save();
}

以上程式碼執行完了之後,整個生命週期本該結束了,發現程式竟然還繼續執行think\initializer\Error類的appShutdown方法:

public function appShutdown(): void
{
    if (!is_null($error = error_get_last()) && $this->isFatal($error['type'])) {
        // 將錯誤資訊託管至think\ErrorException
        $exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']);

        $this->appException($exception);
    }
}

開始是想可能是解構函式呼叫了它,但也沒有到有解構函式呼叫。最後發現,原來前面應用初始化的時候,載入了think\initializer\Error類,並執行了init方法:

 public function init(App $app)
{
    $this->app = $app;
    //開啟所有級別錯誤提示
    error_reporting(E_ALL);
    //設定自定義的函式處理執行中的錯誤
    set_error_handler([$this, 'appError']);
    //設定預設的異常處理程式,用於沒有用 try/catch 塊來捕獲的異常
    set_exception_handler([$this, 'appException']);
    //註冊一個 callback ,它會在指令碼執行完成或者 exit() 後被呼叫。
    register_shutdown_function([$this, 'appShutdown']);
}

該方法最後一行註冊了一個回撥,它會在指令碼執行完成或者 exit() 後被呼叫。

生命週期小結

至此,一個精簡版的請求生命就結束了。一路下來,不停地單步執行、回放,像是程式碼在大腦裡一遍又一遍執行,還好堅持了下來。一個生命週期分析了一遍,還是有很大收穫的。後面計劃分析:事件機制、服務、Facade、模型、驗證器。

Was mich nicht umbringt, macht mich stärker

相關文章