由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开发需要注意的安全问题
Sep 01 PHP
PHP中防止SQL注入实现代码
Feb 19 PHP
php jsonp单引号转义
Nov 23 PHP
php实现的SESSION类
Dec 02 PHP
PHP调用MySQL存储过程并返回值的方法
Dec 26 PHP
Codeigniter通过SimpleXML将xml转换成对象的方法
Mar 19 PHP
详解WordPress中的头像缓存和代理中的缓存更新方法
Mar 01 PHP
php基于mcrypt_encrypt和mcrypt_decrypt实现字符串加密解密的方法
Jul 12 PHP
laravel通过创建自定义artisan make命令来新建类文件详解
Aug 17 PHP
php实现的支付宝网页支付功能示例【基于TP5框架】
Sep 16 PHP
如何在PHP中生成随机数
Jun 04 PHP
php7连接MySQL实现简易查询程序的方法
Oct 13 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边学边教》(02.Apache+PHP环境配置――下篇)
2006/12/13 PHP
php全排列递归算法代码
2012/10/09 PHP
PHP文件缓存类示例分享
2015/01/30 PHP
JQuery 初体验(建议学习jquery)
2009/04/25 Javascript
IE bug table元素的innerHTML
2010/01/11 Javascript
Tinymce+jQuery.Validation使用产生的BUG
2010/03/29 Javascript
extJs 下拉框联动实现代码
2010/04/09 Javascript
javascript奇异的arguments分析
2010/10/20 Javascript
JS分割字符串并放入数组的函数
2011/07/04 Javascript
JQuery获取文本框中字符长度的代码
2011/09/29 Javascript
用jquery写的菜单从左往右滑动出现
2014/04/11 Javascript
Jquery实现Div上下移动示例
2014/04/23 Javascript
jQuery+AJAX实现无刷新下拉加载更多
2015/07/03 Javascript
javascript随机抽取0-100之间不重复的10个数
2016/02/25 Javascript
深入理解Ajax的get和post请求
2016/06/02 Javascript
一个仿微博登陆邮箱提示框js开发案例
2016/07/28 Javascript
Vue.js学习记录之在元素与template中使用v-if指令实例
2017/06/27 Javascript
Vue.js中extend选项和delimiters选项的比较
2017/07/17 Javascript
node.js通过axios实现网络请求的方法
2018/03/05 Javascript
[01:14]辉夜杯战队访谈宣传片—NEWBEE.Y
2015/12/26 DOTA
《Python之禅》中对于Python编程过程中的一些建议
2015/04/03 Python
Python标准库之collections包的使用教程
2017/04/27 Python
Python实现朴素贝叶斯分类器的方法详解
2018/07/04 Python
python中pip的使用和修改下载源的方法
2019/07/08 Python
Python 静态方法和类方法实例分析
2019/11/21 Python
Pycharm如何运行.py文件的方法步骤
2020/03/03 Python
Python同时迭代多个序列的方法
2020/07/28 Python
详解selenium + chromedriver 被反爬的解决方法
2020/10/28 Python
8款使用 CSS3 实现超炫的 Loading(加载)的动画效果
2015/03/17 HTML / CSS
澳大利亚头发和美容产品购物网站:OZ Hair & Beauty
2020/03/27 全球购物
WEB控件及HTML服务端控件能否调用客户端方法?如果能,请解释如何调用?
2015/08/25 面试题
面向对象概念面试题(.NET)
2016/11/04 面试题
Tomcat的缺省是多少,怎么修改
2014/04/09 面试题
自荐信格式的六要素
2013/09/21 职场文书
mysql timestamp比较查询遇到的坑及解决
2021/11/27 MySQL