array_reduce如何形成多层闭包函数【装饰者模式在Laravel框架中的实现】

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]

你可能感兴趣的:(array_reduce如何形成多层闭包函数【装饰者模式在Laravel框架中的实现】)