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
總結
以上是生活随笔為你收集整理的PHP学习笔记(一):理解匿名函数与Closure的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Winform 自定义窗体皮肤组件
- 下一篇: redis之sorted sets类型及