由php的call_user_func传reference引发的思考

问题的提出
网友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在传值的时候不管用,而在传引用的时候得出了正确结果(但其实还有一个废除警告)。

(0)

相关推荐

  • PHP 函数call_user_func和call_user_func_array用法详解

    call_user_func函数是当需要动态调用函数时,才使用的,这个函数有两种用法:第一种是调用孤独的函数: 复制代码 代码如下: <?phpfunction funa($b,$c){    echo $b;    echo $c;}call_user_func('funa', "111","222");call_user_func('funa', "333","444");//显示 111 222 333 444//

  • PHP中call_user_func_array()函数的用法演示

    call_user_func_array (PHP 4 >= 4.0.4, PHP 5) call_user_func_array -- Call a user function given with an array of parameters Description mixed call_user_func_array ( callback function, array param_arr ) Call a user defined function given by function,

  • 解析php中call_user_func_array的作用

    一.直接调用方法 复制代码 代码如下: function test($a, $b) {echo '测试一:'.$a.$b;}//调用test方法,array("asp", 'php')对应相应的参数call_user_func_array('test', array("asp", 'php')); 二.通过类调用类中的方法 复制代码 代码如下: class test2{function phpSay($a, $b) {echo '测试二:'.$a.$b;}}$o =

  • php自定义函数call_user_func和call_user_func_array详解

    call_user_func函数类似于一种特别的调用函数的方法,使用方法如下: 复制代码 代码如下: function a($b,$c) { echo $b; echo $c; } call_user_func('a', "111","222"); call_user_func('a', "333","444"); //显示 111 222 333 444 ?> 调用类内部的方法比较奇怪,居然用的是array,不知道开发

  • 由php的call_user_func传reference引发的思考

    问题的提出 网友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. 简单地翻译一下,是说这个函数的参数是不能依靠引用来传递的. 还有一个例子

  • 有关.NET参数传递的方式引发的思考

    下面就简单的介绍一下.NET的一些常用参数用法,如有不足还望指正,也欢迎大家在下面留言讨论,分享自己的见解. 一.DotNet参数概述: .NET中参数(形式参数)变量是方法或索引器声明的一部分,而实参是调用方法或索引器时使用的表达式. 在CLR中,默认的情况下所有的方法参数都是传值的.在传递引用类型的对象时,对一个对象的引用会传递给方法.这里的船引用本身是以传值的方式传给方法的.这也意味着方法能够修改对象,而调用者能看到这些修改.对于值类型的实例,传给方法的实例的一个副本.意味着方法将获得它专

  • 关于ORACLE通过file_id与block_id定位数据库对象遇到的问题引发的思考

    在ORACLE中,我们可以通过file_id(file#)与block_id(block#)去定位一个数据库对象(object).例如,我们在10046生成的trace文件中file#=4 block#=266 blocks=8,那么我可以通过下面两个SQL去定位对象 SQL 1:此SQL效率较差,执行时间较长. SELECT OWNER, SEGMENT_NAME, SEGMENT_TYPE, TABLESPACE_NAME FROM DBA_EXTENTS WHERE FILE_ID =&F

  • 基于React.js实现原生js拖拽效果引发的思考

    一.起因&思路 一直想写一个原生js拖拽效果,又加上近来学react学得比较嗨.所以就用react来实现这个拖拽效果. 首先,其实拖拽效果的思路是很简单的.主要就是三个步骤: 1.onmousedown的时候,启动可拖拽事件,记录被拖拽元素的原始坐标参数. 2.onmousemove的时候,实时记录鼠标移动的距离,结合被拖拽元素第一阶段的坐标参数,计算并设置新的坐标值. 3.onmouseup的时候,关闭可拖拽事件,记录新的坐标值. 注意:这里主要是通过绝对定位的top和left来确定元素的位置

  • php 无法加载mysql的module的时候的配置的解决方案引发的思考

    之后看phpinfo() 里 确实也没找到mysql 模块, 之后所谓的解决方案如"将php.ini" 放入C:\Windows 环境变量等不靠谱说法..... 甚至拷贝ext的文件夹的dll 到System32 中等 统统不靠谱 直到看到这篇帖子,我才发现问题 http://www.haosblog.com/index.php?mod=article_read&id=322 "mysql无法找到的原因是mysql的运行库无法找到,打开mysql的安装文件夹,在bin

  • javascript 一段代码引发的思考第1/2页

    在2008年的最后一天,在此祝愿大家元旦快乐!!! 郑重声明:此问题根本不是问题,现在看来就是本人知识匮乏,庸人自扰,望广大朋友勿喷!! 细心发现问题,耐心解决问题,信心面对问题. 作者:白某人 长话短说:"服务员,上代码...." 测试代码: function init(){ var tpl = new Ext.Template(' this is div{id} '); tpl.append('div1',{id:'2'}); tpl.insertAfter('div2',{id:

  • 关于C语言除0引发的思考

    复制代码 代码如下: <SPAN style="BACKGROUND-COLOR: rgb(241,254,221)"><SPAN style="FONT-FAMILY: Microsoft YaHei">    进行浮点数编程时,如果没有注意,常常会出现输出类似 1.#IND, 1.#INF 或者 nan, inf 之类奇怪的输出.这通常隐含了浮点数操作的异常.</SPAN></SPAN> 进行整数除0的时候,程序会

  • 一个“灵异”批处理引发的思考加补充说明

    批处理的要求是:随机显示的数字为(6,7,8,9,10,11,12,14,15,16,17)为其中的一个 注:里面没有13的 下面的两个代码,第一个出错,第二个却成功了,但他们的区别只是第一个(%random%)%%(%n%)+1运算后的值赋予%tn%,而第二个则将运算后的值继续赋予%n%-- 复制代码 代码如下: @echo off  set "string=6 7 8 9 10 11 12 14 15 16 17"  for %%i in (%string%) do call se

  • .Net页面局部更新引发的思考

    最近在修改以前做的模块,添加一个新功能.整理了下才发现重用率很低,大部分的东西还是需要重新写.功能里用到了局部更新,所有整理一下一路来实现局部更新的解决方案及改进. 我接触的项目开发大多是以Asp.net WebForm开发的,自然会用到UpdatePanel,好处就是开发快.方便,当然产生的问题也是一大堆.然后是Ajax和一般处理程序配合实现异步请求更新.最后就是利用第三方绑定插件优化Ajax请求.  一.UpdatePanel  将需要更新的模块放入UpdatePanel的ContentTe

  • 基于Log4j2阻塞业务线程引发的思考

    目录 问题描述 问题1 问题2:异常线程栈打印使用讨论 ThrowableProxy使用错误的CCL原因分析 异步Appender追加日志 创建log4j日志事件 创建ThrownProxy代理 为什么同一个类会加载多次? GeneratedMethodAccessor类 问题总结 问题1 问题2 问题描述 问题1 异步日志打印在ringbuffer满了之后2.7版本的log4j2会默认使用当前线程进行打印日志. 即使不使用默认的策略,2.9之后已经改为默认的为enqueue方式,也会因为最后队

随机推荐