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

时间:2011-07-17 20:31:37

标签: php php-internals

another question中注意到,在括号中包装PHP函数调用的结果可以某种方式将结果转换为完全成熟的表达式,以便以下工作:

<?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();

我正在尝试在文档中找到显式的任何内容,并明确地解释这里发生的事情。与C ++不同,我不太了解PHP语法及其对语句/表达式的处理,以便自己派生它。

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


更新

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

然后,using phc使用以下命令为两个脚本生成两个.dot变体的foo()个文件,produced个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

在这两种情况下,结果完全相同:

Parse tree of snippets 1 and 2

2 个答案:

答案 0 :(得分:32)

此行为可归类为 bug ,因此您绝对不应该依赖它。

在函数调用中抛出消息 not 的(简化)条件如下(参见definition of the opcode ZEND_SEND_VAR_NO_REF):

  • 参数不是函数调用(如果是,则通过引用返回)和
  • 参数是引用或引用计数为1(如果它具有引用计数1,则它将变为引用)。

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

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

由于附加的括号,PHP不再检测到参数是函数调用。

解析non empty function argument list时,PHP有三种可能性:

  • expr_without_variable
  • A variable
  • (A &后跟variable,用于按参考功能删除的通话时间传递)

当写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()));

答案 1 :(得分:1)

A)要了解这里发生了什么,需要understand PHP's handling of values/variables and references(PDF,1.2MB)。正如stated throughout the documentation"references are not pointers";和you can only return variables by reference from a function - 没有别的。

在我看来,这意味着,PHP中的任何函数都将返回引用。但是一些函数(内置于PHP)需要 values / variables 作为参数。现在,如果要嵌套函数调用,则内部函数调用将返回引用,而外部函数调用则需要一个值。这会导致“着名的”E_STRICT错误"Only variables should be passed by reference"

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

B)我在PHP-syntax description linked in the question找到了一行。

expr_without_variable = "(" expr ")"

结合documentation中的这句话:“在PHP中,你写的几乎所有内容都是表达式。定义表达式的最简单但最准确的方法是'任何有价值的东西'。”我得出结论,即使(5)是PHP中的表达式,它也会计算为值为5的整数。

(因为$a = 5不仅是一个赋值,而且还是一个表达式,它的值为5。)

结论

如果传递对表达式(...)的引用,则此表达式将返回一个值,然后该值可作为参数传递给外部函数。如果那(我的思路)是真的,那么以下两行应该等效地工作:

// 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: Fatal error: Only variables can be passed by reference; 13.09.2005