市面上常见的php框架有不少,最近由于有技术需求,因此对常见的php框架的中间件进行了一些了解。各个框架尽管在目标上对php框架的定义大同小异,可是在实现方式上却各有不一样,且看下文:php
根据zend-framework中的定义:nginx
所谓中间件是指提供在请求和响应之间的,可以截获请求,并在其基础上进行逻辑处理,与此同时可以完成请求的响应或传递到下一个中间件的代码。 这一介绍十分的简洁,但却略显抽象,接下来咱们经过例子来一个个看。git
##处在原始时代的CIgithub
首先来看CI框架,php star数 12830. 做为一款很是简洁的框架,CI被吐槽的很多,可是也有不少人喜欢。首先来看它官方给出的一张请求时序图:api
CI框架请求时序 根据上文中对中间件的定义,那么对于CI框架来讲,惟一称得上是内置中间件的:Security模块跨域
Security模块是在请求进入controller以前实现的逻辑:浏览器
请求在完成路由以后,进入controller以前; CI框架支持经过配置的方式,决定是否启用包括“URI安全、XSS过滤、CSRF保护”在内的功能模块; 一旦框架初始化时探测到模块启用,那么优先进行模块逻辑; 触发安全模块,请求即了结止。 乍看起来,CI框架的中间件十分的局限,可是其实它却提供了无限的可能性。。由于CI中还提供了一个叫作Hooks的功能。即钩子。缓存
下面来看两个个hooks的例子:php框架
定义一个在controller逻辑以前的钩子,并指定钩子的参数、类名或函数名信息:安全
$hook['pre_controller'] = array( 'class' => 'MyClass', 'function' => 'Myfunction', 'filename' => 'Myclass.php', 'filepath' => 'hooks', 'params' => array('beer', 'wine', 'snacks') ); 定义一个在controller逻辑以后的钩子,并直接给出其实现:
$hook['post_controller'] = function() { /* do something here */ }; 为何说CI没提供什么像样的中间件可是又很灵活呢,就是由于它能够在以下的多个阶段进行挂钩子的操做。细数过来有7种之多。
从后文中能够看出,不少其余的框架可能也就会涵盖两三种阶段,所以,从这个角度上来讲,CI的钩子组合而成的中间件的确很灵活。
pre_system阶段: 在系统执行的早期调用,这个时候只有 基准测试类 和 钩子类 被加载了, 尚未执行到路由或其余的流程; pre_controller阶段: 在你的控制器调用以前执行,全部的基础类都已加载,路由和安全检查也已经完成; post_controller_constructor阶段: 在你的控制器实例化以后当即执行,控制器的任何方法都还还没有调用; post_controller阶段: 在你的控制器彻底运行结束时执行; display_override阶段: 覆盖 _display() 方法,该方法用于在系统执行结束时向浏览器发送最终的页面结果; 这可让你有本身的显示页面的方法。注意你可能须要使用 $this->CI =& get_instance()方法来获取 CI 超级对象,以及使用 $this->CI->output->get_output()方法来 获取最终的显示数据; cache_override阶段: 使用你本身的方法来替代 输出类 中的 _display_cache() 方法,这让你有本身的缓存显示机制。 post_system 在最终的页面发送到浏览器以后、在系统的最后期被调用。 总结来看,CI中的中间件:
有很大的自由度 同时支持在多个阶段对请求进行嵌入(对比下来是最全面的) 钩子函数的使用成本高; 支持各类diy: 请求来时http校验、权限校验、额外的安全策略 请求去时上报数据
##大红大紫的Laravel
github star 24997 做为最近两年大红大紫的Laravel,的确也是有必要对其中间件机制进行了解:
首先Laravel提供了一个很好的中间件自动生成工具: php artisan make:middleware OldMiddleware 由Laravel的命令行完成,这种看似简单的命令行工具其实能够对框架的扩展起到很是重要的做用。
再来看一个Laravel中典型的请求过滤器:
<?php namespace App\Http\Middleware; use Closure; class OldMiddleware { /** * 运行请求过滤器。 * * [@param](https://my.oschina.net/u/2303379) \Illuminate\Http\Request $request * [@param](https://my.oschina.net/u/2303379) \Closure $next * [@return](https://my.oschina.net/u/556800) mixed */ public function handle($request, Closure $next) { if ($request->input('age') <= 200) { return redirect('home'); } return $next($request); } }
过滤器,filter,是中间件中使用最普遍的一种,不少框架里甚至filter就等同于中间件。意如其名,便是对请求Request进行某种过滤,这个过滤能够是参数上的限制、安全策略的限制、http协议的限制,只要是请求中带来的属性,均可以据此进行过滤。
同时这里也能够看到,Laravel使用闭包的方式进行请求的传递,真正践行的优雅的中间件串联的方式,只须要调用next函数,请求便可被按照预先定义的规则传递到下一个中间件中。
Laravel支持全局的中间件和根据具体路由规定的中间件两种,同时优先级又以定义顺序为准。作到全局与具体状况的兼顾。同时它显示的支持前置、后置和Terminable三种中间件,覆盖了大部分的中间件场景,是一种相对不错的设计。
但美中不足或者说场景覆盖不够友好的地方在于它以路由的方式组织中间件,会与controller有些脱节,每次定义controller中action行为的时候,还须要转换为路由进行配置,略有些不方便。
总结来看
Laravel践行了让controller更纯粹的思想,中间件交给路由,controller只作它该作的事; 中间件与路由组灵活结合,可以知足应用场景; 前置、后置与Terminable支持了现有大部分的中间件需求; 自动生成十分方便扩展中间件,开发友好; 但对一个controller内多个action须要统一加入或统一不加入中间件的场景,支持不友好。
##老生常谈yii 2.0
github star 4668
yii框架首先是中国人开发的,star数虽然不是不少,可是功能也算丰富。 yii框架从1.1到2.0,通过了一个比较大的升级,支持了不少新的特性,若是不支持,只怕是要落伍了。
在yii框架1.1中,中间件干脆就叫filters了,十分的直白,分为pre-filter和post-fiter两种,即前文中说的,在进入controller以前的过滤逻辑,和完成controller处理以后的过滤逻辑。
可是到了yii2.0以后,filters通过了一层升级,到了behaviors,明确了一点:重心放在了每个controller的行为上,而不是像Laravel同样controller很傻很单纯。
yii框架的behaviors能够在controller或application中配置。
这里是一个访问控制的filter,具体进行什么样的访问控制由className定义,同时对controller中的action支持“only”关键字,还有“”关键字,可以支持排除法的功能,这个在一些场景下仍是颇有用的。同时“roles”也可以支持你预先定义好的角色的概念,好比学生没法访问教师后台,而教师没法访问学生论坛等。
use yii\filters\AccessControl; public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'only' => ['create', 'update'], 'rules' => [ // allow authenticated users [ 'allow' => true, 'roles' => ['@'], ], // everything else is denied by default ], ], ]; }
固然,在Yii中你也能够自定义filter:
namespace app\components; use Yii; use yii\base\ActionFilter; class ActionTimeFilter extends ActionFilter { private $_startTime; public function beforeAction($action) { $this->_startTime = microtime(true); return parent::beforeAction($action); } public function afterAction($action, $result) { $time = microtime(true) - $this->_startTime; Yii::trace("Action '{$action->uniqueId}' spent $time second."); return parent::afterAction($action, $result); } }
这里明显能够看出,这个filter针对action,分别在“beforeAction”和“afterAction”两个阶段进行了逻辑处理,完成了请求的计时工做。
因此总的来看,Yii框架中的中间件:
支持前置和后置两个阶段的自定义; 提供了基本的访问控制中间件; 配置侵入到controller中,完成对controller行为的深度控制; 没法自动生成中间件,自定义成本略高。 你们伙 ZendFramework ZendFramework是由zend公司推出的php框架,其目标就是创建一套大而全的php框架。以知足企业应用开发的目标。 ZendFramework由不少不一样的模块构成,使用者能够经过相互组合的方式来实现本身想要的功能,同时也可以不一次加载大而全的框架,十分的灵活。 好比有负责受权的"zend-authentication",或者是负责验证码的"zend-captcha"等等。
其中"zend-stratigility" 负责提供中间件以及中间件执行流的功能。
use Zend\Stratigility\MiddlewarePipe; use Zend\Diactoros\Server; require __DIR__ . '/../vendor/autoload.php'; $app = new MiddlewarePipe(); $server = Server::createServer($app, $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); // Landing page $app->pipe('/', function ($req, $res, $next) { if (! in_array($req->getUri()->getPath(), ['/', ''], true)) { return $next($req, $res); } return $res->end('Hello world!'); }); // Another page $app->pipe('/foo', function ($req, $res, $next) { return $res->end('FOO!'); }); $server->listen();
这里的代码给出了两个中间件的例子。第一个是落地页,监听了root路径,若是命中了这一路由规则,那么请求会被提早结束,返回给用户“Hello world!”。 而第二个中间件去匹配foo这一路径,模糊匹配的方式,若是命中了,会返回FOO并结束请求。
与Laravel相似,这里一样支持使用next(可调用的变量)的方式将请求继续向下传递。而这里中间件配置的方式也跟Laravel比较像,是统一在一个地方根据路由进行配置的,这样彻底能够按照以下的方式根据不一样的路由定义不一样的中间件处理逻辑:
$app->pipe('/api', $apiMiddleware); $app->pipe('/docs', $apiDocMiddleware); $app->pipe('/files', $filesMiddleware);
总结来看,ZendFramework的中间件:
主要侧重在请求前置阶段,淡化了请求后置或其余阶段 经过路由的方式统一配置中间件,支持串行 并未预先定义中间件 我心目中的中间件设计 首先按照不一样的类别列举一下常见的中间件:
前置中间件: cookie验证:验证用户的cookie 用户角色验证:定义不一样的用户角色并验证 用户权限验证:配置不一样的用户权限,并验证 安全相关,如CSRF校验:CSRF校验中间件 http方法过滤:过滤特定的GET POST请求 http或者page cache:对指定路径的页面进行缓存 跨域中间件:不用在nginx配置,而是经过框架的方式,针对某些域名或某些请求,提供跨域的服务。 后置中间件: 共同数据输出:针对统一业务的公共数据,在后置中统一输出 请求返回浏览器以后的中间件: 打印日志 更新session(Laravel) 因此一个php框架最好可以:
定义核心可用中间件; 提供在不一样阶段扩展中间件的能力,不能太多,支持前置和后置便可覆盖大部分场景; 统一配置中间件,方便管理全部的中间件,让controller单纯一些; 提供中间件自动生成与方便扩展功能