PHP错误抑制符(@)导致引用传参失败Bug的分析

前端之家收集整理的这篇文章主要介绍了PHP错误抑制符(@)导致引用传参失败Bug的分析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

看下面的例子:
<div class="codetitle"><a style="CURSOR: pointer" data="35143" class="copybut" id="copybut35143" onclick="doCopy('code35143')"> 代码如下:

<div class="codebody" id="code35143">
<?PHP
$array = array(1,2,3);
function add (&$arr) {
$arr[] = 4;
}
add(@$array);
print_r($array);
/*
此时,$array没有改变, 输出:
Array
(
[0] => 1
[1] => 2
[2] => 3
)
/
add($array);
print_r($array);
/*
不使用错误抑制的情况下,输出正常:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
/
?>

这个问题,我之前没有遇到过,所以首先去找找相关资料,看看有没有现成的答案,Goolge了一番,发现虽然有人已经向PHP报了类似的Bug:http://bugs.php.net/bug.php?id=47623,但PHP官方还没有解决,也没有给出答复.

没办法,只能自己分析了,之前我曾经在文章中介绍过错误抑制符的原理( 深入理解PHP原理之错误抑制与内嵌HTML),从原理上来说,错误抑制只是修改了error_reporting的level,按理来说不会影响到上下文之间的函数调用的机制. 只能通过实地试验了.

经过gdb跟踪,发现在使用了错误移植符以后,函数调用前的传参opcode不同:

代码如下:
//没有使用错误抑制符的时候
OPCODE = SEND_REF
//使用了错误抑制符号以后
OPCODE = SEND_VAR_NO_RE

问题初步定位了,但是造成这种差异的原因又是什么呢? 既然OPCODE不同,那么肯定是在语法分析的阶段,走了不同的分支了,想到这一层,问题也就好定位了,原来,PHP语法分析阶段,把形如 “@”+expr的条目,规约成了expr_without_variable,而这种节点的意义就是没有变量的值,也就是字面值,我们都知道字面值是不能传递引用的(因为它不是变量),所以,就会导致这种差异. 具体过程如下:
1. 语法分析阶段:
代码如下:
expr_without_variable:
//...有省略
| '@' { zend_do_begin_silence(&$1 TSRMLS_CC); }
expr { zend_do_end_silence(&$1 TSRMLS_CC); $$ = $3; }
//此处走了ZEND_SEND_VAL分支
non_empty_function_call_parameter_list:
expr_without_variable { ....} //错误的走了这个分支
| variable {..... } //正常情况

所以导致在编译期间,生成了不同的OPCODE,也导致了问题的表象.
最后,我已经把原因在PHP的这个bug页做了说明,有兴趣的可以去看看我的烂英语水平. 最后谢谢cici网友提供的这个有趣的问题.

猜你在找的PHP相关文章