什么是装璜器模式
装璜器模式容许向一个现有的对象增加新的性能,同时又不扭转其构造。
简略代码实现:
<code class="php">interface Decorate { function getInfo(); } /** * Class DecoateA * 初始化一个装璜对象 */ class DecoateA implements Decorate{ /** * 实现接口 */ public function getInfo() { echo __CLASS__.PHP_EOL; } } /** * Class DecoateB * 装璜DecoateA 的装璜类 */ class DecoateB implements Decorate{ /** * DecoateB constructor. * @param Decorate $dec * 结构器 传过来其余装璜类 */ public function __construct(Decorate $dec) { $this->dec = $dec; } /** * 实现接口 */ public function getInfo() { echo __CLASS__.PHP_EOL; $this->dec->getInfo(); } } // 通过一直的创立装璜对象来增加性能 $obj = new DecoateA();// 初始一个对象 $obj = new DecoateB($obj);// 装璜对象 $obj->getInfo(); // DecoateB // DecoateA
装璜器和Laravel中间件
装璜器对现有对象一直增加性能的思维,与框架中间件的思维统一,框架中间件就是对装璜器模式的一大利用。
一个申请能够视为现有对象,在申请达到控制器之前,须要通过 Laravel 框架的各种中间件,每个中间件都对申请进行不同的“装璜”,当申请达到控制器执行结束进行响应客户端,一个响应返回途中要通过刚刚来时的通过的中间件,每个中间件都能够对响应进行不同的“装璜”,最初响应达到客户端。这个流程是一个典型的“洋葱模型”。
以一个简短的代码表白 Laravel 中间件原理
<code class="php">interface Middleware { public static function handle(Closure $next); } /** * 中间件A(能够视为装璜类A) */ class MiddlewareA implements Middleware { public static function handle(Closure $next) { echo __CLASS__." start".PHP_EOL; $next(); echo __CLASS__." end".PHP_EOL; } } /** * 中间件B(能够视为装璜类B) */ class MiddlewareB implements Middleware { public static function handle(Closure $next) { echo __CLASS__." start".PHP_EOL; $next(); echo __CLASS__." end".PHP_EOL; } } /** * 生成闭包函数 */ function makeClosureFun($carry, $middlerwareClass) { return function () use ($carry, $middlerwareClass) { return $middlerwareClass::handle($carry); }; } /** * 执行 */ // Kernel 中的中间件数组 $middlerwareArray = [ 'MiddlewareA', 'MiddlewareB' ]; $prepare = function () { echo "_init_".PHP_EOL; }; // 组装闭包 $closure = array_reduce($middlerwareArray, 'makeClosureFun', $prepare); // 执行 call_user_func($closure);
后果:
MiddlewareB start MiddlewareA start _init_ MiddlewareA end MiddlewareB end
以上代码输入的后果,正好合乎中间件的“洋葱模型”。
代码解析
这是如何是实现的呢?实现中间件的要害是 array_reduce() + 闭包函数。
array_reduce() 第一个参数是数组,第二个参数是回调函数,第三个参数是解决开始或完结时触发的函数。
array_reduce 会给回调函数传入两个参数,carry 和 item ,别离是上次迭代返回的值和本次迭代的值。
组装闭包
当组装闭包时,部分代码如下
<code class="php">function makeClosureFun($carry, $middlerwareClass) { return function () use ($carry, $middlerwareClass) { // 疏忽视为黑盒 }; } // 组装闭包 $closure = array_reduce($middlerwareArray, 'makeClosureFun', $prepare);
makeClosureFun() 返回了一个 use 两个变量的闭包函数,至于这个闭包函数外部具体做了什么不必关注,将其视为一个黑盒。闭包函数 use 的 这两个参数别离是 array_reduce() 传给闭包的上次迭代返回的值和本次要迭代的值。
当第一次迭代执行 makeClosureFun() 时,返回了一个闭包函数,这个闭包函数存储时实际上时一个闭包类,能够看到 use 的两个参数存到 static 属性下。
<code class="php">object(Closure)#2 (1) { ["static"]=> array(2) { ["carry"]=> object(Closure)#1 (0) { } ["middlerwareClass"]=> string(11) "MiddlewareA" } }
第一个迭代返回的闭包函数,持续作为了第二次迭代的 $carry
传入, $middlerwareClass
变为了 $middlerwareArray
数组的第二个元素。如此迭代,到最初失去的 $closure
。
object(Closure)#3 (1) { ["static"]=> array(2) { ["carry"]=> object(Closure)#2 (1) { ["static"]=> array(2) { ["carry"]=> object(Closure)#1 (0) { } ["middlerwareClass"]=> string(11) "MiddlewareA" } } ["middlerwareClass"]=> string(11) "MiddlewareB" } }
执行闭包
应用 call_user_func() 执行 $closure 变量中的闭包函数。
TODO: PHP 如何依据闭包变量中的构造找到对应函数代码片段?(OR call_user_func如何执行闭包函数?)
第一次执行 use 引入 $carry
和 $middlerwareClass
, 执行 $middlerwareClass::handle($carry)
,此时 $middlerwareClass
值为 MiddlewareB,即执行 MiddlewareB::handle($carry)
,传入的 $carry
值是一个闭包。MiddlewareB类中 handle() 办法在先执行 MiddlewareB start
输入,通过 $next()
执行闭包,即第二次执行。
这次执行 $middlerwareClass::handle($carry)
,此时 $middlerwareClass
值为 MiddlewareA,即执行 MiddlewareA::handle($carry)
,输入 MiddlewareA start
, 执行闭包 $next()
,PHP发现闭包为空,触发 array_reduce() 给设定的 $prepare
闭包,输入 _init_
,随后开始回头,输入 MiddlewareA end
,再输入MiddlewareB end
。
参考文章
次要参考,特地鸣谢此文作者
装璜器模式以及Laravel框架下的中间件利用
延长浏览
Pipeline 管道操作实现申请中间件过滤(最具体解说)