PHP中间件(middleware)解析

笔者在研读guzzlehttp源码时,对中间件的使用研究了一翻,在此纪录学习笔记。

我的理解

在执行主要逻辑时,完成一些附带的逻辑,可以分为前置和后置中间件。附带的逻辑往往带有条件判断,比如验证是否有权限,判断配置信息从而做出相应操作。目的是使各个业务逻辑更清晰、独立、可拆卸。

举例

// 主要逻辑
$meet = function($name){
    echo "nice to meet you, $name \n";
};

// 前置中间件
$hello = function($handler){
    return function($name)use($handler){
        echo "hello ".$name.", may I have your name\n";
        $name = 'Lucy';
        return $handler($name);
    };
};

// 前置中间件
$weather = function($handler){
    return function($name)use($handler){
        echo 'what a day'."\n";
        return $handler($name); // weather_return_handler行
    };
};

// 后置中间件
$dinner = function($handler){
    return function($name)use($handler){
        $return = $handler($name);
        $name = 'Lucy';
        echo "OK, $name. Will you have dinner with me?\n";
        return $return;
    };
};
// 中间件栈
$stack = [];

// 打包
function prepare($handler, $stack){
    foreach(array_reverse($stack) as $key => $fn){
        // echo $key; 记为echo_key行
        $handler = $fn($handler); // 记为iterator行
    }
    return $handler;
}
// 入栈
$stack['dinner'] = $dinner;
$stack['weather'] = $weather;
$stack['hello'] = $hello;


// 把所有逻辑打包成一个闭包(closure)
$run = prepare($meet, $stack);

$run('beauty'); // 记为run_prepare

/* 执行结果:
what a day
hello beauty, may I have your name
nice to meet you, Lucy 
OK, Lucy. Will you have dinner with me?
*/

编写规范

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

如果中间件的逻辑在返回句柄return $handler($name)前完成,就是前置中间件,否则为后置。这只是一个代码规范,你完全可以在一个中间件中的任何位置写逻辑代码,只要在最后正确返回。

打包程序

中间件的执行顺序是由打包函数(prepare)决定,这里是先入栈先执行,比如weather比hello先执行。

打包函数是一个这里有趣的地方。如果把prepare函数中的echo_key行 注释打开,会发现打包顺序是hello, weather, dinner。
所以,返回的闭包实际上相当于:

$closure = $dinner($weather($hello($meet))); // 记为make_closure行
$closure('beauty');  // 记为run_closure

困惑的地方在于,为什么weather的执行顺序在hello前。常规的函数如A(B(C('hehe'))), 执行顺序应该是C,B,A。

因为中间件是一个闭包,而且返回一个闭包(满屏尽是闭包),意味着一个中件间要运行两次。外层闭包在打包的时候执行,也就是iterator行 或者 make_closure行,同时决定里层的执行顺序。里层闭包在调用时执行, run_prepare行或run_closure行。

表达式$hello($meet) 返回的是$hello 的里层闭包而不是句柄,因为里层未执行,所以hello beauty不会出现。也就是说,$weather 的形参$handler 对应的实参就是$hello的里层闭包。当程序运行到weather_return_handler行时,$hello 的里层闭包才真正执行,这时$weather 的逻辑已经执行完了。

打包程序的作用可以概括为执行当前中间件的外层闭包,得到里层闭包作为参数,再传给下一个中间件的外层闭包,直至迭代结束。

你可能感兴趣的:(PHP中间件(middleware)解析)