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

看下面的例子:


代码如下:

<?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网友提供的这个有趣的问题.

(0)

相关推荐

  • PHP 前加at符合@的作用解析

    大家在做PHP项目时,总是有些代码行前加上@符号,大家都知道是什么意思吗?下面给大家分享下PHP中函数前加at符合@的作用解析 今天用到了,就记一下吧.其实它是错误控制符,即使出现错误,也无视出现的错误信息,继续执行下边的代码. 复制代码 代码如下: @$page=$_GET['page']?intval($_GET['page']):1; 这句是从URL中获取page关键字的值,比如"index.php?page=5",则$page就会取到5. 但是如果有error,比如"

  • PHP @ at 记号的作用示例介绍

    看PHP的代码,总有些行前边有@符号,一直不知道是什么意思. 例如dede5.7 @ni=imagecreatetruecolor(ftoW,$ftoH); 今天用到了,就记一下吧.其实它是错误控制符,即即使出现错误,也无视出现的错误信息,继续执行下边的代码. @$page=$_GET['page']?intval($_GET['page']):1; 这句是从URL中获取page关键字的值,比如"index.php?page=5",则$page就会取到5. 但是如果有error,比如&

  • php at(@)符号的用法简介

    下面介绍一下它的用法. 例如: 复制代码 代码如下: function db_connect()//连接数据库 { @$db =mysql_connect('localhost','root','test'); if(!$db) throw new Exception('连接数据库失败!请重试!'); mysql_select_db('book'); return $db; } 如果连接数据库不成功的,前面的"@"就能把错误显示给抑制住,也就是不会显示错误,然后再抛出异常,显示自己定义

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

    看下面的例子: 复制代码 代码如下: <?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

  • Go语言到底有没有引用传参(对比 C++ )

    C++ 中三种参数传递方式 值传递: 最常见的一种传参方式,函数的形参是实参的拷贝,函数中改变形参不会影响到函数外部的形参.一般是函数内部修改参数而又不希望影响到调用者的时候会采用值传递. 指针传递 形参是指向实参地址的一个指针,顾名思义,在函数中对形参指向的内容操作,实参本身会被修改. 引用传递 在 C++ 中,引用是变量的别名,实际上是同一个东西,在内存中也存在同一个地址.换句话说,不管在哪里对引用操作,都相当直接操作被引用的变量. 下面看 demo: #include <iostream>

  • 解决javaBean规范导致json传参首字母大写将永远获取不到问题

    javaBean规范导致json传参首字母大写获取不到 规范中另一个特别的地方就是: 1.第二个字母为大写的属性名要区别对待 如果属性名的第二个字母是大写的,那么该属性名直接用作 getter/setter 方法中 get/set 的后部分,就是说大小写不变. 2.属性是首字母大写 次字母小写是,你永远都找不到它的 getter/setter 方法的. 3.对于 boolean 类型属性的 getter 方法 是 isXxx() 还是 getXxx() 就自己决定了,isXxx() 应该更接近于

  • Python中引用传参四种方式介绍

    目录 引用传参一: ​引用传参二: ​​引用传参三: ​​引用传参四: 总结 引用传参一: ​​>>> a = 100 #这里的a是不可变类型 >>> def test(a): ... a+=a #这个式子有两层含义:1.这里可能是重新定义一个新的变量a,2.也有可能是修改a的值,但由于全局 #变量a不能修改,所以此处是重新定义了一个a: ... print("函数内:%d"%a) ... >>> test(a) 函数内:200 &

  • JS 文件传参及处理技巧分析

    解决思路: 1.首先获取到当前JS文件的SRC属性,这里有一个小技巧:我们只需要获取当前页面最后一个script标记内容即可. 为什么??因为JS是顺序解析的,当前JS脚本解析时后面的js都还没有解析到,当然就认为自己就是最后一个script了.此外,这样获取还有一个好处:我们可以多次引用同一个文件且传入不同的参数,这样可以在js文件中根据参数不同做不同处理,很巧妙把!简直就是动态语言了. 代码如下: 复制代码 代码如下: var scripts=document.getElementsByTa

  • js中关于new Object时传参的一些细节分析

    1, 参数是一个对象,核心js对象(native ECMAScript object)或宿主对象(host object),那么将直接返回该对象. 其生成的对象构造器仍然是所传参数对象的构造器.这样造成的后果是虽然该对象是new Object,但其constructor不一定是Object. 复制代码 代码如下: function Person(){this.name='jack';} var w = new Object(window), d = new Object(document), p

  • pyMySQL SQL语句传参问题,单个参数或多个参数说明

    在用pymysql操作数据库的过程中,给sql语句传参碰到了很多问题,网上传参策略很多,这里推荐两种 单个传参用%s,写起来比较简单: field = '-' sql_talk="UPDATE cnp.Test set a='' where b='%s'" cursor.execute(sql_talk % field) db.commit() 多个传参用{0}占位符: field = '-' a = 'code' sql_talk="UPDATE cnp.Test set

  • 深入理解PHP原理之错误抑制与内嵌HTML分析

    PHP提供了一个错误抑制符'@', 它是通过什么方式来阻止错误输出呢? 我又该在什么时候使用它呢? 这是这俩天一些网友提到的共同问题, 今天就索性整体回答下, 备后来人翻阅. PHP文件内嵌HTML的处理方式 在PHP中, 所有在标签外的字符, 在词法分析过程中, 都会翻译成T_INLINE_HTML token, 在语法分析的时候, 所有的T_INLIE_HTML都会被分配ZEND_ECHO输出. 也就是说: 复制代码 代码如下: <?php while($con) { ?> laruenc

  • 上传IPA出现的错误提示“application loader“上传出错解决方法

    上传IPA出现的错误提示"application loader"上传出错解决方法 使用Application Loader 上传ipa出现的错误提示"application loader上传出错 生成的API分析文件太大"解决方法 如下图: 解决办法就是修改Xcode里面的这里写 因为之前上传的时候有使用过的Build号,只需要修改一下就好 感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

  • Vue3父子组件传参有关sync修饰符的用法详解

    目录 单向数据流讲解 Vue2.x使用 定义事件的形式, 通知父组件修改 .sync 和 update: 的使用 父传子, 传递多个数据的简写 采用v-model简写(要求严格) Vue3.x使用 普通用法 简写 单向数据流讲解 单向数据流(堆可以修改,栈不可修改) 我们都知道, 父传子的数据, 是单向数据流,即子组件不能直接修改, 父组件传递过来的值 但实际上, 对于修改值, 真正是:基本数据类型不可修改,复杂数据类型不要修改引用地址(栈),它的值可以随便修改 Vue2.x使用 定义事件的形式

随机推荐