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 相关文章推荐
如何使用Linux的Crontab定时执行PHP脚本的方法
Dec 19 PHP
PHP高级对象构建 多个构造函数的使用
Feb 05 PHP
使用swoole扩展php websocket示例
Feb 13 PHP
php生成高清缩略图实例详解
Dec 07 PHP
yii2分页之实现跳转到具体某页的实例代码
Jun 02 PHP
PHP Yaf框架的简单安装使用教程(推荐)
Jun 08 PHP
PHP执行shell脚本运行程序不产生core文件的方法
Dec 28 PHP
详解php几行代码实现CSV格式文件输出
Jul 01 PHP
php实现的三个常用加密解密功能函数示例
Nov 06 PHP
PHP+jQuery实现双击修改table表格功能示例
Feb 21 PHP
PHP架构及原理知识点详解
Dec 22 PHP
PHP连接MySQL数据库操作代码实例解析
Jul 11 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
php file_exists 检查文件或目录是否存在的函数
2010/05/10 PHP
php Session存储到Redis的方法
2013/11/04 PHP
PHP使用XMLWriter读写xml文件操作详解
2018/07/31 PHP
PHP curl批处理及多请求并发实现方法分析
2018/08/15 PHP
在JavaScript中使用inline函数的问题
2007/03/08 Javascript
JavaScript网页制作特殊效果用随机数
2007/05/22 Javascript
jQuery学习基础知识小结
2010/11/25 Javascript
通过正则表达式实现表单验证是否为中文
2014/02/18 Javascript
JS和JQ的event对象区别分析
2014/11/24 Javascript
jquery实现动态画圆
2014/12/04 Javascript
jQuery事件绑定on()与弹窗实现代码
2016/04/28 Javascript
ES6正则的扩展实例详解
2017/04/25 Javascript
解决Layui选择全部,换页checkbox复选框重新勾选的问题方法
2018/08/14 Javascript
微信小程序中如何使用flyio封装网络请求
2019/07/03 Javascript
深入了解Hybrid App技术的相关知识
2019/07/17 Javascript
小程序Request的另类用法详解
2019/08/09 Javascript
批量将ppt转换为pdf的Python代码 只要27行!
2018/02/26 Python
Django+Ajax+jQuery实现网页动态更新的实例
2018/05/28 Python
DataFrame:通过SparkSql将scala类转为DataFrame的方法
2019/01/29 Python
Tensorflow实现酸奶销量预测分析
2019/07/19 Python
基于python 微信小程序之获取已存在模板消息列表
2019/08/05 Python
django组合搜索实现过程详解(附代码)
2019/08/06 Python
python匿名函数的使用方法解析
2019/10/10 Python
Python 写了个新型冠状病毒疫情传播模拟程序
2020/02/14 Python
CSS3按钮鼠标悬浮实现光圈效果源码
2016/09/11 HTML / CSS
详解CSS3媒体查询响应式布局bootstrap 框架原理实战(推荐)
2020/11/16 HTML / CSS
美国最大的无人机经销商:DroneNerds
2018/03/20 全球购物
英国网上电器商店:Electricshop
2020/03/15 全球购物
在子网210.27.48.21/30种有多少个可用地址?分别是什么?
2014/07/27 面试题
最热门的自我评价
2013/12/30 职场文书
如何写贫困证明申请书
2014/10/29 职场文书
2015年仓库管理员工作总结
2015/04/21 职场文书
护士自荐信范文(2016推荐篇)
2016/01/28 职场文书
Linux安装Nginx步骤详解
2021/03/31 Servers
在Centos 8.0中安装Redis服务器的教程详解
2022/03/21 Redis
解决IIS7下无法绑定https主机的问题
2022/04/29 Servers