由php的call_user_func传reference引发的思考


Posted in PHP onJuly 23, 2010

问题的提出
网友bercmisir在院内留言,针对php手册中的call_user_func函数的文档一事,大致如下:
http://php.net/manual/en/function.call-user-func.php

其中parameter下有这样一句话:
Note: Note that the parameters for call_user_func() are not passed by reference.

简单地翻译一下,是说这个函数的参数是不能依靠引用来传递的。

还有一个例子:

error_reporting(E_ALL); 
function increment(&$var) 
{ 
$var++; 
} 
$a = 0; 
call_user_func('increment', $a); 
echo $a."\n"; 
call_user_func_array('increment', array(&$a)); // You can use this instead before PHP 5.3 
echo $a."\n"; 
?>

输出是:
0
1

而网友bercmisir的问题在于:
call_user_func('increment', $a);输出是0,而call_user_func('increment', &$a);却输出是1,明明说不能依靠引用来传递。

寻根溯源
然后再进一步寻根溯源,这个Note的信息其实是http://bugs.php.net/bug.php?id=24931这个bug中最后处理的结果。
并且在call_user_func('increment', &$a);虽然输出了1的结果,但一般情况下,会有一个警告信息:Deprecated: Call-time pass-by-reference has been deprecated。

这是什么原因呢?
先看一个例子:

error_reporting(E_ALL); 
function increment(&$var) 
{ 
$var++; 
} 
$x = 1; 
increment($x); 
echo $x; 
?>

结果为2,并且没有类似expected to be a reference, value given的警告信息,相反地,如果将第8行代码修改为&$x,将得到一个废除警告。从而得以验证,其实PHP在传递过程中,变量会根据形参需要的到底是引用还是值来自行决定传输的是引用还是值,并不需要显式地传递(相反显式传递是即将被废除的)。

继续深入
http://www.php.net/manual/en/language.references.pass.php
在php手册中,介绍引用的传递一节,在中间位置有一个Note说到:在函数调用时是不需要传引用的(也就是上节所说的显式调用),在5.3中如果显式调用会出来一个废除警告。

分析源码
有人说:在php中写入,everything is a reference。
查阅php源码,在./Zend/zend_compile.c的1579行有函数定义zend_do_pass_param。(php5.2.13)

其中有这样一句判断:
if (original_op == ZEND_SEND_REF && !CG(allow_call_time_pass_reference)) {打印废除警告。}
大概意思就是说,在传递的是引用,并且php.ini的allow_call_time_pass_reference为否的话,打印警告。
再看zend_do_pass_param使用的地方,可以发现是在parser阶段时,根据参数ZVAL结构体中元素的定义,来传递到底是var还是value还是reference。(php5.2.13 ./Zend/zend_language_parser.y/c 451/3593)

结论
引用其实类似linux里的文件硬链接一样,但和C语言中的指针是不相同的,在parser阶段php会根据上下文环境自行判断是传引用还是值。而本文所提到的call_user_function并不会自行判断传的是引用还是值。所以前面的例子call_user_function在传值的时候不管用,而在传引用的时候得出了正确结果(但其实还有一个废除警告)。

PHP 相关文章推荐
php echo 输出字符串函数详解
May 13 PHP
用穿越火线快速入门php面向对象
Feb 22 PHP
PHP可逆加密/解密函数分享
Sep 25 PHP
thinkphp普通查询与表达式查询实例分析
Nov 24 PHP
PHP中COOKIES使用示例
Jul 26 PHP
PHP的Yii框架的基本使用示例
Aug 21 PHP
讲解WordPress中用于获取评论模板和搜索表单的PHP函数
Dec 28 PHP
详解PHP中的序列化、反序列化操作
Mar 21 PHP
Laravel下生成验证码的类
Nov 15 PHP
Laravel如何使用Redis共享Session
Feb 23 PHP
PHP实现获取ip地址的5种方法,以及插入用户登录日志操作示例
Feb 28 PHP
Laravel的加密解密与哈希实例讲解
Mar 24 PHP
Google Voice 短信发送接口PHP开源版(2010.5更新)
Jul 22 #PHP
PHP 飞信好友免费短信API接口开源版
Jul 22 #PHP
PHP计划任务之关闭浏览器后仍然继续执行的函数
Jul 22 #PHP
PHP垃圾回收机制简单说明
Jul 22 #PHP
PHP多线程抓取网页实现代码
Jul 22 #PHP
php上传文件的增强函数
Jul 21 #PHP
php 模拟POST|GET操作实现代码
Jul 20 #PHP
You might like
PHP封装的一个支持HTML、JS、PHP重定向的多功能跳转函数
2014/06/19 PHP
CI框架AR操作(数组形式)实现插入多条sql数据的方法
2016/05/18 PHP
PHP实现的防止跨站和xss攻击代码【来自阿里云】
2018/01/29 PHP
PHP写API输出的时用echo的原因详解
2019/04/28 PHP
fromCharCode和charCodeAt 方法
2006/12/27 Javascript
麦鸡的TAB切换功能结合了javascript和css
2007/12/17 Javascript
HTML 自动伸缩的表格Table js实现
2009/04/01 Javascript
javascript闭包的高级使用方法实例
2013/07/04 Javascript
jquery ajax post提交数据乱码
2013/11/05 Javascript
JavaScript图片放大技术(放大镜)实现代码分享
2013/11/14 Javascript
nodejs npm package.json中文文档
2014/09/04 NodeJs
JavaScript编程的单例设计模讲解
2015/11/10 Javascript
JS中递归函数
2016/06/17 Javascript
js前端实现多图图片上传预览的两个方法(推荐)
2016/11/18 Javascript
js字符串与Unicode编码互相转换
2017/05/17 Javascript
underscore之Chaining_动力节点Java学院整理
2017/07/10 Javascript
Vue使用NProgress进度条的方法
2019/09/21 Javascript
详解Vue 项目中的几个实用组件(ts)
2019/10/29 Javascript
JavaScript中的this原理及6种常见使用场景详解
2020/02/14 Javascript
vue实现输入框自动跳转功能
2020/05/20 Javascript
python字符串连接的N种方式总结
2014/09/17 Python
python搜索指定目录的方法
2015/04/29 Python
对python生成业务报表的实例详解
2019/02/03 Python
Python通过Manager方式实现多个无关联进程共享数据的实现
2019/11/07 Python
解决Python spyder显示不全df列和行的问题
2020/04/20 Python
草莓网化妆品澳大利亚站:Strawberrynet AU
2017/12/18 全球购物
马来西亚太阳镜、眼镜和隐形眼镜网上商店:Focus Point
2018/12/13 全球购物
竞聘医务工作人员的自我评价分享
2013/11/04 职场文书
预备党员入党思想汇报
2014/01/04 职场文书
矿泉水广告词
2014/03/20 职场文书
寒假家长评语大全
2014/04/16 职场文书
找工作求职信
2014/07/07 职场文书
不服从上级领导安排的检讨书
2014/09/14 职场文书
教师对照四风自我剖析材料
2014/09/30 职场文书
开会迟到检讨书范文
2015/05/06 职场文书
单位提档介绍信
2015/10/22 职场文书