由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 相关文章推荐
vBulletin HACK----关于排版的两个HACK
Oct 09 PHP
php打造属于自己的MVC框架
Mar 07 PHP
利用PHP扩展vld查看PHP opcode操作步骤
Mar 04 PHP
php 判断数组是几维数组
Mar 20 PHP
php生成EAN_13标准条形码实例
Nov 13 PHP
php基于base64解码图片与加密图片还原实例
Nov 03 PHP
PHP中使用file_get_contents post数据代码例子
Feb 13 PHP
php实现将wav文件转换成图像文件并在页面中显示的方法
Apr 21 PHP
Yii框架实现邮箱激活的方法【数字签名】
Oct 18 PHP
php通过会话控制实现身份验证实例
Oct 18 PHP
php微信公众号开发(4)php实现自定义关键字回复
Dec 15 PHP
asp函数split()对应php函数explode()
Feb 27 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聊天室技术
2006/10/09 PHP
php 动态添加记录
2009/03/10 PHP
php实现Linux服务器木马排查及加固功能
2014/12/29 PHP
smarty模板引擎基础知识入门
2015/03/30 PHP
php eval函数一句话木马代码
2015/05/21 PHP
php实现QQ空间获取当前用户的用户名并生成图片
2015/07/25 PHP
phpstudy默认不支持64位php的解决方法
2017/02/20 PHP
js字符编码函数区别分析
2011/12/28 Javascript
JavaScript 图像动画的小demo
2012/05/23 Javascript
HTTP 304错误的详细讲解
2013/11/13 Javascript
前台js调用后台方法示例
2013/12/02 Javascript
toggle()隐藏问题的解决方法
2014/02/17 Javascript
基于jQuery实现复选框的全选 全不选 反选功能
2014/11/24 Javascript
JS实现控制表格行内容垂直对齐的方法
2015/03/30 Javascript
基于jQuery下拉选择框插件支持单选多选功能代码
2016/06/07 Javascript
Angular.JS学习之依赖注入$injector详析
2016/10/20 Javascript
JS取数字小数点后两位或n位的简单方法
2016/10/24 Javascript
JavaScript如何实现图片懒加载(lazyload) 提高用户体验(增强版)
2016/11/30 Javascript
详解ECharts使用心得总结
2016/12/06 Javascript
node.js基于express使用websocket的方法
2017/11/09 Javascript
微信小程序用户拒绝授权的处理方法详解
2019/09/20 Javascript
python解决字典中的值是列表问题的方法
2013/03/04 Python
python2.7 json 转换日期的处理的示例
2018/03/07 Python
python实现微信自动回复功能
2018/04/11 Python
python flask框架实现传数据到js的方法分析
2019/06/11 Python
PyQt5基本控件使用详解:单选按钮、复选框、下拉框
2019/08/05 Python
python快速排序的实现及运行时间比较
2019/11/22 Python
Python enumerate函数遍历数据对象组合过程解析
2019/12/11 Python
Pandas —— resample()重采样和asfreq()频度转换方式
2020/02/26 Python
韩国三星旗下的一家超市连锁店:Home Plus
2016/07/30 全球购物
海信商城:海信电视、科龙空调、容声冰箱官方专卖
2017/02/07 全球购物
一些PHP的面试题
2015/05/06 面试题
介绍一下Make? 为什么使用make
2016/07/31 面试题
银行实习生的自我评价
2013/12/09 职场文书
平安建设实施方案
2014/03/19 职场文书
Golang实现可重入锁的示例代码
2022/05/25 Golang