52

另一个问题中指出,将 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 语法及其对语句/表达式的处理知之甚少,无法自己派生它。

文档中是否隐藏了有关此行为的任何内容?如果不是,其他人可以在不求助于假设的情况下解释它吗?


更新

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

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

$ 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 的解析树

4

2 回答 2

32

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

在函数调用中不抛出消息的(简化)条件如下(参见操作码的定义ZEND_SEND_VAR_NO_REF):

  • 参数不是函数调用(或者如果是,它通过引用返回),并且
  • 该参数要么是一个引用,要么它的引用计数为 1(如果它的引用计数为 1,它就变成了一个引用)。

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

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

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

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

  • 一个expr_without_variable
  • 一种variable
  • (A&后跟 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()));
于 2011-07-18T12:30:40.503 回答
1

A)要了解这里发生了什么,需要了解 PHP 对值/变量和引用的处理(PDF,1.2MB)。正如整个文档所述“引用不是指针”;并且您只能通过函数引用返回变量- 仅此而已。

在我看来,这意味着 PHP 中的任何函数都会返回一个引用。但是某些函数(内置于 PHP)需要值/变量作为参数。现在,如果您正在嵌套函数调用,则内部函数调用返回一个引用,而外部函数调用需要一个值。这会导致'著名的' E_STRICT-error "Only variables should be pass 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 语法描述中找到了一行。

expr_without_variable = "(" expr ")"

结合文档中的这句话:“在 PHP 中,您编写的几乎任何东西都是一个表达式。定义表达式的最简单但最准确的方法是'任何具有值的东西'。”,这使我得出结论,即使(5)是 PHP 中的一个表达式,它的计算结果是一个值为 5 的整数。

(As$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:致命错误:只能通过引用传递变量;13.09.2005

于 2011-07-17T22:43:43.103 回答