Open xiaohuilam opened 6 years ago
Laravel 的管道机制,是其中间件 (middleware) 得以被序列执行的基础
在开始之前我们先帖一段管理的应用代码,位于 02. HTTP Kernel Handle解析 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L148-L151
语义化得出的分析为 携带 send 请求对象,经过中间件的处理,然后进入路由。
send
send 携带方法 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L53-L64 好像没啥出奇的,就是传入并储存一个对象,返回 $this
$this
through 经过方法 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L66-L77 好像也没啥关键的实现,传入“被管道”的集合,返回 $this
then 异步方法 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L92-L105
之前的都只是做登记一下就GG的作用,这个 then 才是根本大法, 使用 array_reduce 处理得到 $pipeline。这个 array_reduce 是何方神圣呢?我们查文档得到: Iteratively reduce the array to a single value using a callback function(中文意思就是 使用回调函数,迭代地将数组减少为单个值)。
then
array_reduce
$pipeline
但在走到 array_reduce 之前,调用了 carry 方法 (在 5.3 及以前版本,carry 方法其实为 getSlice 方法,而过程几乎一样 来源),其内部逻辑为:
getSlice
https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L120-L159
Laravel 的这个 carry 方法返回的是一个闭包,执行返回的闭包所返回的又是一个闭包,只不过前一个返回是 array_reduce 批量执行时拿到的,后面是处理原始对象(请求)时,处理中间件时候拿到的。
Laravel
$stack
$pipe
具体 handle 代码我们举一个例子: https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php#L58-L83
其中的 $next($request) 表示执行下一个中间件, $next 指的就是下面这个闭包 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L128-L157
$next($request)
关于中间件执行的流程,是不是有眉目了?
等上面执行完成,Laravel 就要执行这个闭包了 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L166-L178
也就是执行路由的 dispatch 逻辑(包含被调用的逻辑一共 82 行代码)。 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L601-L682
你可能会因为这里又有中间件而诧异 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L674-L681 其实这里的中间件和前面的不一样,前面的是在kernel 中注册的中间件。这里的中间件是在路由绑定中注册的中间件。是两部分。
于是乎,Laravel 鬼使神差的实现了中间件的管道执行机制。
接下来,请查看 09. 容器的依赖注入机制 了解 $route->run() 背后的故事。
$route->run()
管道机制
在开始之前我们先帖一段管理的应用代码,位于 02. HTTP Kernel Handle解析 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L148-L151
语义化得出的分析为 携带
send
请求对象,经过中间件的处理,然后进入路由。代码解析
send 携带方法 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L53-L64 好像没啥出奇的,就是传入并储存一个对象,返回
$this
through 经过方法 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L66-L77 好像也没啥关键的实现,传入“被管道”的集合,返回
$this
then 异步方法 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L92-L105
之前的都只是做登记一下就GG的作用,这个
then
才是根本大法, 使用array_reduce
处理得到$pipeline
。这个array_reduce
是何方神圣呢?我们查文档得到: Iteratively reduce the array to a single value using a callback function(中文意思就是 使用回调函数,迭代地将数组减少为单个值)。但在走到 array_reduce 之前,调用了 carry 方法 (在 5.3 及以前版本,carry 方法其实为
getSlice
方法,而过程几乎一样 来源),其内部逻辑为:https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L120-L159
Laravel
的这个 carry 方法返回的是一个闭包,执行返回的闭包所返回的又是一个闭包,只不过前一个返回是 array_reduce 批量执行时拿到的,后面是处理原始对象(请求)时,处理中间件时候拿到的。$stack
为 null 或者闭包对象,第二个参数$pipe
为从1开始的调用顺序。具体 handle 代码我们举一个例子: https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php#L58-L83
其中的
$next($request)
表示执行下一个中间件, $next 指的就是下面这个闭包 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L128-L157关于中间件执行的流程,是不是有眉目了?
等上面执行完成,Laravel 就要执行这个闭包了 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php#L166-L178
也就是执行路由的 dispatch 逻辑(包含被调用的逻辑一共 82 行代码)。 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L601-L682
你可能会因为这里又有中间件而诧异 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L674-L681 其实这里的中间件和前面的不一样,前面的是在kernel 中注册的中间件。这里的中间件是在路由绑定中注册的中间件。是两部分。
于是乎,Laravel 鬼使神差的实现了中间件的管道执行机制。