PHP文档不一致?

Min*_*ock 7 php mysql pdo specifications list

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

  1. 现在,我在这里看到了list-intrinsic规范,它指出对于像下面这样的list-intrinsic结构,simple-assignment-expression的右边必须是一个指定数组的表达式:

    list (list-expression-list opt ) = 表达式

  2. 但是这里的 php.net 列表文档给出了一个包含这个的例子:

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

如果没有更多的行,那就是PDOStatement::fetch(PDO::FETCH_NUM)返回FALSE.但赋值表达式的右侧 必须是数组 - 而FALSE不是数组.所以这会导致致命错误,对吧?

我是否遗漏了规范中的内容,或者这实际上是不一致的?


根据PHP bugreport

Ja͢*_*͢ck 6

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

while (list($key, $value) = each($array)) {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

结果each()可能false会导致一个令人讨厌的错误,所以虽然这种行为似乎违反了规范,但主要是为了保持向后兼容性.

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

令人讨厌的细节

这个代码可以在这里找到; 目前,右侧表达支持:

  1. 数组,
  2. 实现的对象ArrayAccess,
  3. 还有别的.

在"别的东西"的情况下,它将只分配null给所有列表变量.

更新

Nikita Popov提出以下规范更新作为拉取请求的一部分:

list-intrinsic必须用作simple-assignment-expression中的左操作数,其右边操作数必须是指定实现ArrayAccess接口的数组或对象的表达式(称为源数组).

...

此内在函数将源数组的一个或多个元素分配给目标变量.成功时,它返回源数组的副本.如果源数组不是数组或执行ArrayAccess没有赋值的对象,则返回值为NULL.

(强调变化)

  • @MinecraftShamrock规范是在2014年编写的(我认为它还不是最终的),而解释器的代码允许PHP自PHP(90年代中期)开始构建.我个人从未使用过`each()`而且我认为它只是旧版本PHP(3.0及更低版本)的遗留物(并且可以在没有很多人注意的情况下删除)但是当前的解释器试图向后兼容它们(越多越好).另一方面,规范试图标准化PHP的当前行为和**未来**版本. (3认同)

Sum*_*ai8 1

该文档说明如下,其中“list-intrinsic”是包含所有有效形式list(...)可能具有的语法。

list-intrinsic 必须用作简单赋值表达式中的左侧操作数,其中右侧操作数必须是指定数组(称为源数组)的表达式。

什么指定数组?文档是这样说的:

数组是包含零个或多个元素的集合的数据结构。数组的元素不需要具有相同的类型,并且数组元素的类型可以在其生命周期内改变。

我认为您的想法是正确的FALSE,布尔值不符合指定数组的任何内容,因为它不是集合。

在这种情况下“必须”是什么意思?如果我们阅读文档的一致性部分,我们会发现:

在本说明书中,“必须”应被解释为对实现或程序的要求;相反,“不得”应解释为禁止。

如果违反了约束之外的“必须”或“不得”要求,则行为是未定义的。未定义的行为在本说明书中另外通过词语“未定义的行为”或通过省略任何明确的行为定义来指示。这三者的侧重点没有区别;它们都描述了“未定义的行为”。

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


那么到底发生了什么?该文档在语义下说了以下内容:

此内在函数将源数组的零个或多个元素分配给目标变量。成功时,它返回源数组的副本。如果源数组实际上是 NULL 值,则视为失败,并且 list 的返回值未定义。

源数组中具有字符串类型键的所有元素都将被忽略。int 键为 0 的元素分配给第一个目标变量,int 键为 1 的元素分配给第二个目标变量,依此类推,直到所有目标变量都已分配。任何其他数组元素都将被忽略。如果具有 int 键的源数组元素少于目标变量,则未分配的目标变量将设置为 NULL 并产生非致命错误。

测试给出以下结果:

$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
Run Code Online (Sandbox Code Playgroud)

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

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

因此,表达式while (list($id, $name, $salary) = $result->fetch(PDO::FETCH_NUM))将分配NULL$id$name$salary,并且该FALSE值将终止 while 循环。然而,语言规范并不期望或保证这种行为。