由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实现的功能是显示8条基色色带
Oct 09 PHP
PHP写UltraEdit插件脚本实现方法
Dec 26 PHP
win2003服务器使用WPS的COM组件的一些问题解决方法
Jan 11 PHP
php打开远程文件的方法和风险及解决方法
Nov 12 PHP
页面利用渐进式JPEG来提升用户体验度
Dec 01 PHP
微信随机生成红包金额算法php版
Jul 21 PHP
PHP里面把16进制的图片数据显示在html的img标签上(实现方法)
May 02 PHP
PHP用户注册邮件激活账户的实现代码
May 31 PHP
php生出随机字符串
Jul 06 PHP
php mysql PDO 查询操作的实例详解
Sep 23 PHP
laravel配置Redis多个库的实现方法
Apr 10 PHP
php7性能提升的原因详解
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 程序员的调试技术小结
2009/11/15 PHP
PHP Zip压缩 在线对文件进行压缩的函数
2010/05/26 PHP
php使HTML标签自动补全闭合函数代码
2012/10/04 PHP
visual studio code 调试php方法(图文详解)
2017/09/15 PHP
用jquery ajax获取网站Alexa排名的代码
2009/12/12 Javascript
javascript iframe编程相关代码
2009/12/28 Javascript
常见JS效果之图片减速度滚动实现代码
2011/12/08 Javascript
JS获取地址栏参数的小例子
2013/08/23 Javascript
jquery如何判断某元素是否具备指定的样式
2013/11/05 Javascript
javascript生成随机数的方法
2014/05/16 Javascript
在Linux上用forever实现Node.js项目自启动
2014/07/09 Javascript
比例尺、缩略图、平移缩放之百度地图添加控件方法
2015/08/03 Javascript
AngularJs基于角色的前端访问控制的实现
2016/11/07 Javascript
vue微信分享出来的链接点开是首页问题的解决方法
2018/11/28 Javascript
解决layui checkbox 提交多个值的问题
2019/09/02 Javascript
Nodejs实现图片上传、压缩预览、定时删除功能
2019/10/25 NodeJs
小程序组件传值和引入sass的方法(使用vant Weapp组件库)
2020/11/24 Javascript
[05:04]完美世界携手游戏风云打造 卡尔工作室地图界面篇
2013/04/23 DOTA
wxPython事件驱动实例详解
2014/09/28 Python
Python字典创建 遍历 添加等实用基础操作技巧
2018/09/13 Python
python得到一个excel的全部sheet标签值方法
2018/12/10 Python
python requests.post带head和body的实例
2019/01/02 Python
简单了解Python3 bytes和str类型的区别和联系
2019/12/19 Python
pyecharts动态轨迹图的实现示例
2020/04/17 Python
pycharm激活方法到2099年(激活流程)
2020/09/22 Python
HTML5中drawImage用法分析
2014/12/01 HTML / CSS
马克华菲官方商城:Mark Fairwhale
2016/09/04 全球购物
英国电信商店:BT Shop
2019/12/17 全球购物
幼师专业求职推荐信
2013/11/08 职场文书
销售文员的岗位职责
2013/11/20 职场文书
农民致富事迹材料
2014/01/23 职场文书
公司新年寄语
2014/04/04 职场文书
《高尔基和他的儿子》教学反思
2014/04/09 职场文书
公司奖励通知
2015/04/21 职场文书
爱心捐书倡议书
2015/04/27 职场文书
MySQL生成千万测试数据以及遇到的问题
2022/08/05 MySQL