ThinkPHP6 核心分析(九):发送响应和收尾工作

说明

2019-10-31 更新到6.0正式版

前面所有分析,都是从$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
{
        $this->app->event->trigger(HttpEnd::class, $response);

        //执行中间件
        // 由此可以看出,可以在中间件添加end方法,在程序结束时执行
        $this->app->middleware->end($response);

        // 写入日志
        $this->app->log->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

讨论数量: 1

:kissing_heart:

3个月前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!