JS与PHP:逻辑或运算符结合赋值运算符时的优先级问题

16

(PHP有||OR。JS只有||。)

JS。根据MDN||的优先级高于=。因此这行代码是不起作用的:

a || a = 1;

因为它被评估为:

(a || a) = 1;

这将导致"Invalid left-hand side in assignment(在赋值中左侧无效)",我理解了。这很有道理。

PHP。 根据PHP.net,对于 PHP,它的工作方式与 JavaScript 相同:||= 之前。然而,我经常使用下面这种写法:

$a || $a = 1;
为什么它在PHP中可用?而且更重要的是:PHP的“OR”比“=”的优先级低,所以这些应该不相同:

为什么在 PHP 中有效呢?而且更糟糕的是:PHP 的“OR”运算优先级低于“=”运算符,因此这两个运算符不应该等效:


返回值:

为什么在 PHP 中有效呢?而且更糟糕的是:PHP 的OR运算优先级低于=运算符,因此这两个运算符不应该等效:

$a || $a = 1;
$a OR $a = 1;

但它们确实......https://3v4l.org/UWXMd

我认为 JS 的 || 遵循 MDN 的表格工作,而 PHP 的 OR 像 PHP 的表格一样工作,但是 PHP 的 || 不应该像它现在这样工作。

这是另一个奇怪的 PHP 怪癖吗?

手册还提到了这个:

 

尽管 = 优先级比大多数其他运算符低,但 PHP 仍将允许类似以下的表达式:if (!$a = foo()),在这种情况下,foo() 的返回值被放入$a中。

优先级表规定 PHP 应该计算 (!$a) = foo(),这没有意义并且应该失败,但 PHP 将其评估为 !($a = foo()),因为它喜欢异常。

跟进问题:你认为 if ( $d = $c && $e = $b && $f = $a ) 做什么? https://3v4l.org/3P2hN 我不明白...我确实理解第二和第三种情况(使用 and),只是不知道第一种情况会发生什么。


1
PHP手册还指出,运算符优先级和结合性仅确定表达式的分组方式,它们不指定评估顺序。PHP通常不指定表达式的评估顺序,假设特定的评估顺序的代码应该避免,因为行为可能会在PHP版本之间或取决于周围代码而发生变化。 - CY5
1
此外,我认为在问题中包含以下内容会很好:“($a || $a) = 1” 在 PHP 中也是一个解析错误。 - user2864740
1
虽然我还没有找到关于这些点的“好”答案:http://stackoverflow.com/questions/21596754/precedence-operator-or-and-in-php?rq=1,http://stackoverflow.com/questions/15835817/operators-precedence - user2864740
1
@Stevish Ruby有a = b unless a,这是一种逻辑上等效的语法,但是“为”这种情况而设计。 - user2864740
1
虽然不是完全重复,但是我发现https://dev59.com/oGcs5IYBdhLWcg3w84dp#15144605是关于变量赋值的最佳答案。 - user2864740
显示剩余9条评论
3个回答

13
根据zend_language_parser.y,无论哪种情况,该代码都被解析等价于$a || ($a = 1)$a or ($a = 1)
正如melpomene所总结的那样,赋值语句并不是表达式的中缀二元运算符;而是仅限左侧必须是variable生产的受限制语句。 根据借用的引文的说法:

因此PHP以唯一可能的方式解析表达式..

文档在适用时有关于优先级的说明是正确的
因此$a || $a = 1遵循(反向)以下生产过程:
variable "||" variable "=" expr
variable "||" expr_without_variable
expr "||" expr
expr

!$a = foo() 的情况类似,经过(反向的)产生式解析后被解释为 !($a = foo())

"!" variable "=" expr
"!" expr_without_variable
"!" expr                 
expr
现在,如何处理$d = $c && $e = $b && $f = $a?尽管&&的优先级高于赋值,它并不会被解析为($d = $c) && ..。实际上,它是被解析为$d = ($c && ($e = ..))等等,需要细心的读者来完成。
虽然可能不太容易注意到这种差异,但它可以产生不同的结果:
$a = (($c = 1) && ($d = 0));
var_dump($a, $c, $d);         // => false, 1, 0

$b = ($e = 1 && $f = 0);      // => $b = ($e = (1 && ($f = 0)));
var_dump($b, $e, $f);         // => false, false, 0

因此,在将赋值操作符与具有更高优先级的运算符混合使用时,通常应使用括号,特别是当其结果可能不明确时。

尽管这看起来可能不一致,但它是一个定义良好的语法 - 但技术细节被一些相当通俗易懂的文档所掩盖;而且规则与其他类似于C语法的语言略有不同。文档中缺乏官方的EBNF也没有帮助。


尽管存在解析细节,$a || $a = ..代码(它是有效且定义良好的语法)在评估视角上应保持定义良好,因为由于短路保证,'or'的左侧必须先于右侧发生。


相比之下,在JavaScript中,a || a = 1会被解析为(a || a) = 1,这也是语法上“有效”的代码 - 根据ECMAScript语法规则。然而,a || a不产生有效的Reference Specification Type,因此会抛出一个运行时ReferenceError。


1
根据该语法,=不是一个真正的中缀运算符:无论它左边是什么,它都只会获取一个变量。它的行为就像元前缀运算符一样:对于所有变量$foo$foo =像一个前缀运算符解析。 - melpomene
@melpomene 这就是问题的关键:赋值符号 = 的左侧被限制为变量(而不是表示式)生产。 - user2864740
所以PHP和JS有非常相似的运算符优先级规则,只是解析规则不同?这很有道理。我太过于纠结于优先级表了。谢谢! - Rudie
@Rudie 魔鬼就在细节中 :} - user2864740

1
关于你的后续问题:if ( $d = $c && $e = $b && $f = $a )与以下代码相同:
$d = $c;
if($d) {
    $e = $b;
    if($e) {
        $f = $a;
        if($f) {
            ...
        }
    }
}

我假设你已经知道这点,但是有些问题对我来说很困惑,所以我会提一下... = 是一个赋值运算符,而不是比较运算符。if($a = $b)不检查 $a 和 $b 是否相同,它使 $a 等于 $b,然后检查 $a 是否为真。 if($a == $b) 检查这两个变量是否相同。

1
为什么会被解析成那样?根据优先级表,它应该是$d = (($c && $e) = (($b && $f) = $a)) - melpomene
我的猜测是同样的原因,即"1" + 1 + 1.5计算结果为3.5,而不是抛出关于尝试添加字符串、整数和浮点数的错误。PHP是一种脚本语言,相对宽容。PHP不会在if语句中在=之前计算&&,因为这没有意义(或者说更少意义)。实际上,如果第一个选项计算结果为false(这就是为什么在您的示例中$e和$f保持为-1的原因),它甚至不会计算第二个或第三个选项。如果您已经深入到精确的计算顺序中,我建议依靠括号:它们更可靠。 - Stevish
我编辑了我的代码块,以更准确地反映PHP如何处理if条件中的=&& - Stevish
也许你可以这样说,在 if 条件语句中,PHP 首先查看 && 和 ||,知道它们用于将条件拆分为不同的可能性。我只是在头脑风暴,我能理解你的沮丧:一种宽容的语言很难以确切的方式理解。我个人依赖于应该是不必要的做法(比如到处都是括号),以减少这种沮丧。 - Stevish
1
@Stevish 你怎么知道它是一样的?(除了看结果,这从来不是找出某个东西如何工作的好方法。)它应该像解析错误一样被解析。这是 PHP 尝试变得更加聪明和简单,做一些奇怪的事情以避免解析错误吗? - Rudie
1
如果我理解语法正确的话,这实际上被解析为 $d = ($c && ($e = ($b && ($f = $a)))) - melpomene

-1

$a || $a = 1;这个表达式等同于:

if ( $a != true ) {
    $a = 1;
}

这个想法的一个非常常见的变体用于穷人的调试:

$debug = true;

// Thousands of lines of code

$debug && printf("Foo: {$foo}");

// More code

$debug && printf("Bar: {$bar}");

在这种范式中,只需要将$debug语句设置为true/false即可启用/禁用调试。 我并不主张这种类型的调试,但我已经看到过很多次了。

1
请回复原帖中指出的问题:它并没有像那样被解析。而且,在逻辑上,||&& 是不同的。 - user2864740
(好吧,更确切地说,"不应该是"。) - user2864740
1
你的第一个代码块是错误的:if ( $a == true ) {。你的意思是 false。我知道这个。但这并没有回答我的问题。 - Rudie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接