14. Response 对象和 Kernel::terminate
xiaohuilam opened this issue · comments
在前面 01. HTTP 入口解析 的第 54-56 行代码分析时
Lines 54 to 56 in 7028b17
我们只分析了 $request
的产生,却没有分析 $response
。
这节,我们就来研究下 Laravel 的 Response 对象: Illuminate\Http\Response
。
Illuminate\Http\Response 初识
不难看出,这句代码拿到的一定是个对象。为什么?
因为在下文,就调用了他的 ->send()
方法。
Line 58 in 7028b17
那么 Illuminate\Http\Response
对象是从哪里来的呢?
在从 入口 到 Kernel::handle()
到 管道 的过程尾声,我们拿到了这句逻辑
laravel/vendor/laravel/framework/src/Illuminate/Routing/Router.php
Lines 674 to 681 in d081c91
laravel/vendor/laravel/framework/src/Illuminate/Routing/Router.php
Lines 678 to 680 in d081c91
这个 prepareResponse
是干嘛的呢?
laravel/vendor/laravel/framework/src/Illuminate/Routing/Router.php
Lines 717 to 720 in d081c91
调用了
laravel/vendor/laravel/framework/src/Illuminate/Routing/Router.php
Lines 729 to 755 in d081c91
过程为:
- 判断是否为
PsrResponseInterface
对象; - 则判断为模型,且刚创建,将响应头
statusCoe
强制覆盖成HTTP 201 created
; - 否则判断返回的对象是否具有序列号特征,如果是就重置成
JsonResponse
; - 否则的话覆盖成
Response
;
于是乎,我们才可以在 controller 中大胆的直接返回字符串而转换成
Response
对象。因为在这里他帮我们转了。
经过上面这一波加工后,在 public/index.php 里面才敢执行 $response->send()
Illuminate\Http\Response::send() 的逻辑
laravel/vendor/symfony/http-foundation/Response.php
Lines 366 to 378 in d081c91
调用的 sendContent
逻辑为
laravel/vendor/symfony/http-foundation/Response.php
Lines 354 to 359 in d081c91
echo
代码清晰可见,非常简单。
先于 sendContent
的 sendHeaders
的代码要稍微复杂一点
laravel/vendor/symfony/http-foundation/Response.php
Lines 324 to 347 in d081c91
先是将 http 响应头依次输出,然后将 Cookie 处理后输出。
在 send
方法的后面,还有几句
laravel/vendor/symfony/http-foundation/Response.php
Lines 371 to 375 in d081c91
fastcgi
或者 cli
模式调用,如果是就关闭连接、刷新缓冲区。
至此,send
逻辑就讲完了。
Kernel::terminate() 的逻辑
App\Http\Kernel
并没有 terminate
方法,一路穿透到了 Illuminate\Foundation\Http\Kernel
里面
laravel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
Lines 187 to 192 in d081c91
Illuminate\Foundation\Http\Kernel::terminateMiddleware()
逻辑
laravel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
Lines 201 to 221 in d081c91
将容器中
bind
过的中间件 terminate()
掉。
而这句就比较好理解了,直接调用了容器的 terminate()
容器的 terminate()
方法也比较简单,触发 $terminatingCallbacks
注册的回调。
laravel/vendor/laravel/framework/src/Illuminate/Foundation/Application.php
Lines 962 to 967 in d081c91
PHP的不常驻特性,一个请求完了就自动回收资源,那么除了需要触发 $terminatingCallbacks
回调外,为什么还要实现 terminate()
呢?
Laravel 为我们提供了一个 php artisan serve
的便捷服务器,启动后 Application
对象是常驻的。所以需要单独回收需要回收的资源。
php artisan serve
的本质也是php -S
,详细资料 PHP 的命令行模式>内置Web Server。
扩展阅读:在 Laravel 5.3 以前,
Kernel::terminate()
还调用了fastcgi_finish_request()
。后来移除了,主要原因是因为这个函数会阻断 http 响应的头和 body,造成部分特殊情况的 session 问题,不过也可能还有其他原因。