zoukankan      html  css  js  c++  java
  • PHP学习笔记(一):理解匿名函数与Closure

    1.PHP里的匿名函数实质是Closure类的实例

    (1)不能自己实例化Closure类型的对象,会触发一个Error

    try{
        $closure = new Closure();
    }catch(Error $e){
        var_dump($e);
    }

    (2)匿名函数如何使用父作用域的变量?

    答案:使用use()将父作用域的变量加载到匿名函数对象的静态变量表中

    $msg2 = 'Closure!';
    $greet = function($msg1)use($msg2){
        echo $msg1,' ',$msg2,'<br>';
    };
    $greet('Hello');
    echo gettype($greet);//匿名函数是一个Object
    echo get_class($greet);//匿名函数是类Closure的实例

    注:echo中使用','比'.'效率高,原因是echo执行效率比拼接字符串效率高

    2.create_function()不是标准的匿名函数

    create_function($params,$operation)创建一个函数并返回函数名称。

    (1)返回的函数名称是以‘’开头的全局唯一函数名称:

    $createFuncName = create_function('$arg1,$arg2','echo $arg1," ",$arg2;');//这里用单引号定义第一个参数是为了防止双引号中的变量被解析
    echo '创建的函数名称看起来是:',$createFuncName,'<br>';//lambda_1
    $createFuncName('Hello','create_function()!');
    $myFuncName = trim($createFuncName);//这里不要写死lambda_1随着多运行几次函数返回的名称会有变化
    try{
        $myFuncName();
    }catch(Error $e){
        var_dump($e);//Error:Call to undefined function ()
    }
    echo '实际上生成的函数名称第一个字符是:','<br>';
    var_dump($createFuncName);//string(9) "lambda_1"
    var_dump($myFuncName);//string(8) "lambda_1"
    //手动拼一个''在前面就ok啦
    $myFuncName = chr(0).$myFuncName;
    $myFuncName('Hello','real function name!');
    echo gettype($createFuncName),'<br>';//string 不是匿名函数

    create_function()返回一个函数名称,所以创建的不是标准的匿名函数,而是有系统生成了一个唯一的函数名。

    3.难啃的骨头:Closure::bind()和Closure::bindTo()

    (1)bind()

    借用官方的示例:

    class A {
        private static $sfoo = 1;
        private $ifoo = 2;
    }
    $cl1 = static function() {
        return A::$sfoo;
    };
    $cl2 = function() {
        return $this->ifoo;
    };
    
    $bcl1 = Closure::bind($cl1, null, 'A');
    $bcl2 = Closure::bind($cl2, new A(), 'A');
    echo $bcl1(), "
    ";//1
    echo $bcl2(), "
    ";//2

    这里使用一个php扩展zendump来看一下bind()后发生了什么变化:

    class A {
        public static $psa = 3;
        private static $sfoo = 1;
        private $ifoo = 2;
    }
    //增加一个不进行bind()的匿名函数
    $closure = function(){
        $c = A::$psa;
        zendump_opcodes();
        return $c;
    };
    $cl1 = static function() {
        $a = A::$sfoo;
        zendump_opcodes();//使用扩展提供方法打印当前匿名函数执行内容
        return $a;
    };
    $cl2 = function() {
        $b = $this->ifoo;
        zendump($this);//使用扩展提供方法打印当前$this变量内容
        zendump_opcodes();
        return $b;
    };
    $bcl1 = Closure::bind($cl1, null, 'A');
    $bcl2 = Closure::bind($cl2, new A(), 'A');
    
    echo $bcl1(), "
    ";
    echo $bcl2(), "
    ";
    echo $closure(), "
    ";
    zendump_class('A');//使用扩展提供方法打印类A的基本信息

    1)echo $closure();这一步会打印一个没有被bind()的匿名函数执行情况:

    op_array("{closure}") {closure}() refcount(2) addr(0x7f28c4461cb8) vars(1) T(3) filename(/var/www/html/index.php) line(47,51)
    OPCODE                             OP1                                OP2                                RESULT                             EXTENDED                           
    ZEND_FETCH_STATIC_PROP_R           "psa"                              "A"                                #var0                                                                 
    ZEND_ASSIGN                        $c                                 #var0                                                                                                    
    ZEND_INIT_FCALL                    80                                 "zendump_opcodes"                                                     0                                  
    ZEND_DO_ICALL                                                                                                                                                                  
    ZEND_RETURN                        $c                                                                                                                                          
    ZEND_RETURN                        null                                                                                                                                        
    3

    这里描述执行的函数是{closure},内部获取了类A的静态变量psa;

    2)再看echo $bcl1();这一步:

    op_array("A::{closure}") {closure}() refcount(3) addr(0x7f28c4462078) vars(1) T(3) filename(/var/www/html/index.php) line(52,56)
    OPCODE                             OP1                                OP2                                RESULT                             EXTENDED                           
    ZEND_FETCH_STATIC_PROP_R           "sfoo"                             "A"                                #var0                                                                 
    ZEND_ASSIGN                        $a                                 #var0                                                                                                    
    ZEND_INIT_FCALL                    80                                 "zendump_opcodes"                                                     0                                  
    ZEND_DO_ICALL                                                                                                                                                                  
    ZEND_RETURN                        $a                                                                                                                                          
    ZEND_RETURN                        null                                                                                                                                        
    1

    与1)中对比,明显区别是执行的函数被描述为:A::{closure},所以在这里可以获取类A的私有静态变量sfoo;

    3)最后看看echo $bcl2();这一步:

    先看$this的输出情况:

    zval(0x7f28c44132f0) -> object(A) addr(0x7f28c4457310) refcount(2) {
      default_properties(1) {
        $ifoo =>
        zval(0x7f28c4457338) : long(2)
      }
      static_members(2) {
        $psa =>
        zval(0x7f28c4470900) : long(3)
        $sfoo =>
        zval(0x7f28c4470910) : long(1)
      }
    }

    这里描述匿名函数$bcl2中使用的$this是类A的对象,再看看匿名函数$bcl2的情况:

    op_array("A::{closure}") {closure}() refcount(3) addr(0x7f28c44621b8) vars(1) T(5) filename(/var/www/html/index.php) line(57,62)
    OPCODE                             OP1                                OP2                                RESULT                             EXTENDED                           
    ZEND_FETCH_OBJ_R                   "ifoo"                             #var0                                                                 
    ZEND_ASSIGN                        $b                                 #var0                                                                                                    
    ZEND_INIT_FCALL                    96                                 "zendump"                                                             1                                  
    ZEND_FETCH_THIS                                                                                          #var2                                                                 
    ZEND_SEND_VAR                      #var2                              1                                                                                                        
    ZEND_DO_ICALL                                                                                                                                                                  
    ZEND_INIT_FCALL                    80                                 "zendump_opcodes"                                                     0                                  
    ZEND_DO_ICALL                                                                                                                                                                  
    ZEND_RETURN                        $b                                                                                                                                          
    ZEND_RETURN                        null                                                                                                                                        
    2

    这里描述匿名方法$bcl2为A::{closure},所以这里可以通过指向类A对象实例的$this获取类A的私有变量ifoo。

    2)bindTo()

    使用同上的方法查看官方示例代码的zendump情况:

    class B {
        function __construct($val) {
            $this->val = $val;
        }
        function getClosure() {
            return function() {
                $val = $this->val;
                zendump($this);
                zendump_opcodes();
                return $val; 
            };
        }
    }
    $ob1 = new B(1);
    $ob2 = new B(2);
        
    $cl = $ob1->getClosure();
    echo $cl(), "
    ";
    $cl = $cl->bindTo($ob2);
    echo $cl(), "
    ";

    1)先看第一次执行匿名函数的情况:

    zval(0x7f28c4413290) -> object(B) addr(0x7f28c4456758) refcount(3) {
      properties(1) {
        "val" =>
        zval(0x7f28c4461ca0) : long(1)
      }
    }
    op_array("B::{closure}") {closure}() refcount(2) addr(0x7f28c4461f38) vars(1) T(5) filename(/var/www/html/index.php) line(76,81)
    OPCODE                             OP1                                OP2                                RESULT                             EXTENDED                           
    ZEND_FETCH_OBJ_R                   "val"                              #var0                                                                 
    ZEND_ASSIGN                        $val                               #var0                                                                                                    
    ZEND_INIT_FCALL                    96                                 "zendump"                                                             1                                  
    ZEND_FETCH_THIS                                                                                          #var2                                                                 
    ZEND_SEND_VAR                      #var2                              1                                                                                                        
    ZEND_DO_ICALL                                                                                                                                                                  
    ZEND_INIT_FCALL                    80                                 "zendump_opcodes"                                                     0                                  
    ZEND_DO_ICALL                                                                                                                                                                  
    ZEND_RETURN                        $val                                                                                                                                        
    ZEND_RETURN                        null                                                                                                                                        
    1

    这里匿名函数$cl内部的$this是B(1)实例,匿名函数被描述为B::{closure};

    2)再看看bindTo()另一个类B实例后的变化:

    zval(0x7f28c4413290) -> object(B) addr(0x7f28c44566e0) refcount(3) {
      properties(1) {
        "val" =>
        zval(0x7f28c4461de0) : long(2)
      }
    }
    op_array("B::{closure}") {closure}() refcount(2) addr(0x7f28c4462078) vars(1) T(5) filename(/var/www/html/index.php) line(76,81)
    OPCODE                             OP1                                OP2                                RESULT                             EXTENDED                           
    ZEND_FETCH_OBJ_R                   "val"                              #var0                                                                 
    ZEND_ASSIGN                        $val                               #var0                                                                                                    
    ZEND_INIT_FCALL                    96                                 "zendump"                                                             1                                  
    ZEND_FETCH_THIS                                                                                          #var2                                                                 
    ZEND_SEND_VAR                      #var2                              1                                                                                                        
    ZEND_DO_ICALL                                                                                                                                                                  
    ZEND_INIT_FCALL                    80                                 "zendump_opcodes"                                                     0                                  
    ZEND_DO_ICALL                                                                                                                                                                  
    ZEND_RETURN                        $val                                                                                                                                        
    ZEND_RETURN                        null                                                                                                                                        
    2

    这里$this的内容发生了变化,变成了B(2)实例,匿名函数仍然被描述为B::{closure}。

    原理还是没有理清楚,不过可以通过A::{closure}这种描述以及$this的实例内容的变化,在一定程度上帮助理解bind()和bindTo()的表现。

  • 相关阅读:
    HDU4507 吉哥系列故事――恨7不成妻(数位dp)
    UCF Local Programming Contest 2017 G题(dp)
    ICPC Latin American Regional Contests 2019 I题
    UCF Local Programming Contest 2017 H题(区间dp)
    HDU2089 不要62
    AcWing1084 数字游戏II(数位dp)
    UCF Local Programming Contest 2017 F题(最短路)
    Google Code Jam 2019 Round 1A Pylons(爆搜+贪心)
    AcWing1083 Windy数(数位dp)
    Vue
  • 原文地址:https://www.cnblogs.com/ling-diary/p/9121398.html
Copyright © 2011-2022 走看看