Laravel框架中使用装饰模式来处理请求。其中用到了array_reduce方法。那么array_reduce是如何完成调用的。
先看一下装饰模式代码的简化版:
代码出处
\n";
$next();
}
}
/*定义一个 错误分享类*/
class ShareErrorsFromSession implements Middleware
{
public static function handle(Closure $next)
{
echo "如果session中有'errors'变量,则共享它"."
\n";
$next();
}
}
/*定义一个 开启session类*/
class StartSession implements Middleware
{
public static function handle(Closure $next)
{
echo "开启session,获取数据"."
\n";
$next();
echo "报错数据,关闭session"."
\n";
}
}
/*定义一个 添加cookie队列至响应 类*/
class AddQueuedCookiesToResponse implements Middleware
{
public static function handle(Closure $next)
{
$next();
echo "添加下一次请求需要的cookie"."
\n";
}
}
/*定义一个 加密cookie类*/
class EncryptCookies implements Middleware
{
public static function handle(Closure $next)
{
echo "对输入请求的cookie进行解密"."
\n";
$next();
echo "对输出响应的cookie进行加密"."
\n";
}
}
/*定义一个 检测维护状态类*/
class ChecKForMaintenanceMode implements Middleware
{
public static function handle(Closure $next)
{
echo "确定当前程序是否处于维护状态"."
\n";
$next();
}
}
function getSlice()
{
$func = function($stack, $pipe)
{
error_log(implode(' | ',array(__FILE__,__FUNCTION__,__LINE__, ' first call',' stack:' . get_class($stack),'pipe:'. $pipe)));
$func1 = function() use ($stack, $pipe)
{
error_log(implode(' | ',array(__FILE__,__FUNCTION__,__LINE__,' second call',' stack:' . get_class($stack),'pipe:'. $pipe)));
return $pipe::handle($stack);
};
return $func1;
};
return $func;
}
function then()
{
$pipes = [
"ChecKForMaintenanceMode",
"EncryptCookies",
"AddQueuedCookiesToResponse",
"StartSession",
"ShareErrorsFromSession",
"VerifyCsrfToken"
];
$firstSlice = function() {
echo "请求向路由器传递,返回响应"."
\n";
};
print_r($pipes);
$pipes = array_reverse($pipes);
$reduce = array_reduce($pipes, getSlice(), $firstSlice);
var_dump($reduce);
call_user_func($reduce);
// array_reduce()向用户自定义函数发送数组中的值,并返回一个字符串:
}
then();
/**输出:
* 确定当前程序是否处于维护状态
* 对输入请求的cookie进行解密
* 开启session,获取数据
* 如果session中有'errors'变量,则共享它
* 验证Csrf-Token
* 请求向路由器传递,返回响应
* 保存数据,关闭session
* 添加下一次请求需要的cookie
* 对输出响应的cookie进行加密
*/
控制台输出:
$ php test.php
Array
(
[0] => ChecKForMaintenanceMode
[1] => EncryptCookies
[2] => AddQueuedCookiesToResponse
[3] => StartSession
[4] => ShareErrorsFromSession
[5] => VerifyCsrfToken
)
object(Closure)#8 (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#7 (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#6 (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#5 (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#4 (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#3 (1) {
["static"]=>
array(2) {
["stack"]=>
// stack和pipe这里就是静态外部变量,
// 通过使用外部静态变量,把上一层的结果当成下一层的参数。
// 然后形成这种迭代模型。
object(Closure)#1 (0) {
}
["pipe"]=>
string(15) "VerifyCsrfToken"
}
}
["pipe"]=>
string(22) "ShareErrorsFromSession"
}
}
["pipe"]=>
string(12) "StartSession"
}
}
["pipe"]=>
string(26) "AddQueuedCookiesToResponse"
}
}
["pipe"]=>
string(14) "EncryptCookies"
}
}
["pipe"]=>
string(23) "ChecKForMaintenanceMode"
}
}
确定当前程序是否处于维护状态
对输入请求的cookie进行解密
开启session,获取数据
如果session中有'errors'变量,则共享它
验证Csrf-Token
请求向路由器传递,返回响应
报错数据,关闭session
添加下一次请求需要的cookie
对输出响应的cookie进行加密
日志中输出:
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 76 | first call | stack:Closure | pipe:VerifyCsrfToken
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 76 | first call | stack:Closure | pipe:ShareErrorsFromSession
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 76 | first call | stack:Closure | pipe:StartSession
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 76 | first call | stack:Closure | pipe:AddQueuedCookiesToResponse
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 76 | first call | stack:Closure | pipe:EncryptCookies
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 76 | first call | stack:Closure | pipe:ChecKForMaintenanceMode
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 79 | second call | stack:Closure | pipe:ChecKForMaintenanceMode
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 79 | second call | stack:Closure | pipe:EncryptCookies
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 79 | second call | stack:Closure | pipe:AddQueuedCookiesToResponse
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 79 | second call | stack:Closure | pipe:StartSession
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 79 | second call | stack:Closure | pipe:ShareErrorsFromSession
[19-Mar-2019 10:58:27 Europe/Berlin] C:\xampp\htdocs\test\test.php | {closure} | 79 | second call | stack:Closure | pipe:VerifyCsrfToken
明显可以看出array_reduce方法返回的是一个多层闭包函数。
那么这种结构是如何形成?
mixed array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] )
array_reduce() 将回调函数 callback 迭代地作用到 array 数组中的每一个单元中,从而将数组简化为单一的值。
array_reduce 用回调函数迭代地将数组简化为单一的值。不明白这句话的意思,就写了以下代码进行理解:
1 调用顺序
输出:
-Dog-Cat-Horse
日志输出:
[18-Mar-2019 18:28:26 Asia/Shanghai] /home/dev/git/test/reduce.php | myfunction | 4 | v1: | v2:Dog
[18-Mar-2019 18:28:26 Asia/Shanghai] /home/dev/git/test/reduce.php | myfunction | 4 | v1:-Dog | v2:Cat
[18-Mar-2019 18:28:26 Asia/Shanghai] /home/dev/git/test/reduce.php | myfunction | 4 | v1:-Dog-Cat | v2:Horse
试着使用函数调用:
输出:
-Dog-Cat-Horse
日志输出:
[18-Mar-2019 18:28:26 Asia/Shanghai] /home/dev/git/test/reduce.php | myfunction | 4 | v1: | v2:Dog
[18-Mar-2019 18:28:26 Asia/Shanghai] /home/dev/git/test/reduce.php | myfunction | 4 | v1:-Dog | v2:Cat
[18-Mar-2019 18:28:26 Asia/Shanghai] /home/dev/git/test/reduce.php | myfunction | 4 | v1:-Dog-Cat | v2:Horse
那么就可以理解为是数组有n个元素,将数组最后一个元素作为函数第二个参数,然后前n-1个元素的没有function的值作为第一个参数;
那么前n-1个元素的myfunction的值为多少?
然后再调用 第n-1个元素作为函数的第二个参数,前n-2个元素的没有function的值作为第一个参数。
如此迭代下去,就如同我直接调用这个函数的调用方式:
其实,以上的只是其中一部分。
更多的是使用array_reduce将回调函数迭代形成一个元素的功能。
array_reduce会对其中的元素从前到后的进行迭代,第一个元素在最里面,那么对于call_user_func来说是最后执行,它的执行顺序是先外面再里面。
所以要按照数组定义的顺序来执行,那么就需要使用array_reverse对数组进行一次逆序操作。
手册中对于call_user_func这个方法没有特殊讲解,只是说用来调用回调函数。
2 不使用array_reduce来构成多层闭包函数
输出:
ttwp@ttwp /c/xampp/htdocs/test
$ php tt.php
object(Closure)#3 (1) {
["static"]=>
array(2) {
["func2"]=>
object(Closure)#2 (1) {
["static"]=>
array(2) {
["func1"]=>
object(Closure)#1 (1) {
["parameter"]=>
array(1) {
["$arg"]=>
string(10) ""
}
}
["arg"]=>
int(1)
}
}
["arg"]=>
int(1)
}
}
func3 [1]
func2 [1]
func1 [1]