括号改变了函数调用结果的语义

在另一个问题中指出,将一个PHP函数调用的结果包含在括号中可以以某种方式将结果转换为一个完全成熟的expression式,从而以下工作:

<?php error_reporting(E_ALL | E_STRICT); function get_array() { return array(); } function foo() { // return reset(get_array()); // ^ error: "Only variables should be passed by reference" return reset((get_array())); // ^ OK } foo(); 

我试图在文档中find任何明确无误地解释这里发生的事情。 与C ++不同,我不太了解PHP语法及其对语句/expression式的处理以便自己派生。

有关此行为的文档中是否有隐藏的内容? 如果没有,其他人可以解释而不诉诸假设?


更新

我首先发现这个EBNF声称代表PHP语法,并试图自己解码我的脚本,但最终放弃了。

然后, 使用phc生成两个foo()变体的.dot文件,我使用以下命令为这两个脚本生成了 AST图像:

 $ yum install phc graphviz $ phc --dump-ast-dot test1.php > test1.dot $ dot -Tpng test1.dot > test1.png $ phc --dump-ast-dot test2.php > test2.dot $ dot -Tpng test2.dot > test2.png 

在这两种情况下,结果都是一样的:

解析片段1和片段2的树

这种行为可以被归类为错误 ,所以你绝对不应该依赖它。

消息被抛出函数调用的(简化)条件如下(请参阅操作码ZEND_SEND_VAR_NO_REF的定义 ):

  • 参数不是一个函数调用(或者如果是,它通过引用返回),和
  • 参数是一个引用,或者引用计数为1(如果引用计数为1,则将其转换为引用)。

让我们更详细地分析这些。

第一点是真的(不是函数调用)

由于额外的括号,PHP不再检测到该参数是一个函数调用。

parsing非空函数参数列表时 ,PHP有三种可能性:

  • 一个expr_without_variable
  • 一个variable
  • (A &后跟一个variable ,用于删除的通话时间通过引用function)

在编写get_array() PHP将此视为一个variable

(get_array())不能作为一个variable 。 这是一个expr_without_variable

这最终会影响代码编译的方式,即操作码SEND_VAR_NO_REF的扩展值将不再包含标志ZEND_ARG_SEND_FUNCTION ,这是在操作码实现中检测函数调用的方式。

第二点是真的(引用计数是1)

在几个点上,Zend引擎允许引用计数为1的非引用在需要引用的地方。 这些细节不应该暴露给用户,但不幸的是他们在这里。

在你的例子中,你正在返回一个没有被其他地方引用的数组。 如果是的话,你仍然会得到这个信息,即第二点不会是真的。

所以下面非常相似的例子不起作用

 <?php $a = array(); function get_array() { return $GLOBALS['a']; } return reset((get_array())); 

A)为了理解这里发生了什么,需要理解PHP对值/variables和引用的处理 (PDF,1.2MB)。 正如整个文档所述 : “引用不是指针” ; 而且你只能通过函数引用来返回variables – 没有别的。

在我看来,这意味着PHP中的任何函数都会返回一个引用 。 但是一些函数(build立在PHP中)需要值/variables作为参数。 现在,如果嵌套函数调用,那么内部函数会返回一个引用,而外部函数需要一个值。 这导致了“着名的”E_STRICT错误“只有variables应该通过引用传递” 。

 $fileName = 'example.txt'; $fileExtension = array_pop(explode('.', $fileName)); // will result in Error 2048: Only variables should be passed by reference in… 

B) 在问题中链接的PHP语法描述中find了一行。

 expr_without_variable = "(" expr ")" 

结合文档中的这句话:“在PHP中,几乎所有你写的东西都是一个expression式,定义expression式的最简单但最准确的方法是”任何有价值的东西“,这使我得出的结论是, (5)是PHP中的一个expression式,其值为5。

(因为$a = 5不仅是一项任务,而且也是一个expression式,评估为5)

结论

如果传递给expression式(...)的引用,则此expression式将返回一个值,然后可以将其作为parameter passing给外部函数。 如果这(我的思路)是真的,那么下面两行应该是等价的:

 // what I've used over years: (spaces only added for readability) $fileExtension = array_pop( ( explode('.', $fileName) ) ); // vs $fileExtension = array_pop( $tmp = explode('.', $fileName) ); 

另请参阅PHP 5.0.5:致命错误:只有variables可以通过引用传递; 2005年9月13日