定义

首先什么是php的中间件?

根据zend-framework中的定义:

所谓中间件是指提供在请求和响应之间的,能够截获请求,并在其基础上进行逻辑处理,与此同时能够完成请求的响应或传递到下一个中间件的代码。

这一介绍十分的简洁,但却略显抽象,接下来我们通过例子来一个个看。

处在原始时代的CI

首先来看CI框架,php star数 12830.
作为一款非常简洁的框架,CI被吐槽的不少,但是也有很多人喜欢。

根据上文中对中间件的定义,那么对于CI框架来说,唯一称得上是内置中间件的:Security模块

Security模块是在请求进入controller之前实现的逻辑:

  • 请求在完成路由之后,进入controller之前;
  • CI框架支持通过配置的方式,决定是否启用包括“URI安全、XSS过滤、CSRF保护”在内的功能模块;
  • 一旦框架初始化时探测到模块启用,那么优先进行模块逻辑;
  • 触发安全模块,请求即告终止。

乍看起来,CI框架的中间件十分的局限,但是其实它却提供了无限的可能性。。因为CI中还提供了一个叫做Hooks的功能。即钩子。

下面来看两个个hooks的例子:

定义一个在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 \Illuminate\Http\Request $request
* @param \Closure $next
* @return 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单纯一些;
  • 提供中间件自动生成与方便扩展功能。

定义

首先什么是php的中间件?

根据zend-framework中的定义:

所谓中间件是指提供在请求和响应之间的,能够截获请求,并在其基础上进行逻辑处理,与此同时能够完成请求的响应或传递到下一个中间件的代码。

PHP中间件的更多相关文章

  1. ASP.NET Core 中的那些认证中间件及一些重要知识点

    前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...

  2. ASP.NET Core应用的错误处理[3]:ExceptionHandlerMiddleware中间件如何呈现“定制化错误页面”

    DeveloperExceptionPageMiddleware中间件利用呈现出来的错误页面实现抛出异常和当前请求的详细信息以辅助开发人员更好地进行纠错诊断工作,而ExceptionHandlerMi ...

  3. ASP.NET Core应用的错误处理[2]:DeveloperExceptionPageMiddleware中间件如何呈现“开发者异常页面”

    在<ASP.NET Core应用的错误处理[1]:三种呈现错误页面的方式>中,我们通过几个简单的实例演示了如何呈现一个错误页面,这些错误页面的呈现分别由三个对应的中间件来完成,接下来我们将 ...

  4. ASP.NET Core 中间件之压缩、缓存

    前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...

  5. .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法

    .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法 0x00 为什么需要Map(MapWhen)扩展 如果业务逻辑比较简单的话,一条主管道就够了,确实用不到 ...

  6. .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类

    .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类 0x00 为什么要引入扩展方法 有的中间件功能比较简单,有的则比较复杂,并且依赖其它组件.除 ...

  7. .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理

    .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...

  8. 从中间件的历史来看移动App开发的未来

    在移动开发领域我们发现一个很奇怪的现象:普通菜鸟新手经过3个月的培训就可以拿到 8K 甚至上万的工作:在北京稍微有点工作经验的 iOS 开发,就要求 2 万一个月的工资.不知道大家是否想过:移动应用开 ...

  9. ASP.NET Core的路由[4]:来认识一下实现路由的RouterMiddleware中间件

    虽然ASP.NET Core应用的路由是通过RouterMiddleware这个中间件来完成的,但是具体的路由解析功能都落在指定的Router对象上,不过我们依然有必要以代码实现的角度来介绍一下这个中 ...

  10. ASP.NET Core应用针对静态文件请求的处理[5]: DefaultFilesMiddleware中间件如何显示默认页面

    DefaultFilesMiddleware中间件的目的在于将目标目录下的默认文件作为响应内容.我们知道,如果直接请求的就是这个默认文件,那么前面介绍的StaticFileMiddleware中间件会 ...

随机推荐

  1. Docker --Dockerfile(制作镜像)

    Dockerfile Dockerfile 是一个文本格式的配置文件,用户可以使用 Dockerfile 快速创建自定义的镜像 Dockerfile 常用指令 FROM 作用:指定基础镜像,Docke ...

  2. 一条sql查出数据库某张表的所有属性

    select t.TABLE_NAME,--表名 t.COLUMN_NAME,--字段名 t.DATA_TYPE,--字段属性 t.DATA_LENGTH,--类型长度 t.DATA_PRECISIO ...

  3. 网际互连__TCP/IP三次握手和四次挥手

    在TCP/IP协议中,TCP协议提供可靠的连接服务. 位码即tcp标志位,有6种标示: SYN(synchronous建立联机).ACK(acknowledgement 确认).PSH(push传送) ...

  4. 从官方文档中探索MySQL分页的几种方式及分页优化

    概览 相比于Oracle,SQL Server 等数据库,MySQL分页的方式简单得多了,官方自带了分页语法 limit 语句: select * from test_t LIMIT {[offset ...

  5. springsecurity教程一

    可以看这个人的springsecurity省的自己写了 1.springsecurity学习目标 2.1 springsecurity简介 2.2 springsecurity快速入门demo 1): ...

  6. python的re模块一些方法 && Tkinter图形界面设计 && 终止python运行函数 && python读写文件 && python一旦给字符串赋值就不能单独改变某个字符,除非重新给变量赋值

    Tkinter图形界面设计见:https://www.cnblogs.com/pywjh/p/9527828.html#radiobutton 终止python运行函数: 采用sys.exit(0)正 ...

  7. Codeforces Round #669 (Div. 2) B. Big Vova (枚举)

    题意:有一个长度为\(n\)的序列,你需要对其重新排序,构造一个新数组\(c\),\(c_{i}=gcd(a_{1},...,a{i})\)并且使得\(c\)的字典序最小. 题解:直接跑\(n\)次, ...

  8. 牛客编程巅峰赛S1第5场 - 青铜&白银 B.完全平方数的尾巴 (暴力)

    题意:有一个数\(x\),判断其是否能有某个完全平方数$mod$1000得到. 题解:直接写个for判断一下就好了,因为对1000取模,所以枚举到1000即可. 代码: class Solution ...

  9. kubernetes实战-配置中心(四)分环境使用apollo配置中心

    要进行分环境,需要将现有实验环境进行拆分 portal服务,可以各个环境共用,但是apollo-adminservice和apollo-configservice必须要分开. 1.zk环境拆分为tes ...

  10. 流水线cpu —Verilog HDL

    一.准备工作 先看看书(<计算机原理与设计 Verilog HDL版>),搞懂一点原理.然后照着书上的代码写一写(用8.4的就可以了,不用8.6的). 注意mux2x32,mux4,cla ...