Open xiaohuilam opened 5 years ago
在前面 01. HTTP 入口解析 的第 54-56 行代码分析时 https://github.com/xiaohuilam/laravel/blob/7028b17ed8bf35ee2f1269c0f9c985b411cb4469/public/index.php#L54-L56
我们只分析了 $request 的产生,却没有分析 $response 。 这节,我们就来研究下 Laravel 的 Response 对象: Illuminate\Http\Response。
$request
$response
Illuminate\Http\Response
不难看出,这句代码拿到的一定是个对象。为什么? 因为在下文,就调用了他的 ->send() 方法。 https://github.com/xiaohuilam/laravel/blob/7028b17ed8bf35ee2f1269c0f9c985b411cb4469/public/index.php#L58
->send()
那么 Illuminate\Http\Response 对象是从哪里来的呢?
在从 入口 到 Kernel::handle() 到 管道 的过程尾声,我们拿到了这句逻辑
Kernel::handle()
https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L674-L681 的 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L678-L680
这个 prepareResponse 是干嘛的呢? https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L717-L720
prepareResponse
调用了 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L729-L755
过程为:
PsrResponseInterface
statusCoe
HTTP 201 created
JsonResponse
Response
于是乎,我们才可以在 controller 中大胆的直接返回字符串而转换成 Response 对象。因为在这里他帮我们转了。
经过上面这一波加工后,在 public/index.php 里面才敢执行 $response->send()
$response->send()
https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/symfony/http-foundation/Response.php#L366-L378
调用的 sendContent 逻辑为 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/symfony/http-foundation/Response.php#L354-L359 其中的 echo 代码清晰可见,非常简单。
sendContent
echo
先于 sendContent 的 sendHeaders 的代码要稍微复杂一点 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/symfony/http-foundation/Response.php#L324-L347
sendHeaders
先是将 http 响应头依次输出,然后将 Cookie 处理后输出。
在 send 方法的后面,还有几句 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/symfony/http-foundation/Response.php#L371-L375 作用是判断是否是 fastcgi 或者 cli 模式调用,如果是就关闭连接、刷新缓冲区。
send
fastcgi
cli
至此,send 逻辑就讲完了。
App\Http\Kernel 并没有 terminate 方法,一路穿透到了 Illuminate\Foundation\Http\Kernel 里面 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L187-L192
App\Http\Kernel
terminate
Illuminate\Foundation\Http\Kernel
Illuminate\Foundation\Http\Kernel::terminateMiddleware() 逻辑 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L201-L221 将容器中 bind 过的中间件 terminate() 掉。
Illuminate\Foundation\Http\Kernel::terminateMiddleware()
bind
terminate()
而这句就比较好理解了,直接调用了容器的 terminate() https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L191
容器的 terminate() 方法也比较简单,触发 $terminatingCallbacks 注册的回调。 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Application.php#L962-L967
$terminatingCallbacks
PHP的不常驻特性,一个请求完了就自动回收资源,那么除了需要触发 $terminatingCallbacks 回调外,为什么还要实现 terminate() 呢?
Laravel 为我们提供了一个 php artisan serve 的便捷服务器,启动后 Application 对象是常驻的。所以需要单独回收需要回收的资源。
php artisan serve
Application
php artisan serve 的本质也是 php -S,详细资料 PHP 的命令行模式>内置Web Server。 扩展阅读:在 Laravel 5.3 以前,Kernel::terminate() 还调用了 fastcgi_finish_request()。后来移除了,主要原因是因为这个函数会阻断 http 响应的头和 body,造成部分特殊情况的 session 问题,不过也可能还有其他原因。
php artisan serve 的本质也是 php -S,详细资料 PHP 的命令行模式>内置Web Server。
php -S
扩展阅读:在 Laravel 5.3 以前,Kernel::terminate() 还调用了 fastcgi_finish_request()。后来移除了,主要原因是因为这个函数会阻断 http 响应的头和 body,造成部分特殊情况的 session 问题,不过也可能还有其他原因。
Kernel::terminate()
fastcgi_finish_request()
在前面 01. HTTP 入口解析 的第 54-56 行代码分析时 https://github.com/xiaohuilam/laravel/blob/7028b17ed8bf35ee2f1269c0f9c985b411cb4469/public/index.php#L54-L56
我们只分析了
$request
的产生,却没有分析$response
。 这节,我们就来研究下 Laravel 的 Response 对象:Illuminate\Http\Response
。Illuminate\Http\Response 初识
不难看出,这句代码拿到的一定是个对象。为什么? 因为在下文,就调用了他的
->send()
方法。 https://github.com/xiaohuilam/laravel/blob/7028b17ed8bf35ee2f1269c0f9c985b411cb4469/public/index.php#L58那么
Illuminate\Http\Response
对象是从哪里来的呢?在从 入口 到
Kernel::handle()
到 管道 的过程尾声,我们拿到了这句逻辑https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L674-L681 的 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L678-L680
这个
prepareResponse
是干嘛的呢? https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L717-L720调用了 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L729-L755
过程为:
PsrResponseInterface
对象;statusCoe
强制覆盖成HTTP 201 created
;JsonResponse
;Response
;经过上面这一波加工后,在 public/index.php 里面才敢执行
$response->send()
Illuminate\Http\Response::send() 的逻辑
https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/symfony/http-foundation/Response.php#L366-L378
调用的
sendContent
逻辑为 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/symfony/http-foundation/Response.php#L354-L359 其中的echo
代码清晰可见,非常简单。先于
sendContent
的sendHeaders
的代码要稍微复杂一点 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/symfony/http-foundation/Response.php#L324-L347先是将 http 响应头依次输出,然后将 Cookie 处理后输出。
在
send
方法的后面,还有几句 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/symfony/http-foundation/Response.php#L371-L375 作用是判断是否是fastcgi
或者cli
模式调用,如果是就关闭连接、刷新缓冲区。至此,
send
逻辑就讲完了。Kernel::terminate() 的逻辑
App\Http\Kernel
并没有terminate
方法,一路穿透到了Illuminate\Foundation\Http\Kernel
里面 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L187-L192Illuminate\Foundation\Http\Kernel::terminateMiddleware()
逻辑 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L201-L221 将容器中bind
过的中间件terminate()
掉。而这句就比较好理解了,直接调用了容器的
terminate()
https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L191容器的
terminate()
方法也比较简单,触发$terminatingCallbacks
注册的回调。 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Application.php#L962-L967PHP的不常驻特性,一个请求完了就自动回收资源,那么除了需要触发
$terminatingCallbacks
回调外,为什么还要实现terminate()
呢?Laravel 为我们提供了一个
php artisan serve
的便捷服务器,启动后Application
对象是常驻的。所以需要单独回收需要回收的资源。