PHP中的布尔赋值运算符

21

我发现自己经常会做这种事情:

$foo = true;
$foo = $foo && false; // bool(false)
使用位运算符,您可以使用&=|=简写方式:
$foo = 1;
$foo &= 0; // int(0)

鉴于对 10 的位运算在功能上等同于对 truefalse 的布尔运算,我们可以依靠类型转换并执行类似以下代码:

$foo = true;
$foo &= false; // int(0)
$foo = (bool)$foo; // bool(false)

...但这很丑陋,也违背了使用简写赋值语法的初衷,因为我们必须使用另一条语句来将类型转回布尔值。

我真正想做的是像这样:

$foo = true;
$foo &&= false; // bool(false)

...但是&&=||=不是有效的运算符,这很明显。所以,我的问题是 - 是否有其他甜美的语法或者可能作为替代的晦涩核心函数?对于变量像$foo那样简短,只使用$foo = $foo && false语法并不是什么大问题,但是多维数组元素和/或对象方法调用可以使语法相当冗长。


$foo = $foo && false; 有什么问题吗?还是只是好奇心作祟? - Dany Caissy
2
@DanyCaissy:我在上一个段落中解释了。这并没有什么“问题”;只是这种语法是多余的,而且可能会变得非常冗长(例如$some['big']['long']['variable'] = $some['big']['long']['variable'] && $some['other']['boolean'];)。 - FtDRbwLXw6
你可能需要更改你的示例。从逻辑上讲,这些示例始终会产生相同的结果 = $foo &= false;$foo &&= false; 对于 $foo = true。所以我看不到问题/目标在哪里。 - Jason McCreary
2
@JasonMcCreary: 这取决于你对“同样”的定义。正如我的例子所指出的那样,前者会产生int(0),而后者会产生bool(false)。因此,尽管由于隐式类型转换,0 == false,但0 !== false - FtDRbwLXw6
这仍然与 PHP7/8 类型强制检查有关,7年后仍然相关。如果使用短语法很容易意外地从布尔值转换为整数。 - evo_rob
如果邮件未发送,则知道$mailsSent = (Swift_Mailer)$mailer->send($message)false,我一直在连续使用$mailsSent &= $mailer->send($message2)。经过一年的使用,这是我第一次在第一封和第二封邮件组中有不同数量的收件人。今天我发现了两件事。首先,如果邮件成功发送,则send()方法返回发送的邮件数而不是true。其次,&=是一个按位赋值运算符,意味着1 + 2 = 0。应该更仔细地阅读文档。通过(bool)$mailer->send($message)解决了这个问题。 - s3c
3个回答

6

从某种意义上说,你已经回答了自己的问题:

对于1和0的按位运算在功能上等同于对true和false的布尔运算

需要注意的是,PHP是一种弱类型语言,所以不必将其强制转换为严格的布尔值,因为10等同于truefalse(除了严格相等,见下文)。

考虑以下代码,使用你的示例:

$foo = true;
$foo &= false;

if (!$foo) {
  echo 'Bitwise works!';
}

$bar = true;
$bar = $bar && false;

if (!$bar) {
  echo 'Boolean works!';
}

// Output: Bitwise works!Boolean works!

给定PHP的隐式类型转换假值,除了严格相等之外,我很难看到这样的简写操作&&=||=不会产生与&=|=相同的结果。特别是在评估布尔值时。这可能就是为什么这样的简写在PHP中不存在的原因。

更新

一些快速的基准测试证明了这些确实是等效的,除了真实的数组/对象:

<?php
$values = array(false, 0, 0.0, "", "0", array(), 12, "string", array(1));

foreach ($values as $value) {
    $bit_test = true;
    $bit_test &= $value;

    $bool_test = true;
    $bool_test = $bool_test && false;
    if ($bit_test != $bool_test) {
        echo 'Difference for: ';
        var_dump($value);
    }
}

// Output:
// Difference for: array(1) {
//  [0]=>
//  int(1)
// }

1
你说得没错,但我实际上正在寻找一种不改变类型的解决方案(这就是为什么我的第三个示例明确地将其强制转换回“bool”)。我可以控制两个操作数,并且它们保证是“bool”,因此我更愿意使用较长的语法,而不是通过返回“falsy”值而不是接口文档中记录的严格“bool”来引入代码错误。 - FtDRbwLXw6
1
尽管PHP是一种弱类型、动态语言,但我支持编写这样明确的代码。我的答案基于这样一个事实:对于布尔值(以及大多数其他简单类型),按位运算符等同于布尔运算符。但如果你想使用这样的速记方式,你必须放弃严格的相等检查(或者与0/1进行比较)。最终,我真的看不出有什么意义。尽管如此,我还是想为未来的读者提供一个答案。 - Jason McCreary
记住,在返回值之前,您可以始终将其转换。这也是一个好的实践方式。 - Jason McCreary
@JasonMcCreary 不,布尔运算符和位运算符并不等同,尽管它们的输出相同。最大的区别在于,如果结果没有任何差异,PHP甚至不执行布尔操作的表达式。因此,$res = sql_query() OR die();$res = sql_query(); $res |= die();并不相等。 - Matmarbon
1
@Matmarbon,没错,在这些评论中我忘了一个关键的限定词——在功能上等效。 - Jason McCreary
很遗憾,对于强类型检查来说这不再是真的了@JasonMcCreary。$result = true; $result &= somefun()会将$result的类型从bool更改为int。我仍然认为在php中有布尔赋值运算符的用例。事实上,这就是我来到这里的原因。 - theking2

2
正如Jason所提到的,位运算符可以使用,并且不需要将结果转换回布尔值,因为PHP会正确地将它们的值处理为布尔值。
如果您想要一种不使用位运算符的替代方法,以增加可读性或者如果您想要严格相等,您可以使用以下方法:
function OrOp(&$booleanVar, $conditions)
{
    $booleanVar = $booleanVar && $conditions;
    return $booleanVar;
}

这意味着,您可以更改此内容:

$foo = $foo && false;

将此转换为:

OrOp($foo, false);

它也可以适用于多个条件:

OrOp($foo, $condition1 && $condition2 && $condition3);

2
虽然有帮助,但似乎OP正在寻找一些本地的、能够编写自定义代码的东西。 - Jason McCreary
1
我认为没有什么本地的东西可以为他们完成这项任务,这就是为什么我把它放在这里的原因。如果我被证明是错误的,我会删除我的答案。虽然我相信 OP 可以自己想出这种方法,但是那些经验不足的未来读者可能会发现它有用。 - Dany Caissy
有人考虑将这个问题提交给 PHP 开发团队吗? - theking2
我在 GitHub 上做了这件事:[https://github.com/php/php-src/issues/8312] - theking2

1

没错,PHP确实缺少&&=||=运算符,因为位运算符不能作为替代品使用(除非进行强制转换)。

以下是一个例子,你期望它返回true,但实际上返回false

$a = true;
$a &= 10; // => returns 0 (false) because 1 & 10 = 0

缺失的&&=运算符将返回true,因为1 && 10 = true
$a = true;
$a &&= 10; // => $a would be true

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