很久没有写文章了。记录下这段时间的所见所闻吧。Laravel中间件是一个很方便的东西。可以解耦一些逻辑实现,在laravel中,middleware的写法也很方便。谁是谁知。1.装饰者模式laravel中的中间件使用的是装饰者模式。什么是装饰者模式,我们先去了解一下。就说到这里吧。这种模式主要用来解决当一个类需要动态扩展功能的时候。使用继承时,子类会扩展,如果扩展的函数是公共函数,不利于函数的复用和代码的解耦。在laravel中,使用这种模式的函数称为请求处理管道,即pipeline//publicinterfaceinterfacemiddleware{publicstaticfunctionhandle(Closure$next);}//decorator1classMiddleStepOneimplementsmiddleware{publicstaticfunctionhandle(Closure$next){echo"第一步预处理"."
";$下一步();echo"后处理第一步"."
";}}//decorationDevice2类MiddleStepTwo实现中间件{publicstaticfunctionhandle(Closure$next){echo"第二步预处理"."
";$下一步();echo"后处理第二步"."
";}}functiongoFunc(){返回函数($step,$className){返回函数()使用($step,$className){return$className::handle($step);};};}$pip=array(MiddleStepOne::class,MiddleStepTwo::class,);$pip=array_reverse($pip);//反转数组运行以满足要求$first=function(){echo"预处理完成"."
";};//实际要处理的函数$a=array_reduce($pip,goFunc(),$first);//遍历pip数组,first作为第一个参数传入$a();//执行输出这是一个基于装饰器模式的简单流水线。它的本质其实是基于闭包和递归。通过分析这个程序,对于最后的$a变量,它的值大概是这样MiddleStepOne.handle(MiddleStepTwo.handle(first)),执行的时候,因为handle中有一个next()函数,所以这是递归调用。对于laravel的中间件,其实现原理同上。2.laravel中的中间件和请求处理管道在laravel中,我们可以设置中间件在请求执行之前做一些预处理。从请求入口public/index.php开始重要的是这段代码:即处理请求并返回请求的响应$response=$kernel->handle($request=Illuminate\Http\Request::capture()//创建请求实例);然后我们进入内核看它在IlluminateFoundationHttpKernel.php中dispatchToRouter()函数的具体实现,请自行阅读,这里就不多说了。接下去就是激动人心的PipeLine类了,container=$container;}/***设置通过管道发送的对象。**@parammixed$passable*@return$this*/publicfunctionsend($passable){$this->passable=$passable;返回$这个;}/***设置管道数组。**@paramarray|mixed$pipes*@return$this*/publicfunctionthrough($pipes){$this->pipes=is_array($pipes)?$管道:func_get_args();返回$这个;}/***设置调用管道的方法。**@paramstring$method*@return$this*/publicfunctionvia($method){$this->method=$method;返回$这个;}/***使用最终目标回调运行管道。**@param\Closure$destination*@returnmixed*/publicfunctionthen(Closure$destination){$pipeline=array_reduce(array_reverse($this->pipes),$this->carry(),$thi}s->prepareDestination($destination));返回$pipeline($this->passable);}/***获取最后一块Closure洋葱。**@param\Closure$destination*@return\Closure*/protectedfunctionprepareDestination(Closure$destination){returnfunction($passable)use($destination){return$destination($passable);}};}/***获取代表应用程序洋葱切片的闭包。**@return\Closure*/protectedfunctioncarry(){returnfunction($stack,$pipe){returnfunction($passable)use($stack,$pipe){if(is_callable($pipe)){//如果管道是闭包的一个实例,我们将直接调用它,但//否则我们将从容器中解析管道并使用适当的方法和参数调用它,返回结果又出来了。//如果pip也是一个中间件函数,是一个闭包可调用函数,直接返回闭包函数即可。//这里没找到对应的使用场景,补上return$pipe($passable,$stack);}elseif(!is_object($pipe)){list($name,$parameters)=$this->parsePipeString($pipe);//如果管道是一个字符串,我们将解析该字符串并将类从依赖注入容器中解析出来。然后我们可以构建一个可调用对象并//执行提供所需参数的管道函数。$pipe=$this->getContainer()->make($name);$parameters=array_merge([$passable,$stack],$parameters);}else{//如果管道已经是一个对象,我们将创建一个可调用对象并将其按原样传递给//管道。不需要做任何额外的解析和格式化//因为我们得到的对象已经是一个完全实例化的对象。$parameters=[$passable,$stack];}返回method_exists($pipe,$this->method)?$pipe->{$this->method}(...$parameters):$pipe(...$parameters);};};}/***解析完整的管道字符串以获取名称和参数。**@paramstring$pipe*@returnarray*/protectedfunctionparsePipeString($pipe){list($name,$parameters)=array_pad(explode(':',$pipe,2),2,[]);如果(is_string($parameters)){$parameters=explode(',',$parameters);}返回[$名称,$参数];}/***获取容器实例。**@return\Illuminate\Contracts\Container\Container*@throws\RuntimeException*/protectedfunctiongetContainer(){如果(!$this->container){thrownewRuntimeException('容器实例还没有被传递到管道。');}返回$this->容器;}}总的来说,pipeLine类的实现和我之前写的装饰器差不多。这里的主要问题是它在受保护的函数carry()函数内部。当pip是闭包、字符串、对象处理的时候,我觉得laravel的中间件是一个很神秘的东西。但是看了之后感觉就是那样,很精致,而且这种模式在实际开发中也很有帮助。比如我们目前正在使用的一个网关项目中,因为没有使用框架,所以将判断条件剥离出来,写入到中间件中,这样就实现了一定程度的模块化编程。
