由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上的memcache和memcached两个pecl库
Mar 29 PHP
PHP JSON 数据解析代码
May 26 PHP
PHP获取本周第一天和最后一天示例代码
Feb 24 PHP
PHP JSON出错:Cannot use object of type stdClass as array解决方法
Aug 16 PHP
php中使用array_filter()函数过滤空数组的实现代码
Aug 19 PHP
在Windows XP下安装Apache+MySQL+PHP环境
Feb 22 PHP
PHP获取QQ达人QQ信息的方法
Mar 05 PHP
PHP实现简单爬虫的方法
Jul 29 PHP
PHP代码维护,重构变困难的4种原因分析
Jan 25 PHP
PHP身份证校验码计算方法
Aug 10 PHP
php转换上传word文件为PDF的方法【基于COM组件】
Jun 10 PHP
Laravel 读取 config 下的数据方法
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
咖啡知识 咖啡养豆要养多久 排气又是什么
2021/03/06 新手入门
PHP HTML代码串 截取实现代码
2009/06/29 PHP
php中获取远程客户端的真实ip地址的方法
2011/08/03 PHP
PHP分多步骤填写发布信息的简单方法实例代码
2012/09/23 PHP
遍历指定目录,并存储目录内所有文件属性信息的php代码
2016/10/28 PHP
TP5框架页面跳转样式操作示例
2020/04/05 PHP
在html页面中包含共享页面的方法
2008/10/24 Javascript
JS中confirm,alert,prompt函数使用区别分析
2010/04/01 Javascript
页面载入结束自动调用js函数示例
2013/09/23 Javascript
jQuery实现可编辑的表格实例讲解(2)
2015/09/17 Javascript
Bootstrap模态窗口源码解析
2017/02/08 Javascript
bootstrap suggest下拉框使用详解
2017/04/10 Javascript
浅谈针对Vue相同路由不同参数的刷新问题
2018/09/29 Javascript
Vue路由管理器Vue-router的使用方法详解
2020/02/05 Javascript
解决vue 退出动画无效的问题
2020/08/09 Javascript
JS实现购物车基本功能
2020/11/08 Javascript
[38:27]完美世界DOTA2联赛PWL S2 Forest vs FTD.C 第二场 11.26
2020/11/30 DOTA
Python实现批量把SVG格式转成png、pdf格式的代码分享
2014/08/21 Python
利用Python的装饰器解决Bottle框架中用户验证问题
2015/04/24 Python
python实现图片上添加图片
2019/11/26 Python
Python日期格式和字符串格式相互转换的方法
2020/02/18 Python
PyCharm最新激活码PyCharm2020.2.3有效
2020/11/18 Python
HTML5进阶段内联标签汇总(小篇)
2016/07/13 HTML / CSS
阿迪达斯德国官方网站:adidas德国
2017/07/12 全球购物
荷兰在线钓鱼商店:Raven
2019/06/26 全球购物
Ibatis如何调用存储过程
2015/05/15 面试题
电子商务自荐书范文
2014/01/04 职场文书
会计与出纳自荐书范文
2014/03/16 职场文书
学校督导评估方案
2014/06/10 职场文书
革命英雄事迹演讲稿
2014/09/13 职场文书
工作作风建设心得体会
2014/10/22 职场文书
求职自我评价怎么写
2015/03/09 职场文书
地道战观后感300字
2015/06/04 职场文书
大学生心理健康教育心得体会
2016/01/12 职场文书
导游词之重庆渣滓洞
2020/01/08 职场文书
HTML页面中使两个div并排显示的实现
2022/05/15 HTML / CSS