中间件,听着很久了,但是它到底是怎么实现的?刚开始看到Lumen这其中的处理方式时,几行代码,硬是没看懂,一度怀疑自己。。。回味了好久,总算是可以体会到了,它的基础实现方式是如何运作的。
下面结合简单代码,通过一个简化的流程,体会一下核心应用,看看会输出什么
$array = [
'tom',
'jane',
'dark'
];
function show($origin, $item)
{
$callable = static function() use ($origin, $item) {
if (is_string($origin)) {
var_dump($origin);
}
var_dump($item);
if (is_callable($origin)) {
$origin();
}
};
return $callable;
}
$go = array_reduce($array, 'show', 'prepare');
$go();
——以上代码将输出
首先,我们需要理解一下函数array_reduce。个人理解:将数组中的每个元素使用回调元素,简化成一个单一的值。回调函数第一个参数为当前上一次回调结束后返回的值,如果是第一次,那么这个值为array_reduce指定的第三个参数,initial;第二个值为此次数组传递的元素。
官方文档解释为:array_reduce(array, callback[, initial = null] ) 将回调函数 callback 迭代地作用到 array 数组中的每一个单元中,从而将数组简化为单一的值。
在上述的示例中,回调函数show,返回的是一个匿名函数,在分析的过程中,我们先不去理会返回的匿名函数具体是如何执行的,我们只需要认识到,这是一个匿名函数,只有在被触发之后,才会运行。现在按照执行顺序整体解析一遍。
1、array_reduce,调起show,传入第一个参数:字符串prepare,和第二个参数:数组第一个元素tom,show的内部执行,是返回一个闭包,我们称之为A-tom,参数是origin:prepare;item:tom
2、array_reduce,调起show,传入第一个参数:闭包A-tom,和第二个参数:数组第二个元素jane,show的内部执行,依旧返回一个闭包,我们称之为B-jane,参数是origin:A-tom;item:jane
3、array_reduce,调起show,传入第一个参数:闭包B-jane,和第二个参数:数组第三个元素dark,show的内部执行,依旧返回一个闭包,我们称之为C-dark,参数是origin:B-jane;item:dark
4、最后go得到一个闭包,即C-dark
5、go被执行
6、闭包(B-jane,dark)执行,B-jane不是字符串,所以不进入,然后打印dark,此时屏幕输出【dark】,然后B-jane是可回调,则执行B-jane
7、B-jane同样是一个闭包,它的成员是origin:A-tom;item:jane,因此首先A-tom不是字符串,所以不进入,然后打印jane,此时屏幕输出【jane】,然后A-tom可回调,则执行A-tom
8、A-tom同样是一个闭包,它的成员是origin:prepare;item:tom,首先prepare是字符串,所以进入,打印prepare,此时输出【prepare】,然后打印tom,此时屏幕输出【tom】,最后prepare不是可回调,因此不进入。结束
所以整个过程下来,屏幕输出
dark
jane
prepare
tom
熟悉整个过程之后,我们更全局的看一遍整个过程:在array_reduce对每个元素执行show方法的时候,我们可以看做为一次又一次的不断对一个对象包装,而最初的第一个元素就在整个最终对象的最里面,最后执行的在最外层。这个可以比作为入栈的过程,而最后这个对象被执行的时候,就是一层一层的从最外面去拆开,去执行它,仿佛就是在剥洋葱,也可以比作是在出栈。
所以三个元素,最后进入的,最先被执行打印出来,然后第二层,第三层。在第三层的时候,它是最底层了,闭包的第一个参数已经是一个字符串了,不是闭包了,所以遇到了判断为字符串时,此时为真,所以先输出了prepare,然后接着遇到了打印item,输出tom。
而在具体的框架实现中,中间件show方法为类似这样的功能:
function carry($closures, $middleware)
{
return function() use($closures, $middleware) {
return $middleware->handle($closures);
};
}
每个中间件在被执行的时候,可以自己处理自己的事情:
class ExampleMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
echo 'before';
return $next($request);
echo 'after';
}
}
当然,实际的框架比这个复杂的多,以上只是针对实现的底层原理基础进行了一个剖析,后面还是得尝试去看看框架中的代码