由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 相关文章推荐
解决163/sohu/sina不能够收到PHP MAIL函数发出邮件的问题
Mar 13 PHP
谷歌音乐搜索栏的提示功能php修正代码
May 09 PHP
VIM中设置php自动缩进为4个空格的方法详解
Jun 14 PHP
php中\r \r\n \t的区别示例介绍
Feb 08 PHP
PHP实现远程下载文件到本地
May 17 PHP
php使用ffmpeg获取视频信息并截图的实现方法
May 03 PHP
PHP延迟静态绑定的深入讲解
Apr 02 PHP
PHP bin2hex()函数基础实例讲解
Feb 11 PHP
PHP convert_cyr_string()函数讲解
Feb 13 PHP
Thinkphp5.0框架视图view的循环标签用法示例
Oct 12 PHP
解决Laravel5.2 Auth认证退出失效的问题
Oct 14 PHP
详解no input file specified 三种解决方法
Nov 29 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.MVC的模板标签系统(三)
2006/09/05 PHP
php过滤XSS攻击的函数
2013/11/12 PHP
基于Laravel5.4实现多字段登录功能方法示例
2017/08/11 PHP
判断iframe是否加载完成的完美方法
2010/01/07 Javascript
非jQuery实现照片散落桌子上,单击放大的LightBox效果
2014/11/28 Javascript
浅谈js的setInterval事件
2014/12/05 Javascript
jquery使用animate方法实现控制元素移动
2015/03/27 Javascript
基于MVC5和Bootstrap的jQuery TreeView树形控件(一)之数据支持json字符串、list集合
2016/08/11 Javascript
Bootstrop实现多级下拉菜单功能
2016/11/24 Javascript
搭建简单的nodejs http服务器详解
2017/03/09 NodeJs
使用vue-cli打包过程中的步骤以及问题的解决
2018/05/08 Javascript
详解小程序缓存插件(mrc)
2018/08/17 Javascript
js的Object.assign用法示例分析
2020/03/05 Javascript
js中实现继承的五种方法
2021/01/25 Javascript
python BeautifulSoup设置页面编码的方法
2015/04/03 Python
Python的Flask框架中web表单的教程
2015/04/20 Python
Python算法输出1-9数组形成的结果为100的所有运算式
2017/11/03 Python
python opencv摄像头的简单应用
2019/06/06 Python
python如何读取bin文件并下发串口
2019/07/05 Python
python爬虫 2019中国好声音评论爬取过程解析
2019/08/26 Python
flask 框架操作MySQL数据库简单示例
2020/02/02 Python
jupyter notebook 多环境conda kernel配置方式
2020/04/10 Python
Canvas与Image互相转换示例代码
2013/08/09 HTML / CSS
HTML块级标签汇总(小篇)
2016/07/13 HTML / CSS
canvas实现扭蛋机动画效果的示例代码
2018/10/17 HTML / CSS
通过canvas转换颜色为RGBA格式及性能问题的解决
2019/11/22 HTML / CSS
加拿大票务网站:Ticketmaster加拿大
2017/07/17 全球购物
大学生应聘自荐信
2013/10/11 职场文书
儿科护士自我鉴定
2013/10/14 职场文书
前厅收银主管岗位职责
2014/02/04 职场文书
《问银河》教学反思
2014/02/19 职场文书
简历里的自我评价范文
2014/02/24 职场文书
文体活动实施方案
2014/03/27 职场文书
优秀毕业生求职信
2014/06/05 职场文书
护士医德医风自我评价
2014/09/15 职场文书
单位领导婚礼致辞
2015/07/28 职场文书