PHP进阶之中间件代码实现

分享于:2019-09-07 00:24:48

楠神最近在学习laravel,初次接触中间件,TP框架5.1好像也支持中间件了。我有点孤陋寡闻了,这是什么新潮的技术或编程思想,跟楠神一起好好了解下。


中间件英文名“middleware”:


20170923115548221.png


看上面图,可简单理解一次http请求,从request-》application-》response,application就是真实的业务,可理解为写在控制器的代码,从request到application之间可以有一个或多个中间件,这样的中间件称为前置中间件。从application到response之间也可以有一个或多个中间件,这样的中间件称为后置中间件


中间件也是写代码的地方,一个项目很复杂时,我们不可能把所有的业务逻辑写在一个地方,好的程序代码需要做到很好的分层。


比如用户权限验证,用户有没有登录,很多控制器方法可能会用到这样的判断。楠神以前这样写:多个控制器继承一个公共父类控制器,构造方法也调用父类控制器构造方法,在父类控制器构造方法中写验证用户逻辑代码。使用中间件,也可以实现同样的效果:把验证用户逻辑代码写入一个前置中间件,访问某些控制器方法时先访问中间件。


以上两种方法都可以达到效果,每种方法各有各的好处,甚至使用中间件比楠神常用的第一种方法更广泛使用。比如在laravel框架给控制器添加中间件很方便,不需要修改控制器的代码。中间件越来越被更多开发者使用,我们有必要去了解下。


对中间件内容上的理解可以去看这篇文章《【PHP进阶】什么是中间件?(以laravel为例)


接下来讨论下中间件在PHP中用代码怎么实现:


模拟一个PHP中间件代码,先要对PHP闭包掌握下《PHP进阶之闭包》,中间件是一个闭包,而且返回一个闭包。


当一个中间件时


看代码,先添加一个中间件:

// 框架核心应用层,写入一匿名函数  $name项目名称,就是一随便的变量
$application = function($name) { 
	echo "{$name}项目业务逻辑代码<br />"; 
};

// 一个前置校验中间件,不仅是一匿名函数,还是一闭包函数 $handler也是一匿名函数变量,
//它是(已注册过其他中间件的)核心应用。
$middleware = function($handler) {
    return function($name) use ($handler) {
        echo "{$name}项目代码执行前的校验代码<br />";
        return $handler($name);
    };
};

//注册中间件 $handler是核心应用($application),$middlewareArr是中间件数组
function register_middleware($handler, $middlewareArr){
    foreach ( $middlewareArr as  $middleware) {//遍历中间件数组      
		//注册中间件并返回给下个中间件注册,
		//所谓注册其实就是把核心应用这个匿名函数当做参数赋值给中间件这个匿名函数
        $handler = $middleware($handler);
    }
    return $handler;
}

$run = register_middleware( $application ,  [$middleware] );
$run('Laravle');


执行结果:


1.png

代码是没有问题的,比较难懂是注册函数这块,如果看注册函数看不明白,可这样看

$run = $middleware($application);//相当于注册函数的作用
/*
$run = function($name){
        echo "{$name}项目代码执行前的校验代码<br />";
        return $application($name);
    };
*/
$run('Laravle');


当有两个中间件时


为了精简代码,楠神把注释去掉:

$application = function($name) { echo "{$name}项目业务逻辑代码<br />"; };
$middleware1 = function($handler) {
    return function($name) use ($handler) {
        echo "{$name}项目代码执行前的校验代码1<br />";
        return $handler($name);
    };
};
$middleware2 = function($handler) {
    return function($name) use ($handler) {
        echo "{$name}项目代码执行前的校验代码2<br />";
        return $handler($name);
    };
};
function register_middleware($handler, $middlewareArr){
    foreach ( $middlewareArr as  $middleware) { $handler = $middleware($handler); }
    return $handler;
}
$run = register_middleware( $application ,  [$middleware1,$middleware2] );
$run('Laravle');


执行结果:


1.png


注册函数的作用可以这样看:

$run = $middleware2( $middleware1( $application ) );//相当于注册函数的作用
/*
$run = function($name){
        echo "{$name}项目代码执行前的校验代码2<br />";
        echo "{$name}项目代码执行前的校验代码1<br />";
		return $application($name);
    };
*/

$run('Laravle');


最后注册的中间件先执行。


当三个中间件时


前面两个中间件都是前置中间件,怎么添加一个后置中间件呢?再看代码:

$application = function($name) { echo "{$name}项目业务逻辑代码<br />"; };
$middleware1 = function($handler) {
    return function($name) use ($handler) {
        echo "{$name}项目代码执行前的校验代码1<br />";
        return $handler($name);
    };
};
$middleware2 = function($handler) {
    return function($name) use ($handler) {
        echo "{$name}项目代码执行前的校验代码2<br />";
        return $handler($name);
    };
};
$middleware3 = function($handler) {
    return function($name) use ($handler) {
		$return = $handler($name);//重点是这,先执行前置中间件代码和核心应用代码
        echo "{$name}项目代码执行后的日志记录代码3<br />";
        return $return;
    };
};
function register_middleware($handler, $middlewareArr){
    foreach ( $middlewareArr as  $middleware) { $handler = $middleware($handler); }
    return $handler;
}
$run = register_middleware( $application ,  [$middleware1,$middleware2,$middleware3] );
$run('Laravle');

执行结果:


1.png

注册函数可以这样理解:

$run = $middleware3( $middleware2( $middleware1( $application ) ) );//相当于注册函数的作用
$run('Laravle');
/*
$run = function($name){
	 $return = $handler2($name);//看下面
	 echo "{$name}项目代码执行后的日志记录代码3<br />";
	 return $return;
     };
$handler2 = function($name){
	 echo "{$name}项目代码执行前的校验代码2<br />";
	 return $handler($name);//看下面
     };
$handler = return function($name){
	 echo "{$name}项目代码执行前的校验代码1<br />";
	 return $application($name);//看下面
     };
$application = function($name) { echo "{$name}项目业务逻辑代码<br />"; };
*/


好了,演示这些,总结就是:


中间件要满足一定的规范:总是返回一个闭包,闭包中总是传入相同的参数(由主要逻辑决定), 闭包总是返回句柄(handler)的执行结果;

如果中间件的逻辑在返回句柄return $handler($name)前完成,就是前置中间件,否则为后置中间件。