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)返回的函数名称是以‘\0’开头的全局唯一函数名称:

$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 '实际上生成的函数名称第一个字符是\0:','<br>';
var_dump($createFuncName);//string(9) "lambda_1"
var_dump($myFuncName);//string(8) "lambda_1"
//手动拼一个'\0'在前面就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(), "\n";//1
echo $bcl2(), "\n";//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(), "\n";
echo $bcl2(), "\n";
echo $closure(), "\n";
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(), "\n";
$cl = $cl->bindTo($ob2);
echo $cl(), "\n";

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()的表现。

转载于:https://www.cnblogs.com/ling-diary/p/9121398.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值