当前位置: 首页 > 后端技术 > PHP

装饰器模式在Laravel中的应用

时间:2023-03-29 19:38:13 PHP

什么是装饰器模式装饰器模式允许在不改变现有对象结构的情况下向现有对象添加新功能。简单代码实现:interfaceDecorate{functiongetInfo();}/***ClassDecoateA*初始化一个装饰对象*/classDecoateAimplementsDecorate{/***实现接口*/publicfunctiongetInfo(){echo__CLASS__.PHP_EOL;}}/***ClassDecoateB*Decoate类装饰DecoateA*/classDecoateBimplementsDecoate{/***DecoateB构造函数。*@paramDecorate$dec*构造函数传递其他装饰类*/publicfunction__construct(Decorate$dec){$this->dec=$dec;}/***实现接口*/publicfunctiongetInfo(){echo__CLASS__.PHP_EOL;$this->dec->getInfo();}}//通过连续创建一个装饰器对象来添加功能$obj=newDecoateA();//初始一个对象$obj=newDecoateB($obj);//装饰器对象$obj->getInfo();//DecoateB//DecoateA装饰器和Laravel中间件装饰器不断给现有对象添加功能的思想与框架中间件的思想是一致的。框架中间件是装饰者模式的一个很好的应用。可以将请求视为现有对象。请求到达控制器之前,需要经过Laravel框架的各种中间件。每个中间件以不同的方式“装饰”请求。当request到达controller并执行时,响应给client端,一个response需要经过刚刚返回途中经过的中间件。每个中间件都可以对响应进行不同的“装饰”,最终响应到达客户端。这个过程就是一个典型的“洋葱模型”。用简短的代码表达Laravel中间件原理interfaceMiddleware{publicstaticfunctionhandle(Closure$next);}/***MiddlewareA(可以看成是装饰类A)*/classMiddlewareAimplementsMiddleware{publicstaticfunctionhandle(Closure$next){echo__CLASS__."start".PHP_EOL;$下一步();echo__CLASS__."end".PHP_EOL;}}/***中间件B(可以看成是装饰类B)*/classMiddlewareBimplementsMiddleware{publicstaticfunctionhandle(Closure$next){echo__CLASS__."start".PHP_EOL;$下一步();echo__CLASS__."end".PHP_EOL;}}/***生成闭包函数*/functionmakeClosureFun($carry,$middlerwareClass){returnfunction()use($carry,$middlerwareClass){return$middlerwareClass::handle($carry);};}/***执行*///内核中的中间件数组$middlerwareArray=['MiddlewareA','MiddlewareB'];$prepare=function(){echo"_init_".PHP_EOL;};//程序集关闭$closure=array_reduce($middlerwareArray,'makeClosureFun',$prepare);//执行call_user_func($closure);结果:中间wareBstartMiddlewareAstart_init_MiddlewareAendMiddlewareBend上面代码输出的结果正好符合中间件的“洋葱模型”代码分析。这是如何实现的?实现中间件的关键是array_reduce()+闭包函数。array_reduce()第一个参数是数组,第二个参数是回调函数,第三个参数是处理开始或结束时触发的函数。array_reduce会给回调函数传入两个参数,carry和item,分别是上一次迭代返回的值和本次迭代的值。组装闭包组装闭包时,部分代码如下闭包$closure=array_reduce($middlerwareArray,'makeClosureFun',$prepare);makeClosureFun()返回一个使用两个变量的闭包函数。至于闭包函数内部做了什么,不用管它。它被视为一个黑盒子。闭包函数use的两个参数是array_reduce()传递给闭包的最后一次迭代返回的值和当前迭代的值。当第一次迭代执行makeClosureFun()时,返回一个闭包函数。这个闭包函数在存储的时候其实就是一个闭包类。可以看到use的两个参数都存放在static属性下。object(Closure)#2(1){["static"]=>array(2){["carry"]=>object(Closure)#1(0){}["middlerwareClass"]=>string(11)"MiddlewareA"}}第一次迭代返回的闭包函数作为第二次迭代的$carry继续传入,$middlerwareClass成为$middlerwareArray数组的第二个元素。以此方式迭代,直到得到最终的$closure。对象(闭包)#3(1){[“静态”]=>数组(2){[“携带”]=>对象(闭包)#2(1){[“静态”]=>数组(2){["carry"]=>object(Closure)#1(0){}["middlewareClass"]=>string(11)"MiddlewareA"}}["middlewareClass"]=>string(11)"MiddlewareB"}}执行闭包使用call_user_func()执行$closure变量中的闭包函数。TODO:PHP如何根据闭包变量中的结构找到对应的函数代码片段?(ORcall_user_func是如何执行闭包函数的?)use的第一次执行引入了$carry和$middlerwareClass,执行了$middlerwareClass::handle($carry)。此时$middlerwareClass的值为MiddlewareB,即执行MiddlewareB::handle($carry),传入的$carry值为闭包。MiddlewareB类中的handle()方法先执行MiddlewareB启动输出,通过$next()执行闭包,即第二次执行。这次执行$middlerwareClass::handle($carry)。此时$middlerwareClass的值为MiddlewareA,即执行MiddlewareA::handle($carry),输出MiddlewareAstart,执行闭包$next(),PHP发现闭包为空,触发array_reduce()到设置的$prepare闭包,输出_init_,然后开始往回走,输出MiddlewareA结束,再输出MiddlewareB结束。参考文章的主要参考,特别感谢本文作者Laravel框架下Decorator模式和中间件应用扩展阅读Pipeline流水线操作实现请求中间件过滤(最详解)