由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 相关文章推荐
PHP5/ZendEngine2的改进
Oct 09 PHP
php中截取字符串支持utf-8
Jan 18 PHP
php adodb连接mssql解决乱码问题
Jun 12 PHP
PHP之APC缓存详细介绍 apc模块安装
Jan 13 PHP
PHP弹出对话框技巧详细解读
Sep 26 PHP
php实现的递归提成方案实例
Nov 14 PHP
php ajax异步读取rss文档数据
Mar 29 PHP
thinkphp表单上传文件并将文件路径保存到数据库中
Jul 28 PHP
万能的php分页类
Jul 06 PHP
微信公众号开发之获取位置信息php代码
Jun 13 PHP
PDO::errorInfo讲解
Jan 28 PHP
PHP正则表达式处理函数(PCRE 函数)实例小结
May 09 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
实现 win2003 下 mysql 数据库每天自动备份
2006/12/06 PHP
php简单获取文件扩展名的方法
2015/03/24 PHP
php实现网页上一页下一页翻页过程详解
2019/06/28 PHP
jQuery对象和DOM对象使用说明
2010/06/25 Javascript
使用jquery获取网页中图片高度的两种方法
2013/09/26 Javascript
javascript正则表达式使用replace()替换手机号的方法
2015/01/19 Javascript
jQuery+html5实现div弹出层并遮罩背景
2015/04/15 Javascript
js实现文字垂直滚动和鼠标悬停效果
2015/12/31 Javascript
jquery ajax结合thinkphp的getjson实现跨域的方法
2016/06/06 Javascript
nodejs和php实现图片访问实时处理
2017/01/05 NodeJs
laravel5.3 vue 实现收藏夹功能实例详解
2018/01/21 Javascript
使用Vue-cli 3.0搭建Vue项目的方法
2018/06/07 Javascript
浅谈开发eslint规则
2018/10/01 Javascript
vue中用 async/await 来处理异步操作
2020/07/18 Javascript
解析Python中的二进制位运算符
2015/05/13 Python
Python实现截屏的函数
2015/07/26 Python
python获取指定日期范围内的每一天,每个月,每季度的方法
2019/08/08 Python
Python 转换文本编码实现解析
2019/08/27 Python
Python TCPServer 多线程多客户端通信的实现
2019/12/31 Python
使用TFRecord存取多个数据案例
2020/02/17 Python
通过实例了解Python异常处理机制底层实现
2020/07/23 Python
Python自动化xpath实现自动抢票抢货
2020/09/19 Python
Python urlopen()参数代码示例解析
2020/12/10 Python
pycharm配置安装autopep8自动规范代码的实现
2021/03/02 Python
aden + anais英国官网:美国婴儿贴身用品品牌
2019/09/08 全球购物
大学生优秀团员事迹材料
2014/01/30 职场文书
高中历史教学反思
2014/02/08 职场文书
工程管理英文求职信
2014/03/18 职场文书
学校综治宣传月活动总结
2014/07/02 职场文书
社区文艺活动方案
2014/08/19 职场文书
规范化管理年活动总结
2014/08/29 职场文书
地道战观后感2000字
2015/06/04 职场文书
导游词之泉州崇武古城
2019/12/20 职场文书
详解运行Python的神器Jupyter Notebook
2021/06/03 Python
python程序的组织结构详解
2021/12/06 Python
世界十大动漫制作公司排行榜,迪士尼上榜,第二是美国代表性文化符
2022/03/18 欧美动漫