由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远程连接MYSQL数据库非常慢的解决方法
Jul 05 PHP
php合并数组array_merge函数运算符加号与的区别
Oct 31 PHP
php 定义404页面的实现代码
Nov 19 PHP
基于PHP常用字符串的总结(待续)
Jun 07 PHP
php使用cookie保存用户登录的用户名实例
Jan 26 PHP
PHP+MySql+jQuery实现的"顶"和"踩"投票功能
May 21 PHP
[原创]php求圆周率的简单实现方法
May 30 PHP
详谈PHP面向对象中常用的关键字和魔术方法
Feb 04 PHP
php使用curl实现ftp文件下载功能
May 16 PHP
PHP 7.1中AES加解密方法mcrypt_module_open()的替换方案
Oct 17 PHP
PHP生成二维码与识别二维码的方法详解【附源码下载】
Mar 07 PHP
php下的原生ajax请求用法实例分析
Feb 28 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
BBS(php & mysql)完整版(一)
2006/10/09 PHP
使用ThinkPHP生成缩略图及显示
2017/04/27 PHP
PHP有序表查找之二分查找(折半查找)算法示例
2018/02/09 PHP
php tpl模板引擎定义与使用示例
2019/08/09 PHP
jquery Ajax 实现加载数据前动画效果的示例代码
2014/02/07 Javascript
JavaScript生成的动态下雨背景效果实现方法
2015/02/25 Javascript
AngularJS 实现按需异步加载实例代码
2015/10/18 Javascript
jQuery插件实现多级联动菜单效果
2015/12/01 Javascript
Easyui 之 Treegrid 笔记
2016/04/29 Javascript
checkbox 选中一个另一个checkbox也会选中的实现代码
2016/07/09 Javascript
JS实现根据文件字节数返回文件大小的方法
2016/08/02 Javascript
AngularJS ng-repeat指令中使用track by子语句解决重复数据遍历错误问题
2017/01/21 Javascript
js获取浏览器和屏幕的各种宽度高度
2017/02/22 Javascript
js省市区级联查询(插件版&无插件版)
2017/03/21 Javascript
jQuery 控制文本框自动缩小字体填充
2017/06/16 jQuery
Vue 实现v-for循环的时候更改 class的样式名称
2020/07/17 Javascript
python 中的列表解析和生成表达式
2011/03/10 Python
Python简单日志处理类分享
2015/02/14 Python
python从入门到精通(DAY 2)
2015/12/20 Python
在centos7中分布式部署pyspider
2017/05/03 Python
值得收藏,Python 开发中的高级技巧
2018/11/23 Python
如何通过python画loss曲线的方法
2019/06/26 Python
Python 获取windows桌面路径的5种方法小结
2019/07/15 Python
python单例模式原理与创建方法实例分析
2019/10/26 Python
sklearn-SVC实现与类参数详解
2019/12/10 Python
iPhoneX安全区域(Safe Area)底部小黑条在微信小程序和H5的屏幕适配
2020/04/08 HTML / CSS
国际象棋商店:The Chess Store
2018/07/09 全球购物
幼师自我鉴定
2014/02/01 职场文书
工程技术员岗位职责
2014/03/02 职场文书
《永远的白衣战士》教学反思
2014/04/25 职场文书
安全生产月活动总结
2014/05/04 职场文书
2014年小学语文工作总结
2014/12/20 职场文书
工作犯错保证书
2015/05/11 职场文书
《一面五星红旗》教学反思
2016/02/23 职场文书
Python socket如何解析HTTP请求内容
2022/02/12 Python
详解Spring Security如何在权限中使用通配符
2022/06/28 Java/Android