<?php
interface Middleware
{
public static function handle(Closure $next);
}
class VerifyCsrfToken implements Middleware
{
public static function handle(Closure $next)
{
echo "(5)验证Csrf-Token".'<br>';
$next();
}
}
class ShareErrorsFromSession implements Middleware
{
public static function handle(Closure $next)
{
echo "(4)如果session中有'errors'变量,则共享它".'<br>';
$next();
}
}
class StartSession implements Middleware
{
public static function handle(Closure $next)
{
echo "(3)开启session,获取数据".'<br>';
$next();
echo "(7)保存数据,关闭session".'<br>';
}
}
class AddQueuedCookiesToResponse implements Middleware
{
public static function handle(Closure $next)
{
$next();
echo "(8)添加下一次请求需要的cookie".'<br>';
}
}
class EncryptCookies implements Middleware
{
public static function handle(Closure $next)
{
echo "(2)对输入请求的cookie进行解密".'<br>';
$next();
echo "(9)对输出相应的cookie进行加密".'<br>';
}
}
class CheckForMaintenanceMode implements Middleware
{
public static function handle(Closure $next)
{
echo "(1)确定当前程序是否处于维护状态".'<br>';
$next();
}
}
function getSlice()
{
return function($stack, $pipe)
{
return function() use ($stack, $pipe)
{
return $pipe::handle($stack);
};
};
}
function then()
{
$pipes = [
"CheckForMaintenanceMode",
"EncryptCookies",
"AddQueuedCookiesToResponse",
"StartSession",
"ShareErrorsFromSession",
"VerifyCsrfToken"
];
$firstSlice = function() {
echo "(6)请求向路由器传递,返回响应.".'<br>';
};
$pipes = array_reverse($pipes);
$go = array_reduce($pipes, getSlice(),$firstSlice);
$go();
}
then();
?>
<?php
function myfunction($v1,$v2)
{
return $v1+$v2;
}
$a=array(10,15,20);
print_r(array_reduce($a,"myfunction",5)); //50
?>
上面的结果为50.那么它的过程是怎么样的呢?我们对代码进行改良
function myfunction($v1, $v2)
{
var_dump($v1, $v2);
return $v1 + $v2;
}
$a = array(10, 15, 20);
print_r(array_reduce($a, "myfunction", 5)); //50
echo "\n";
然后可以看到如下输出
int(5)
int(10)
int(15)
int(15)
int(30)
int(20)
50
第一个v1 = 5,v2 = 10;
第二个v1 = 15 (前一个返回的值) , v2 = 15; $a[1]的值;
第三个v1 = 30 (上一次的返回值) , v2 = 20; $a[2]的值;
最后输出50. 那么我们看第一个值为什么是5? 因为array_reduce接收的第3个参数就是表示当第一次迭代的时候传递的值。下面我们来自己实现一个array_reduce去深度的理解它 :)
function myfunction($v1, $v2)
{
var_dump($v1, $v2);
return $v1 + $v2;
}
$a = array(10, 15, 20);
print_r(my_array_reduce($a, "myfunction", 5)); //50
echo "\n";
/**
* array_reduce
* @param array $arr
* @param callable $fn
* @param null $initial
* @return mixed
*/
function my_array_reduce(array $arr, callable $fn, $initial = null)
{
$v = $initial;
foreach ($arr as $item) {
$v = $fn($v, $item);
}
return $v;
}
这样是不是好多了呢?
下面是根据题主的问题进行补充
$arr = [
'VerifyCsrfToken'
];
function getSlice()
{
return function($stack, $pipe)
{
return function() use ($stack, $pipe)
{
return $pipe::handle($stack);
};
};
}
$firstSlice = function() {
echo "(6)请求向路由器传递,返回响应.".'<br>';
};
$go = array_reduce($pipes, getSlice(),$firstSlice);
$go();
当arr 是上面的数组 以及回调的方法是上面的方法时.我们来看看发生了什么
首先
1.getSlice的返回值是function
当arr第一次循环的时候,根据我们上面所提到的array_reduce的原理看看发生了什么?
$arr = [
'VerifyCsrfToken'
];
function getSlice()
{
return function($stack, $pipe)
{
return function() use ($stack, $pipe)
{
return $pipe::handle($stack);
};
};
}
首先看这里
$go = array_reduce($pipes, getSlice(),$firstSlice);
第二个参数传的并不是callback 而是 直接写的 getSlice(); 那么这个函数将会直接执行并且将返回值传递给 array_reduce的第二个参数.
也就是直接返回
function ($stack, $pipe) {
return function () use ($stack, $pipe) {
return $pipe::handle($stack);
};
};
也就是和下面的写法是等价的。
function getSlice($stack, $pipe)
{
return function () use ($stack, $pipe) {
return $pipe::handle($stack);
};
}
$firstSlice = function () {
echo "(6)请求向路由器传递,返回响应\n";
};
$go = my_array_reduce($pipes, "getSlice", $firstSlice);
$go();
-
只是因为前者的写法更加优雅 易于让ide查找;
弄清楚了这个我们接下来继续看。
return function () use ($stack, $pipe) {
return $pipe::handle($stack);
};
当第一次迭代的时候 $stack 的值为$firstSlice
pipe 的值 为 VerifyCsrfToken.
那么这个函数被执行了.$firstSlice当作参数。
所以当调用$go();时
所有的pipe::handle方法会立即执行。
而每次都把$stack作为参数
所以执行顺序是倒过来了。因为到最后一次的时候 $next 才 === $firstSlice
$pipes = [
"VerifyCsrfToken",
"VerifyCsrfToken1"
];
/**
* array_reduce
* @param array $arr
* @param callable $fn
* @param null $initial
* @return mixed
*/
function my_array_reduce($arr, callable $fn, $initial = null)
{
$v = $initial;
foreach ($arr as $item) {
$v = $fn($v, $item);
}
return $v;
}
再来回顾这段代码.为什么VerifyCsrfToken1先执行呢?
因为当foreach执行完毕的时候. $item = $pipes[count($pipes)-1];
也就是最后一个而 $v 永远为上一个return 的 值。