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相关推荐
- 初探swift语言的学习笔记(闭包 - 匿名函数或block块代码)
很多高级语言都支持匿名函数操作,在OC中的block也为大家所熟悉,然面在swift里好像是被重新作了一个定义,不叫匿名函数,或 block了,而叫闭包(closure).下面配合代码来理解一下swi ...
- 《Go语言圣经》学习笔记 第五章函数
<Go语言圣经>学习笔记 第五章 函数 目录 函数声明 递归 多返回值 匿名函数 可变参数 Deferred函数 Panic异常 Recover捕获异常 注:学习<Go语言圣经> ...
- 【Python学习笔记】第八章 函数
[Python学习笔记]第八章 函数 文章目录 [Python学习笔记]第八章 函数 前言 8.1 定义函数 8.2 调用函数 8.2.1 使用位置参数调用函数 8.2.2 使用关键字参数调用函数 8 ...
- Unity学习笔记:监听函数有什么卵用?(似乎就是从一件事过渡到另一件事?)
Unity学习笔记:监听函数有什么卵用?(似乎就是从一件事过渡到另一件事?) 个人学习经验,仅供参考,欢迎各位码友批评指正. 做项目敲代码时,一直不是很理解监听函数是个什么玩意. 按我目前的理解,说白 ...
- C语言学习笔记(8)函数
C语言学习笔记(8)函数 函数 函数主要是用于将解决复杂的编程问题分解为数个可以分开进行解决的小问题,通过函数分装,最后在拼连在一起.有利于多人协作开发. 函数同样需要先声明后调用,声明符号为 voi ...
- python 内置函数转list_python学习笔记11-python内置函数
python学习笔记11-python内置函数 一.查看python的函数介绍: 二.python内置函数 1.abs获取绝对值: 通过python官网查看absabs(x) Return the a ...
- Vue学习笔记进阶篇——Render函数
本文为转载,原文:Vue学习笔记进阶篇--Render函数 基础 Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML.然而在一些场景中,你真的需要 JavaScript 的完全编 ...
- 没有varselect这个函数_JavaScript学习笔记(四)-- 函数基础
函数基础 我们代码里面所说的函数和我们上学的时候学习的什么三角函数.二次函数之类的不是一个东西 函数的概念 对于 js 来说,函数就是把任意一段代码放在一个 盒子 里面 在我想要让这段代码执行的时候, ...
- oracle数据变化记录,学习笔记:Oracle伪列函数ora_rowscn 记录表中行数据的修改时间...
天萃荷净 Oracle数据库开发时使用伪列函数ora_rowscn查询出数据库表中行数据的修改时间 一.默认情况下 –创建t_orascn测试表 SQL> create table t_oras ...
最新文章
- WeApp-Workflow:基于Gulp 的“微信小程序”前端开发工作流
- mybatis_helloword(1)
- oracle数据库swap占用率高,Oracle数据库所在服务器swap严重
- 【鉴权/授权】一步一步实现一个简易JWT鉴权
- zsh of termux
- Ubuntu学习日记--Lesson3:vim编辑器使用和常用命令
- 使用epublib自动生成epub文件
- 09-01-28 自助装机
- C#实战009:Excel操作-删除指定的Excel工作表
- python内turtle库应用
- 智能送药小车解说(国二)
- 马毅:低维模型与深度模型的殊途同归(神经网络、压缩感知和低秩分解与补全)
- 黑马程序员-反射-constructor-feld类-Method-数组的反射-反射的作用
- 关于alpine如何制作JDK镜像
- hadoop 中各种概念解释记忆
- 【1】GAN在医学图像上的生成,今如何?
- Eclipse红叉报错
- HTTP协议抓包 【HTTP协议解析】
- COOX培训材料 — PMT(1.Phase)
- Linux ubuntu下C/C++开发工具安装和开发环境搭建(c/c++,CLion工具)