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


Posted in PHP onMay 02, 2011

看下面的例子:

<?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 相关文章推荐
建立动态的WML站点(二)
Oct 09 PHP
PHP与SQL注入攻击防范小技巧
Sep 16 PHP
基于php中使用excel的简单介绍
Aug 02 PHP
php解析xml提示Invalid byte 1 of 1-byte UTF-8 sequence错误的处理方法
Nov 14 PHP
php获取CSS文件中图片地址并下载到本地的方法
Dec 02 PHP
php判断输入是否是纯数字,英文,汉字的方法
Mar 05 PHP
php删除文本文件中重复行的方法
Apr 28 PHP
百度工程师讲PHP函数的实现原理及性能分析(三)
May 13 PHP
PHP生成随机字符串(3种方法)
Sep 25 PHP
php仿微信红包分配算法的实现方法
May 13 PHP
PHP检查网站是否宕机的方法示例
Jul 24 PHP
php实现与python进行socket通信的方法示例
Aug 30 PHP
一些PHP Coding Tips(php小技巧)[2011/04/02最后更新]
May 02 #PHP
PHP中使用gettext来支持多语言的方法
May 02 #PHP
php中神奇的fastcgi_finish_request
May 02 #PHP
PHP开发不能违背的安全规则 过滤用户输入
May 01 #PHP
PHP 调试工具Debug Tools
Apr 30 #PHP
php debug 安装技巧
Apr 30 #PHP
vs中通过剪切板循环来循环粘贴不同内容
Apr 30 #PHP
You might like
基于mysql的论坛(5)
2006/10/09 PHP
APMServ使用说明
2006/10/23 PHP
PHP如何抛出异常处理错误
2011/03/02 PHP
Laravel事件监听器用法实例分析
2019/03/12 PHP
JavaScript 数组的 uniq 方法
2008/01/23 Javascript
JS中字符问题(二进制/十进制/十六进制及ASCII码之间的转换)
2008/11/03 Javascript
jquery-syntax动态语法着色示例代码
2014/05/14 Javascript
从零学jquery之如何使用回调函数
2014/05/16 Javascript
js实现文字跟随鼠标移动而移动的方法
2015/02/28 Javascript
浅析JS获取url中的参数实例代码
2016/06/14 Javascript
常用Javascript函数与原型功能收藏(必看篇)
2016/10/09 Javascript
JavaScript实现图片懒加载(Lazyload)
2016/11/28 Javascript
Vue.js双向绑定操作技巧(初级入门)
2016/12/27 Javascript
js实现文字选中分享功能
2017/01/25 Javascript
Javascript中 带名 匿名 箭头函数的重要区别(推荐)
2017/01/29 Javascript
基于javascript实现最简单选项卡切换
2017/02/01 Javascript
Node.js中多进程模块Cluster的介绍与使用
2017/05/27 Javascript
node.js 用socket实现聊天的示例代码
2017/10/17 Javascript
解决Vue 项目打包后favicon无法正常显示的问题
2018/09/01 Javascript
在vue中阻止浏览器后退的实例
2019/11/06 Javascript
Python卸载模块的方法汇总
2016/06/07 Python
Python对象类型及其运算方法(详解)
2017/07/05 Python
pandas groupby 分组取每组的前几行记录方法
2018/04/20 Python
Ubuntu下使用python读取doc和docx文档的内容方法
2018/05/08 Python
Python对象与引用的介绍
2019/01/24 Python
Python3 chardet模块查看编码格式的例子
2019/08/14 Python
python判断链表是否有环的实例代码
2020/01/31 Python
Python实现简单的猜单词小游戏
2020/10/28 Python
Harman Audio官方商店:购买JBL、Harman Kardon、Infinity和AKG
2019/12/05 全球购物
印度尼西亚手表和包包商店:Urban Icon
2019/12/12 全球购物
我未来的职业规划范文
2014/01/11 职场文书
小区门卫值班制度
2014/01/24 职场文书
精彩的英文自荐信
2014/01/30 职场文书
统计工作个人总结
2015/03/03 职场文书
小人国观后感
2015/06/11 职场文书
nginx简单配置多个server的方法
2021/03/31 Servers