博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PHP错误抑制符(@)导致引用传参失败的Bug
阅读量:4120 次
发布时间:2019-05-25

本文共 1563 字,大约阅读时间需要 5 分钟。

今天cici网友发来一个问题, 说是在函数调用参数前面使用错误抑制符号(@)的时候, 貌似引用传参就失效了. 他想让我帮他解答为什么.

看下面的例子:

  1. $array = array(1,2,3);
  2.  
  3. function add (&$arr) {
  4.     $arr[] = 4;
  5. }
  6. add(@$array);
  7. print_r($array);
  8. /**
  9. 此时, $array没有改变, 输出:
  10. Array
  11. (
  12. [0] => 1
  13. [1] => 2
  14. [2] => 3
  15. )
  16.  
  17. */
  18. add($array);
  19. print_r($array);
  20. /**
  21. 不使用错误抑制的情况下, 输出正常:
  22. Array
  23. (
  24. [0] => 1
  25. [1] => 2
  26. [2] => 3
  27. [3] => 4
  28. )
  29. */
  30. ?>

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

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

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

  1. //没有使用错误抑制符的时候
  2. OPCODE = SEND_REF
  3.  
  4. //使用了错误抑制符号以后
  5. OPCODE = SEND_VAR_NO_REF

问题初步定位了, 但是造成这种差异的原因又是什么呢?

既然OPCODE不同, 那么肯定是在语法分析的阶段, 走了不同的分支了, 想到这一层, 问题也就好定位了,

原来, PHP语法分析阶段, 把形如 “@”+expr的条目, 规约成了expr_without_variable, 而这种节点的意义就是没有变量的值, 也就是字面值, 我们都知道字面值是不能传递引用的(因为它不是变量), 所以, 就会导致这种差异.

具体过程如下:

1. 语法分析阶段:

  1. expr_without_variable:
  2. //...有省略
  3.    | '@' { zend_do_begin_silence(&$1 TSRMLS_CC); }
  4.        expr { zend_do_end_silence(&$1 TSRMLS_CC); $$ = $3; }
  5.  
  6. //此处走了ZEND_SEND_VAL分支
  7. non_empty_function_call_parameter_list:
  8.         expr_without_variable { ....} //错误的走了这个分支
  9.     | variable {..... } //正常情况下

所以导致在编译期间, 生成了不同的OPCODE, 也导致了问题的表象.

最后, 我已经把原因在PHP的这个bug页做了说明, 有兴趣的可以去看看我的烂英语水平. 最后谢谢cici网友提供的这个有趣的问题.

你可能感兴趣的文章
JDBC核心技术 - 上篇
查看>>
JDBC核心技术 - 下篇
查看>>
一篇搞懂Java反射机制
查看>>
【2021-MOOC-浙江大学-陈越、何钦铭-数据结构】树
查看>>
MySQL主从复制不一致的原因以及解决方法
查看>>
RedisTemplate的key默认序列化器问题
查看>>
序列化与自定义序列化
查看>>
ThreadLocal
查看>>
从Executor接口设计看设计模式之最少知识法则
查看>>
OKhttp之Call接口
查看>>
application/x-www-form-urlencoded、multipart/form-data、text/plain
查看>>
关于Content-Length
查看>>
WebRequest post读取源码
查看>>
使用TcpClient可避免HttpWebRequest的常见错误
查看>>
EntityFramework 学习之一 —— 模型概述与环境搭建 .
查看>>
C# 发HTTP请求
查看>>
启动 LocalDB 和连接到 LocalDB
查看>>
Palindrome Number --回文整数
查看>>
Reverse Integer--反转整数
查看>>
Container With Most Water --装最多水的容器(重)
查看>>