深入了解PHP闭包的使用以及实现
一、介绍
匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值。当然,也有其它应用的情况。
二、使用场景
1、动态调用静态类的时候
<?php
class test
{
public static function getinfo()
{
var_dump(func_get_args());
}
} call_user_func(array('test', 'getinfo'), 'hello world');
2、在callback函数中使用
<?php
//eg array_walk array_map preg_replace_callback etc echo preg_replace_callback('~-([a-z])~', function ($match) {
return strtoupper($match[1]);
}, 'hello-world');
// 输出 helloWorld
?>
3、赋值给一个普通的变量
<?php
$greet = function($name)
{
printf("Hello %s\r\n", $name);
}; $greet('World');
$greet('PHP');
?>
4、使用use从父域中继承
<?php
$message = 'hello'; // 继承 $message
$example = function () use ($message) {
var_dump($message);
};
echo $example(); // Inherit by-reference
$example = function () use (&$message) {
var_dump($message);
};
echo $example(); // The changed value in the parent scope
// is reflected inside the function call
$message = 'world';
echo $example();
5、传递参数
<?php
$example = function ($arg) use ($message) {
var_dump($arg . ' ' . $message);
};
$example("hello");
6、OO中的使用
<?php class factory{
private $_factory;
public function set($id,$value){
$this->_factory[$id] = $value;
} public function get($id){
$value = $this->_factory[$id];
return $value();
}
}
class User{
private $_username;
function __construct($username="") {
$this->_username = $username;
}
function getUserName(){
return $this->_username;
}
} $factory = new factory(); $factory->set("zhangsan",function(){
return new User('张三');
});
$factory->set("lisi",function(){
return new User("李四");
});
echo $factory->get("zhangsan")->getUserName();
echo $factory->get("lisi")->getUserName();
7、函数中的调用
<?php function call($callback){
$callback();
}
call(function() {
var_dump('hell world');
});
三、分析
第一个例子
[root@chenpingzhao www]# php-cgi -dvld.active=1 k1.php
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename: /data/www/k1.php
function name: (null)
number of ops: 11
compiled vars: none
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
4 0 E > EXT_STMT
11 1 EXT_STMT
2 EXT_FCALL_BEGIN
3 INIT_ARRAY ~0 'foo'
4 ADD_ARRAY_ELEMENT ~0 'func'
5 SEND_VAL ~0
6 INIT_ARRAY ~0 'hello+world'
7 SEND_VAL ~0
8 DO_FCALL 2 'call_user_func_array'
9 EXT_FCALL_END
12 10 > RETURN 1 branch: # 0; line: 4- 12; sop: 0; eop: 10; out1: -2
path #1: 0,
Class foo:
Function func:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename: /data/www/k1.php
function name: func
number of ops: 11
compiled vars: none
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
5 0 E > EXT_NOP
7 1 EXT_STMT
2 EXT_FCALL_BEGIN
3 EXT_FCALL_BEGIN
4 DO_FCALL 0 $0 'func_get_args'
5 EXT_FCALL_END
6 SEND_VAR_NO_REF 6 $0
7 DO_FCALL 1 'var_dump'
8 EXT_FCALL_END
8 9 EXT_STMT
10 > RETURN null branch: # 0; line: 5- 8; sop: 0; eop: 10; out1: -2
path #1: 0,
End of function func End of class foo. X-Powered-By: PHP/5.5.23
Content-type: text/html
没有这个DECLARE_LAMBDA_FUNCTION 这个步骤,说明这个和自己实现的闭包是两码事
第三个例子比较简单,我们分析一下好了
[root@localhost www]# php-cgi -dvld.active=1 k3.php
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename: /data/www/k3.php
function name: (null)
number of ops: 17
compiled vars: !0 = $greet
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > EXT_STMT
1 DECLARE_LAMBDA_FUNCTION '%00%7Bclosure%7D%2Fdata%2Fwww%2Fk3.php0xa67ff017'
5 2 ASSIGN !0, ~0
7 3 EXT_STMT
4 INIT_FCALL_BY_NAME !0
5 EXT_FCALL_BEGIN
6 SEND_VAL 'World'
7 DO_FCALL_BY_NAME 1
8 EXT_FCALL_END
8 9 EXT_STMT
10 INIT_FCALL_BY_NAME !0
11 EXT_FCALL_BEGIN
12 SEND_VAL 'PHP'
13 DO_FCALL_BY_NAME 1
14 EXT_FCALL_END
10 15 EXT_STMT
16 > RETURN 1 branch: # 0; line: 2- 10; sop: 0; eop: 16; out1: -2
path #1: 0,
Function %00%7Bclosure%7D%2Fdata%2Fwww%2Fk3.php0xa67ff01:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename: /data/www/k3.php
function name: {closure}
number of ops: 10
compiled vars: !0 = $name
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > EXT_NOP
1 RECV !0
4 2 EXT_STMT
3 EXT_FCALL_BEGIN
4 SEND_VAL 'Hello+%25s%0D%0A'
5 SEND_VAR !0
6 DO_FCALL 2 'printf'
7 EXT_FCALL_END
5 8 EXT_STMT
9 > RETURN null branch: # 0; line: 2- 5; sop: 0; eop: 9; out1: -2
path #1: 0,
End of function %00%7Bclosure%7D%2Fdata%2Fwww%2Fk3.php0xa67ff01 X-Powered-By: PHP/5.5.23
Content-type: text/html Hello World
Hello PHP
让我看一下底层是怎么实现的:Zend/zend_vm_execute.h
其实用的应该是LAMBDA_FUNCTION
static int ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_function *op_array;
int closure_is_static, closure_is_being_defined_inside_static_context; SAVE_OPLINE(); if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(opline->op1.zv), \
Z_STRLEN_P(opline->op1.zv), Z_HASH_P(opline->op1.zv), (void *) &op_array) == FAILURE) ||
UNEXPECTED(op_array->type != ZEND_USER_FUNCTION)) {
zend_error_noreturn(E_ERROR, "Base lambda function for closure not found");
} closure_is_static = op_array->common.fn_flags & ZEND_ACC_STATIC;
closure_is_being_defined_inside_static_context = EX(prev_execute_data) &&\
EX(prev_execute_data)->function_state.function->common.fn_flags & ZEND_ACC_STATIC;
if (closure_is_static || closure_is_being_defined_inside_static_context) {
//关键函数在这里
zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(called_scope), NULL TSRMLS_CC);
} else {
zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(scope), EG(This) TSRMLS_CC);
} CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
我们再看一下zend_create_closure具体是怎么实现的:Zend/zend_closures.c
ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zval \
*this_ptr TSRMLS_DC) /* {{{ */
{
zend_closure *closure; object_init_ex(res, zend_ce_closure);//初始化 closure = (zend_closure *)zend_object_store_get_object(res TSRMLS_CC); closure->func = *func;
closure->func.common.prototype = NULL;
closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; if ((scope == NULL) && (this_ptr != NULL)) {
/* use dummy scope if we're binding an object without specifying a scope */
/* maybe it would be better to create one for this purpose */
scope = zend_ce_closure;
} if (closure->func.type == ZEND_USER_FUNCTION) {//用户自定函数
if (closure->func.op_array.static_variables) {
HashTable *static_variables = closure->func.op_array.static_variables;
//hash表,申请内存、初始化
ALLOC_HASHTABLE(closure->func.op_array.static_variables);
zend_hash_init(closure->func.op_array.static_variables, \
zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
//对变量赋值 zval_copy_static_var 这儿是静态变量
zend_hash_apply_with_arguments(static_variables TSRMLS_CC,\
(apply_func_args_t)zval_copy_static_var, 1, closure->func.op_array.static_variables);
}
closure->func.op_array.run_time_cache = NULL;
(*closure->func.op_array.refcount)++;
} else {
//绑定错误
/* verify that we aren't binding internal function to a wrong scope */
if(func->common.scope != NULL) {
if(scope && !instanceof_function(scope, func->common.scope TSRMLS_CC)) {
zend_error(E_WARNING, "Cannot bind function %s::%s to scope class %s",\
func->common.scope->name, func->common.function_name, scope->name);
scope = NULL;
}
if(scope && this_ptr && (func->common.fn_flags & ZEND_ACC_STATIC) == 0 &&
!instanceof_function(Z_OBJCE_P(this_ptr), closure->func.common.scope TSRMLS_CC)) {
zend_error(E_WARNING, "Cannot bind function %s::%s to object of class %s",\
func->common.scope->name, func->common.function_name, Z_OBJCE_P(this_ptr)->name);
scope = NULL;
this_ptr = NULL;
}
} else {
/* if it's a free function, we won't set scope & this since they're meaningless */
this_ptr = NULL;
scope = NULL;
}
} closure->this_ptr = NULL;
/* Invariants:
* If the closure is unscoped, it has no bound object.
* The the closure is scoped, it's either static or it's bound */
closure->func.common.scope = scope;
if (scope) {
closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
if (this_ptr && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
closure->this_ptr = this_ptr;
Z_ADDREF_P(this_ptr);
} else {
closure->func.common.fn_flags |= ZEND_ACC_STATIC;
}
}
}
/* }}} */
下面我看看变量是如何赋值的:zend/zend_variables.c
ZEND_API int zval_copy_static_var(zval **p TSRMLS_DC, int num_args, va_list args, \
zend_hash_key *key) /* {{{ */
{
HashTable *target = va_arg(args, HashTable*);//定一个一个hashtable
zend_bool is_ref;//是否为引用变量
zval *tmp; if (Z_TYPE_PP(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) {//变量作用域 use的时候
is_ref = Z_TYPE_PP(p) & IS_LEXICAL_REF; if (!EG(active_symbol_table)) {
zend_rebuild_symbol_table(TSRMLS_C);
}
if (zend_hash_quick_find(EG(active_symbol_table), key->arKey, key->nKeyLength, \
key->h, (void **) &p) == FAILURE) {
if (is_ref) {
ALLOC_INIT_ZVAL(tmp);
Z_SET_ISREF_P(tmp);
zend_hash_quick_add(EG(active_symbol_table), key->arKey, key->nKeyLength, \
key->h, &tmp, sizeof(zval*), (void**)&p);
} else {
tmp = EG(uninitialized_zval_ptr);
zend_error(E_NOTICE,"Undefined variable: %s", key->arKey);
}
} else {
if (is_ref) {
SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
tmp = *p;
} else if (Z_ISREF_PP(p)) {
ALLOC_INIT_ZVAL(tmp);
ZVAL_COPY_VALUE(tmp, *p);
zval_copy_ctor(tmp);
Z_SET_REFCOUNT_P(tmp, 0);
Z_UNSET_ISREF_P(tmp);
} else {
tmp = *p;
}
}
} else {
tmp = *p;
}
if (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, &tmp, \
sizeof(zval*), NULL) == SUCCESS) {
Z_ADDREF_P(tmp);
}
return ZEND_HASH_APPLY_KEEP;
}
/* }}} */
参考:http://php.net/manual/zh/function.call-user-func-array.php
深入了解PHP闭包的使用以及实现的更多相关文章
- 《Web 前端面试指南》1、JavaScript 闭包深入浅出
闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...
- 干货分享:让你分分钟学会 JS 闭包
闭包,是 Javascript 比较重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,很难从定义去理解它.因此,本文不会对闭包的概念进行大篇幅描述 ...
- 深入浅出JavaScript之闭包(Closure)
闭包(closure)是掌握Javascript从人门到深入一个非常重要的门槛,它是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.下面写下我的学习笔记~ 闭包-无处不 ...
- javascript之闭包理解以及应用场景
半个月没写博文了,最近一直在弄小程序,感觉也没啥好写的. 之前读了js权威指南,也写了篇博文,但是实话实说当初看闭包确实还是一头雾水.现在时隔一个多月(当然这一段时间还是一直有在看闭包的相关知识)理解 ...
- js闭包 和 prototype
function test(){ var p=200; function q(){ return p++; } return q; } var s = test(); alert(s()); aler ...
- js闭包for循环总是只执行最后一个值得解决方法
<style> li{ list-style: none;width:40px;height: 40px;text-align:center;line-height: 40px;curso ...
- JavaScript学习笔记(二)——闭包、IIFE、apply、函数与对象
一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div, ...
- 带你一分钟理解闭包--js面向对象编程
上一篇<简单粗暴地理解js原型链--js面向对象编程>没想到能攒到这么多赞,实属意外.分享是个好事情,尤其是分享自己的学习感悟.所以网上关于原型链.闭包.作用域等文章多如牛毛,很多文章写得 ...
- 如何设计一门语言(七)——闭包、lambda和interface
人们都很喜欢讨论闭包这个概念.其实这个概念对于写代码来讲一点用都没有,写代码只需要掌握好lambda表达式和class+interface的语义就行了.基本上只有在写编译器和虚拟机的时候才需要管什么是 ...
- JavaScript 闭包深入浅出
闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...
随机推荐
- tomcat启动时间修改
Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds. If the server requires ...
- 用于Web开发的8 个最好的跨平台编辑器
1) Best Cross Platform IDE - Brackets Brackets是一个在前端Web开发和设计人员中最流行的开放源码IDE/代码编辑器之一.它拥有一些实用工具能够将HTML ...
- scala 学习笔记七 基于类型的模式匹配
1.介绍 Scala 提供了强大的模式匹配机制,应用也非常广泛. 一个模式匹配包含了一系列备选项,每个都开始于关键字 case.每个备选项都包含了一个模式及一到多个表达式.箭头符号 => 隔开了 ...
- scala里的模式匹配和Case Class
模式匹配的简介 scala语言里的模式匹配可以看作是java语言中switch语句的改进. 模式匹配的类型 包括:常量模式.变量模式.构造器模式.序列模式.元组模式以及变量绑定模式等. 常量模式匹配 ...
- Linq-分页查询
var list = from s in db.t_address select s; & userid != null) { list = list.Where(v => v.user ...
- 我对REST的理解
1:rest的由来 REST即表述性状态传递(英文:Representational State Transfer,简称REST) 通俗点说:资源在网络中以某种表现形式进行状态转移. 源于REST之父 ...
- 【android】模拟点击某个指定坐标作用在View上
/** * 模拟点击某个指定坐标作用在View上 * @param view * @param x * @param y */ public void clickView(View view,floa ...
- Session 共享(Custom模式)By Memcached(原创)
1.web.config配置: <machineKey decryptionKey="FD69B2EB9A11E3063518F1932E314E4AA1577BF0B824F369& ...
- web.xml不同版本的头
web.xml v2.3 <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web- ...
- ios backgroundColor
loginView.backgroundColor=[UIColorcolorWithHue:0saturation:0brightness:0.9alpha:0.85]; 可随意调