PHP 文档不一致?

Inconsistency in PHP documentation?

我目前正在阅读此处的 PHP 规范:https://github.com/php/php-langspec

  1. 现在,我看到了 list-intrinsic 规范 here,它指出对于 list-intrinsic 构造如下,简单赋值表达式 的右侧必须是指定数组的表达式:

    list ( list-expression-listopt ) = expression

  2. 但是 list 的文档来自 php.net here,给出了一个包含这个的例子:

    $result = $pdo->query("SELECT id, name, salary FROM employees");
    while (list($id, $name, $salary) = $result->fetch(PDO::FETCH_NUM)) {
        //output $id, $name and $salary
    }
    

问题是 PDOStatement::fetch(PDO::FETCH_NUM) returns FALSE 如果没有更多的行。但是 assignment-expression 的右侧必须是数组 - 而 FALSE 不是数组。所以这会导致致命错误,对吧?

我是否遗漏了规范中的某些内容,或者这真的是不一致?


According PHP bugreport

这是在 php 的实现中故意完成的,以允许这段可疑的代码:

while (list($key, $value) = each($array)) {
  // ...
}

each() 的结果可能是 false,否则会触发严重错误,因此虽然此行为看似违反规范,但主要是为了保持向后兼容性。

PHP 的下一个版本可能会废除此行为,但可能性不大,但此时我建议可以编辑规范以反映此特定人工制品,尽管隐含的未定义行为可能也可以达到这个目的:)

令人讨厌的细节

可找到此代码here;目前,右侧表达式支持:

  1. 一个数组,
  2. 实现ArrayAccess
  3. 的对象
  4. 其他。

在“其他”的情况下,它只会将 null 分配给所有列表变量。

更新

Nikita Popov has proposed the following specification update as part of a pull request:

list-intrinsic must be used as the left-hand operand in a simple-assignment-expression of which the right-hand operand must be an expression that designates an array or object implementing the ArrayAccess interface (called the source array).

...

This intrinsic assigns one or more elements of the source array to the target variables. On success, it returns a copy of the source array. If the source array is not an array or object implementing ArrayAccess no assignments are performed and the return value is NULL.

(强调更改)

The documentation 表示以下内容,其中 "list-intrinsic" 是包含 list(...) 可能具有的所有有效形式的语法。

list-intrinsic must be used as the left-hand operand in a simple-assignment-expression of which the right-hand operand must be an expression that designates an array (called the source array).

什么指定数组? The documentation 是这样说的:

An array is a data structure that contains a collection of zero or more elements. The elements of an array need not have the same type, and the type of an array element can change over its lifetime.

我认为你认为布尔值 FALSE 不符合 任何指定数组的东西 是正确的,因为它不是集合。

在这种情况下,'must' 是什么意思?如果我们阅读文档的 Conformance 部分,我们会发现:

In this specification, "must" is to be interpreted as a requirement on an implementation or on a program; conversely, "must not" is to be interpreted as a prohibition.

If a "must" or "must not" requirement that appears outside of a constraint is violated, the behavior is undefined. Undefined behavior is otherwise indicated in this specification by the words "undefined behavior" or by the omission of any explicit definition of behavior. There is no difference in emphasis among these three; they all describe "behavior that is undefined".

您假设必须引发致命错误是否正确?我认为你这样假设是不正确的。除非在 "semantics" 下指出会发生致命错误,否则缺少行为规范或 'must' 下约束意味着未定义该语言部分的行为。它可以工作。它可以抛出一个错误,一个致命错误。它可以创建一个 AI 来摧毁我们所有人,将月亮变成紫色,或者炸毁服务器。它是未定义的。


这是怎么回事?该文档在语义下说了以下内容:

This intrinsic assigns zero or more elements of the source array to the target variables. On success, it returns a copy of the source array. If the source array is actually the value NULL, this is consider a failure, and the return value from list is undefined.

All elements in the source array having keys of type string are ignored. The element having an int key of 0 is assigned to the first target variable, the element having an int key of 1 is assigned to the second target variable, and so on, until all target variables have been assigned. Any other array elements are ignored. If there are fewer source array elements having int keys than there are target variables, the unassigned target variables are set to NULL and a non-fatal error is produced.

测试结果如下:

$a = 1;
$z = FALSE;
$e = (list( $a, $b ) = $z);

var_dump($a); //NULL
var_dump($b); //NULL
var_dump($z); //FALSE

var_dump($e); //FALSE

事实上,$z = $e 对于任何 $z 看来,即使 $z = NULL。我测试过的任何值都不会生成通知、警告或错误,除非源数组的长度小于列表固有表达式中的变量数量。在这种情况下,显示 Notice: Undefined offset

似乎任何不可迭代的表达式都被视为 NULL 值(但这是未定义的行为);在我的 PHP 版本中,似乎任何 NULL 值都会中途中断赋值;它不会被迭代,但是执行了为所有变量分配 NULL 的初步部分。

表达式 while (list($id, $name, $salary) = $result->fetch(PDO::FETCH_NUM)) 因此会将 NULL 赋值给 $id$name$salary,而 FALSE 值将终止while 循环。但是,语言规范不期望或保证此行为。