由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 fsockopen中多线程问题的解决办法[翻译]
Nov 09 PHP
PHP的博客ping服务代码
Feb 04 PHP
浅析PHP文件下载原理
Dec 25 PHP
php+mysqli批量查询多张表数据的方法
Jan 29 PHP
微信支付PHP SDK之微信公众号支付代码详解
Dec 09 PHP
Laravel中Trait的用法实例详解
Mar 16 PHP
PHP实现bitmap位图排序与求交集的方法
Jul 28 PHP
利用PHP获取网站访客的所在地位置
Jan 18 PHP
php实现留言板功能
Mar 05 PHP
php实现购物车产品删除功能(2)
Jul 23 PHP
thinkPHP微信分享接口JSSDK用法实例
Jul 07 PHP
php中输出json对象的值(实现方法)
Mar 07 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代码
2010/07/17 PHP
测试php函数的方法
2013/11/13 PHP
PHP实现获取图片颜色值的方法
2014/07/11 PHP
php上传图片客户端和服务器端实现方法
2015/03/30 PHP
PHP编程开发怎么提高编程效率 提高PHP编程技术
2015/11/09 PHP
ThinkPHP中html:list标签用法分析
2016/01/09 PHP
php操作redis常见方法示例【key与value操作】
2020/04/14 PHP
ASP.NET MVC中EasyUI的datagrid跨域调用实现代码
2012/03/14 Javascript
YUI Compressor压缩JavaScript原理及微优化
2013/01/07 Javascript
jQuery 淡入淡出 png图在ie8下有黑色边框的解决方法
2013/03/05 Javascript
在JavaScript中实现类的方式探讨
2013/08/28 Javascript
JavaScript插件化开发教程 (四)
2015/01/27 Javascript
JQuery select(下拉框)操作方法汇总
2015/04/15 Javascript
WordPress中利用AJAX异步获取评论用户头像的方法
2016/01/08 Javascript
nodejs实现发出蜂鸣声音(系统报警声)的方法
2017/01/18 NodeJs
AngularJS自定义指令实现面包屑功能完整实例
2017/05/17 Javascript
jQuery实现点击DIV同时点击CheckBox,并为DIV上背景色的实例
2017/12/18 jQuery
Vue二次封装axios为插件使用详解
2018/05/21 Javascript
layui前端框架之table表数据的刷新方法
2018/08/17 Javascript
微信小程序实现的点击按钮 弹出底部上拉菜单功能示例
2018/12/20 Javascript
微信小程序配置服务器提示验证token失败的解决方法
2019/04/03 Javascript
javascript 关于赋值、浅拷贝、深拷贝的个人理解
2019/11/01 Javascript
Selenium执行Javascript脚本参数及返回值过程详解
2020/04/01 Javascript
Python 装饰器深入理解
2017/03/16 Python
python抓取网站的图片并下载到本地的方法
2018/05/22 Python
Python时间序列处理之ARIMA模型的使用讲解
2019/04/02 Python
关于Python形参打包与解包小技巧分享
2019/08/24 Python
python中如何实现将数据分成训练集与测试集的方法
2019/09/13 Python
python通过opencv实现图片裁剪原理解析
2020/01/19 Python
Python3 搭建Qt5 环境的方法示例
2020/07/16 Python
美国时尚孕妇装品牌:A Pea in the Pod
2017/07/16 全球购物
美国温暖商店:The Warming Store
2018/12/15 全球购物
Linux上比较文件的命令都有哪些
2012/02/24 面试题
机关搬迁方案
2014/05/18 职场文书
2014年幼儿园教师工作总结
2014/11/08 职场文书
幼儿园师德师风心得体会
2016/01/12 职场文书