xiaohuilam / laravel

Laravel 深入详解 —— 源代码解析,新手进阶指南
433 stars 80 forks source link

05. Pipeline 和 Middleware #5

Open xiaohuilam opened 6 years ago

xiaohuilam commented 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 请求对象,经过中间件的处理,然后进入路由。

代码解析

  1. send 携带方法 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L53-L64 好像没啥出奇的,就是传入并储存一个对象,返回 $this

  2. through 经过方法 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php#L66-L77 好像也没啥关键的实现,传入“被管道”的集合,返回 $this

  3. 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 批量执行时拿到的,后面是处理原始对象(请求)时,处理中间件时候拿到的。

具体 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

关于中间件执行的流程,是不是有眉目了? image

等上面执行完成,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() 背后的故事。