由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 相关文章推荐
Oracle 常见问题解答
Oct 09 PHP
简单易用的计数器(数据库)
Oct 09 PHP
杏林同学录(五)
Oct 09 PHP
数组与类使用PHP的可变变量名需要的注意的问题
Jun 20 PHP
浅析php数据类型转换
Jan 09 PHP
解决PhpMyAdmin中导入2M以上大文件限制的方法分享
Jun 06 PHP
学习php中的正则表达式
Aug 17 PHP
支付宝接口开发集成支付环境小结
Mar 17 PHP
WordPress中重置文章循环的rewind_posts()函数讲解
Jan 11 PHP
Zend Framework数据库操作方法实例总结
Dec 11 PHP
TP5(thinkPHP5)框架基于ajax与后台数据交互操作简单示例
Sep 03 PHP
PHP实现微信提现功能(微信商城)
Nov 21 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的构造方法,析构方法和this关键字详细介绍
2013/10/22 PHP
PHP文件下载实例代码浅析
2016/08/17 PHP
php利用递归实现删除文件目录的方法
2016/09/23 PHP
Laravel框架实现利用中间件进行操作日志记录功能
2018/06/06 PHP
php判断/计算闰年的方法小结【三种方法】
2019/07/06 PHP
学习jquery必备 api中英文对照的chm手册 下载
2007/05/03 Javascript
jquery中输入验证中一个不错的效果
2010/08/21 Javascript
jQuery 定时局部刷新(setInterval)
2010/11/19 Javascript
遍历jquery对象的代码分享
2011/11/02 Javascript
window.open 以post方式传递参数示例代码
2014/02/27 Javascript
JavaScript实现判断图片是否加载完成的3种方法整理
2015/03/13 Javascript
简化版手机端照片预览组件
2015/04/13 Javascript
原生Javascript和jQuery做轮播图简单例子
2016/10/11 Javascript
jQuery ajax实现省市县三级联动
2021/03/07 Javascript
js截取字符串功能的实现方法
2017/09/27 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【圆形情况】
2018/12/13 Javascript
vue使用一些外部插件及样式的配置代码
2019/11/18 Javascript
antd 表格列宽自适应方法以及错误处理操作
2020/10/27 Javascript
Python导入txt数据到mysql的方法
2015/04/08 Python
Python中的一些陷阱与技巧小结
2015/07/10 Python
python itchat给指定联系人发消息的方法
2019/06/11 Python
Python实现打印实心和空心菱形
2019/11/23 Python
Windows系统下pycharm中的pip换源
2020/02/23 Python
python3将变量写入SQL语句的实现方式
2020/03/02 Python
python中xlutils库用法浅析
2020/12/29 Python
使用html5 canvas创建太空游戏的示例
2014/05/08 HTML / CSS
美国求婚钻戒网站:Super Jeweler
2016/08/27 全球购物
来自世界各地的优质葡萄酒:VineShop24
2018/07/09 全球购物
DJI全球:DJI Global
2021/03/15 全球购物
大型活动策划方案
2014/01/12 职场文书
后备干部培训方案
2014/05/22 职场文书
2014年团总支工作总结
2014/11/21 职场文书
无犯罪记录证明样本
2015/06/16 职场文书
Python实现随机生成迷宫并自动寻路
2021/06/13 Python
美国运营商 T-Mobile 以 117.83Mb/s 的速度排第一位
2022/04/21 数码科技