本篇文章给大家带来的内容是关于laravel框架的中间件middleware的详解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
laravel中间件是个非常方便的东西,能将一些逻辑实现解耦,并且在laravel中,
中间件的编写也是非常的方便。谁用谁知道。
1.装饰器模式
laravel中的中间件使用的就是装饰器模式,什么是[装饰器模式][1],先去了解一下吧,这里大概说一下,就是这个模式主要的就是用于解决 当一个类需要动态扩展功能的时候,使用继承的方式会让子类膨胀,并且这个扩展的功能是个公用功能的情况下,不利于功能的复用以及代码的解耦。
在laravel,使用对于使用这种模式的功能,称为请求处理管道,也就是pipeline
//公共接口interface middleware { public static function handle(closure $next); }//装饰器1class middlestepone implements middleware{ public static function handle(closure $next) { echo 前期处理的第一步.<br>; $next(); echo 后期处理的第一步.<br>; } }//装饰器2class middlesteptwo implements middleware{ public static function handle(closure $next) { echo 前期处理的第二步.<br>; $next(); echo 后期处理的第二步.<br>; }}function gofunc() { return function ($step,$classname) { return function () use ($step,$classname) { return $classname::handle($step); }; };}$pip = array( middlestepone::class, middlesteptwo::class,);$pip = array_reverse($pip); //反转数组,以求达到要求的顺序运行$first = function (){ echo 前期处理完毕.<br>;}; //实际要处理的函数$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() //创建一个请求实例);
接着我们进入kernel中看他的具体实现 illuminatefoundationhttpkernel.php中
关于dispatchtorouter()函数请大家自己去看,这里就不多说了。
接下来就是激动人心的pipeline类了,
<?phpnamespace illuminate\pipeline;use closure;use runtimeexception;use illuminate\contracts\container\container;use illuminate\contracts\pipeline\pipeline as pipelinecontract;class pipeline implements pipelinecontract{ /** * the container implementation. * * @var \illuminate\contracts\container\container */ protected $container; /** * the object being passed through the pipeline. * * @var mixed */ protected $passable; /** * the array of class pipes. * * @var array */ protected $pipes = []; /** * the method to call on each pipe. * * @var string */ protected $method = 'handle'; /** * create a new class instance. * * @param \illuminate\contracts\container\container|null $container * @return void */ public function __construct(container $container = null) { $this->container = $container; } /** * set the object being sent through the pipeline. * * @param mixed $passable * @return $this */ public function send($passable) { $this->passable = $passable; return $this; } /** * set the array of pipes. * * @param array|mixed $pipes * @return $this */ public function through($pipes) { $this->pipes = is_array($pipes) ? $pipes : func_get_args(); return $this; } /** * set the method to call on the pipes. * * @param string $method * @return $this */ public function via($method) { $this->method = $method; return $this; } /** * run the pipeline with a final destination callback. * * @param \closure $destination * @return mixed */ public function then(closure $destination) { $pipeline = array_reduce( array_reverse($this->pipes), $this->carry(), $this->preparedestination($destination) ); return $pipeline($this->passable); } /** * get the final piece of the closure onion. * * @param \closure $destination * @return \closure */ protected function preparedestination(closure $destination) { return function ($passable) use ($destination) { return $destination($passable); }; } /** * get a closure that represents a slice of the application onion. * * @return \closure */ protected function carry() { return function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { if (is_callable($pipe)) { // if the pipe is an instance of a closure, we will just call it directly but // otherwise we'll resolve the pipes out of the container and call it with // the appropriate method and arguments, returning the results back out. //如果pip也就中间件函数是一个闭包可调用函数,就直接返回这个闭包函数就行了 //这里我还没有找到对应的使用场景,后续补充 return $pipe($passable, $stack); } elseif (! is_object($pipe)) { list($name, $parameters) = $this->parsepipestring($pipe); // if the pipe is a string we will parse the string and resolve the class out // of the dependency injection container. we can then build a callable and // execute the pipe function giving in the parameters that are required. $pipe = $this->getcontainer()->make($name); $parameters = array_merge([$passable, $stack], $parameters); } else { // if the pipe is already an object we'll just make a callable and pass it to // the pipe as-is. there is no need to do any extra parsing and formatting // since the object we're given was already a fully instantiated object. $parameters = [$passable, $stack]; } return method_exists($pipe, $this->method) ? $pipe->{$this->method}(...$parameters) : $pipe(...$parameters); }; }; } /** * parse full pipe string to get name and parameters. * * @param string $pipe * @return array */ protected function parsepipestring($pipe) { list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []); if (is_string($parameters)) { $parameters = explode(',', $parameters); } return [$name, $parameters]; } /** * get the container instance. * * @return \illuminate\contracts\container\container * @throws \runtimeexception */ protected function getcontainer() { if (! $this->container) { throw new runtimeexception('a container instance has not been passed to the pipeline.'); } return $this->container; }}
总的来说pipeline类的实现和我之前写的修饰器是差不多,这里主要麻烦的地方就在于就在于
protected function carry()函数内部,对于当pip是闭包,字符串,还有对象的处理。
之前觉得laravel的中间件是个很神秘的东西,但是看了之后才觉得也就那样,很精巧,在实际开发中这种模式也是很有帮助的,例如我们目前用的一个gateway项目,因为没有使用任何框架,所以将判断条件剥离,写入到中间件中, 这样实现了一定程度上的模块化编程。
以上就是laravel框架的中间件middleware的详解的详细内容。