浅谈laravel中间件的创建思路
Laravel 中间件提供了一种机制在不修改逻辑代码的情况下,中断原本程序流程,通过中间件来处理一些事件,或者扩展一些功能。比如日志中间件可以方便的记录请求和响应日志,而不需要去更改逻辑代码。
那么我们简化一下软件执行过程,现在有一个核心类kernel,下面是它的laravel代码
#捕获请求 $request = Illuminate\Http\Request::capture() #处理请求 $response = $kernel->handle($request);
代码的作用是 捕获一个 Request ,返回一个 Response。这里面就是后续分发到具体执行逻辑的代码段并返回结果。
那么如果想在执行这个$kernel->handle()方法之前或者之后,增加一段逻辑一般会怎么写呢。大概如下:
$request = Illuminate\Http\Request::capture() function midware(){ before()#在之前执行的语句集合 ##### $response = $kernel->handle($request); ##### after()#在之后执行的语句集合 }
显然这样写没有问题,但是毫无拓展性可言,想执行什么东西都要更改这个方法,这种是不可能封装成框架核心内容的。怎么改进呢
定义一个要执行的中间件类叫middleware,类实现两个方法,before()和after()然后代码如下。
#配置项中有一项配置中间件: middleware = ''; $request = Illuminate\Http\Request::capture() function midware(){ middleware.before() ##### $response = $kernel->handle($request); ##### middleware.after() }
是否解决了问题呢,是解决了不用更改的问题,但是我们如果需要多个中间件怎么办呢,最容易想到的就是:定义一个中间件数组middleware_arr,每一个middleware类都含有before和after方法,代码如下:
配置项中有middleware_arr middleware_arr=array(); $request = Illuminate\Http\Request::capture() function midware(){ foreach(middleware_arr as middleware){ middleware.before() } ##### $response = $kernel->handle($request); ##### foreach(middleware_arr as middleware){ middleware.after() } }
虽然有点老土,但是的确解决了问题。但是这个还存在一个问题,就是我们怎么向中间件传递参数的问题,那么如下可以吗:
$request = Illuminate\Http\Request::capture() function midware(){ foreach(middleware_arr as middleware){ middleware.before($request) } ##### $response = $kernel->handle($request); ##### foreach(middleware_arr as middleware){ middleware.after($response) } }
看似是解决了问题,但是仔细分析,就会发现,这里面每次给中间件的都是最初的$request,这显然不行,修改成如下:
$request = Illuminate\Http\Request::capture() function midware(){ foreach(middleware_arr as middleware){ $request = middleware.before($request) } ##### $response = $kernel->handle($request); ##### foreach(middleware_arr as middleware){ $response = middleware.after($response) } }
还有一个问题就是,假设有两个中间件A和B,那么执行顺序应该是怎么样呢:
$request = Illuminate\Http\Request::capture() $request = A.before($request); $request = B.before($request); $response = $kernel->handle($request); $response = A.after(); $response = B.after();
这样合理吗?不太好分辨,我们假设有一个记录请求和响应日志的中间件,这个时候,不论你把它放在什么位置,都不能完美的记录最初请求和最终日志。难道类似情况要写两个类,一个记录请求放在中间件数组第一个,一个处理响应,放在数组最后一位吗?不如在执行后面的foreach之前把middleware_arr数组给反转一下,这样就符合了要求:
$request = Illuminate\Http\Request::capture() $request = A.before($request); $request = B.before($request); $response = $kernel->handle($request); $response = B.after(); $response = A.after();
但是我也开始怀疑这个老土且不灵活的方案是否有更好的解决办法,在观察这个执行顺序的时候,发现是一个包裹样式(洋葱式)的。那个接下来的问题就能不能找到更灵活精美的解决方案,看上面这种结构,总感觉有点熟悉,他很像是A的函数包裹B的函数,B的函数包括了最初的执行代码。函数内部调用函数容易,但是咱们这里每一个中间件之间是不知道对方存在的,所以要把其他中间件要执行的函数传递到上一级,这里就用到了闭包函数还有一个php函数array_reduce(),
array_reduce函数定义:mixed array_reduce ( array $input , callable $function [, mixed $initial = NULL ] )
<?php function rsum ( $v , $w ){ $v += $w ; return $v ; } function rmul ( $v , $w ){ $v *= $w ; return $v ; } $a = array( 1 , 2 , 3 , 4 , 5 ); $x = array(); $b = array_reduce ( $a , "rsum" ); $c = array_reduce ( $a , "rmul" , 10 ); ?>
输出:
这将使 $b 的值为 15, $c 的值为 1200(= 10*1*2*3*4*5)
array_reduce() 将回调函数 function 迭代地作用到 input 数组中的每一个单元中,从而将数组简化为单一的值。咱们是把多个函数包裹成最终调用一个函数。
#我们先假设只有一个middleware,叫log来简化情况,这里的类应该是一个类全路径,我这里就简单的写一下,要不然太长了。 $middleware_arr = ['log']; #最终要执行的代码先封装成一个闭包,要不然没有办法传递到内层,如果用函数名传递函数的话,是没有办法传递参数的。 $default = function() use($request){ return $kernel->handle($request); } $callback = array_reduce($middleware_arr,function($stack,$pipe) { return function() use($stack,$pipe){ return $pipe::handle($stack); }; },$default); # 这里 callback最终是 这样一个函数: function() use($default,$log){ return $log::handle($default); }; #所以每一个中间件都需要有一个方法handle方法,方法中要对传输的函数进行运行,类似如下,这里我类名就不大写了 class log implements Milldeware { public static function handle(Closure $func){ $func(); } } #这里不难看出可以加入中间件自身逻辑如下: class log implements Milldeware { public static function handle(Closure $func){ #这里可以运行逻辑块before() $func(); #这里可以运行逻辑块after() } }
这样在执行callback函数的时候,执行顺序如下:
先运行log::haddle()方法,
执行了log::before()方法
运行default方法,执行$kernel->handle($request)
运行log::after()方法
然后模拟多个的情况如下:
$middleware_arr = ['csrf','log']; #最终要执行的代码先封装成一个闭包,要不然没有办法传递到内层,如果用函数名传递函数的话,是没有办法传递参数的。 $default = function() use($request){ return $kernel->handle($request); } $callback = array_reduce($middleware_arr,function($stack,$pipe) { return function() use($stack,$pipe){ return $pipe::handle($stack); }; },$default); # 这里 callback最终是 执行这样: $log::handle(function() use($default,$csrf){ return $csrf::handle($default); });
执行顺序如下:
1.先运行log::haddle(包含csrf::handle闭包函数)方法,
2.执行了log::before()方法
3.运行闭包也就是运行了$csrf::handle($default)
4.执行了csrf::before()方法
5.运行default方法,执行$kernel->handle($request)
6.执行了csrf::after()方法
7.运行log::after()方法
注意这里还有一个问题就是中间件产生的结果,并没有进行传递,可以通过修改共有资源的方式来达到相同的目的,并非需要真的传值到下一个中间件。
到此这篇文件就结束了,其实其中很多关节都是我写这篇文章的时候才想明白的。尤其是对闭包函数的运用和理解更深了,闭包函数可以延迟利用资源,比如当前不适合执行的语句,又要传递到后面,利用闭包可以封装起来传递出去,这是传统函数做不到的。
以上就是浅谈laravel中间件的创建思路的详细内容,更多关于laravel中间件的创建思路的资料请关注猪先飞其它相关文章!
相关文章
- 如果我们需要安培Laravel4的话最php最低要求要在php5.3.7版本并且我们需要把mcrypt与openss这两个扩展开启才可以,具体步骤我们参考下文。 前面我们介绍我了 com...2016-11-25
Laravel 调试工具 laravel-debugbar 打印日志消息
laravel-debugbar 调试工具的教程小编整理了几篇不错的教程,今天我们来看一篇Laravel 调试工具 laravel-debugbar 打印日志消息例子,希望文章对各位有帮助。 其实不...2016-11-25Laravel 5.1中定义事件、事件监听器以及触发事件例子
下文我们来看一篇关于Laravel 5.1中定义事件、事件监听器以及触发事件例子,希望能够帮助到各位新手朋友的哦。 这里我们基于之前基于模型+缓存对文章增删改查这篇...2016-11-25- Laravel框架我们用到的不多了,但如果使用需要搭配了,下面我们来看一篇关于搭建php Laravel框架教程详解,具体的操作细节如下所示,希望对各位有帮助。 一、安装 Compos...2016-11-25
- 你可能想创建一个在应用的任何地方都可以访问的函数,这个篇文章将带你解决此问题,感兴趣的同学,可以参考下。...2021-05-27
- Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁...2016-11-25
- 数据表之间是纵横交叉、相互关联的,laravel的一对一,一对多比较好理解,本文重点通过实例给大家讲解 laravel中的多对多关系,感兴趣的朋友一起看看吧 数据表之间是纵...2017-07-06
- 本文给大家讲解的是在laravel中是怎么实现autoload的?分析之后才发现,真的是很巧妙,下面就来给大家详细说明下...2017-05-21
- Laravel的主要技术特点:1、Bundle是Laravel的扩展包组织形式或称呼。Laravel的扩展包仓库已经相当成熟了,可以很容易的帮你把扩展包(bundle)安装到你的应用中。你可以选择下载一个扩展包(bundle)然后拷贝到bundles目录,或者...2015-10-21
- 这篇文章主要给大家介绍了关于利用.net core实现反向代理中间件的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用.net core具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2021-09-22
PHP框架Laravel中实现supervisor执行异步进程的方法
这篇文章主要给大家介绍了PHP框架Laravel中实现supervisor执行异步进程的方法,文中介绍的非常详细,相信对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。...2017-06-11- 数据表之间是纵横交叉、相互关联的,laravel的一对一,一对多比较好理解,本文重点通过实例给大家讲解 laravel中的多对多关系,感兴趣的朋友一起看看吧...2017-06-11
laravel Eloquent模型创建、更新及批量赋值详解
下面我们来看一篇关于laravel Eloquent模型创建、更新及批量赋值详解,希望文章能够让各位清楚的了解Eloquent模型的基本知识的哦。 1、创建模型 1.1 使用save方...2016-09-14- 这篇文章主要介绍了laravel 表单验证实现多个字段组合后唯一,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-06
- 多对多就相当于一个专题Topic有多个文章,但是这多个文章又属于多个专题,下面这篇文章主要给大家介绍了关于laravel多对多关联模型的相关资料,需要的朋友可以参考下...2021-08-11
- Laravel是一套简洁、优雅的PHP Web开发框架 (PHP Web Framework) 。在世界(不含中国)PHP框架的占有率超过40%。自从接触PHP以来一直使用Yii,感觉Yii实现功能比较简单,是一个很不错的框架。最近由于工作的原因开始研究Lar...2015-10-30
- 这篇文章主要介绍了Laravel的加密解密与哈希实例讲解,加密解密的知识,对于代码安全还是比较重要的,有感兴趣的同学可以学习下...2021-03-20
在laravel中使用Symfony的Crawler组件分析HTML
这篇文章主要介绍了在laravel中使用Symfony的Crawler组件分析HTML,需要的朋友可以参考下...2017-06-20- 这篇文章主要介绍了laravel日志优化实例讲解,日志是查找代码问题的重要的文件,有感兴趣的同学可以学习下...2021-03-19
- 这篇文章主要介绍了浅谈ASP.NET Core 2.0 中间件,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2021-09-22