注意 'and 与 &&' 或 '|| 与 or' 之间的优先级差异。
<?php
$bool = true && false;
var_dump($bool); // false,预期结果
$bool = true and false;
var_dump($bool); // true,意外!
?>
因为 "and/or" 的优先级低于 "=",而 "||/&&" 的优先级更高。
运算符的优先级指定它将两个表达式“紧密地”绑定在一起的方式。例如,在表达式 1 + 5 * 3
中,答案是 16
而不是 18
,因为乘法(“*”)运算符的优先级高于加法(“+”)运算符。必要时,可以使用括号强制优先级。例如:(1 + 5) * 3
计算结果为 18
。
当运算符具有相同的优先级时,它们的结合性决定运算符的组合方式。例如,“-”是左结合的,所以 1 - 2 - 3
组合为 (1 - 2) - 3
并计算结果为 -4
。“=”另一方面是右结合的,所以 $a = $b = $c
组合为 $a = ($b = $c)
。
具有相同优先级的非结合运算符不能彼此相邻使用,例如 1 < 2 > 1
在 PHP 中是非法的。另一方面,表达式 1 <= 1 == 1
是合法的,因为 ==
运算符的优先级低于 <=
运算符。
结合性仅对二元(和三元)运算符有意义。一元运算符要么是前缀,要么是后缀,因此该概念不适用。例如,!!$a
只能组合为 !(!$a)
。
使用括号,即使在严格不需要时,也可以通过明确地进行分组,而不是依赖于隐式运算符优先级和结合性,从而提高代码的可读性。
下表按优先级顺序列出了运算符,优先级最高的运算符位于顶部。同一行上的运算符具有相同的优先级,在这种情况下,结合性决定分组。
结合性 | 运算符 | 其他信息 |
---|---|---|
(n/a) |
clone new |
clone 和 new |
右 | ** |
算术 |
(n/a) |
+ - ++ -- ~ (int) (float) (string) (array) (object) (bool) @ |
算术(一元 + 和 - )、自增/自减、位运算、类型转换 和 错误控制 |
左 | instanceof |
类型 |
(n/a) | ! |
逻辑 |
左 |
*
/
%
|
算术 |
左 |
+
-
.
|
算术(二元 + 和 - )、数组 和 字符串(在 PHP 8.0.0 之前为 . ) |
左 |
<<
>>
|
位运算 |
左 | . |
字符串(从 PHP 8.0.0 开始) |
非结合 |
<
<=
>
>=
|
比较 |
非结合 |
==
!=
===
!==
<>
<=>
|
比较 |
左 | & |
位运算 和 引用 |
左 | ^ |
位运算 |
左 | | |
位运算 |
左 | && |
逻辑 |
左 | || |
逻辑 |
右 | ?? |
空值合并 |
非结合 | ? : |
三元(在 PHP 8.0.0 之前为左结合) |
右 |
=
+=
-=
*=
**=
/=
.=
%=
&=
|=
^=
<<=
>>=
??=
|
赋值 |
(n/a) | yield from |
yield from |
(n/a) | yield |
yield |
(n/a) | print |
|
左 | and |
逻辑 |
左 | xor |
逻辑 |
左 | or |
逻辑 |
示例 #1 结合性
<?php
$a = 3 * 3 % 5; // (3 * 3) % 5 = 4
// 三元运算符的结合性与 C/C++ 不同
$a = true ? 0 : true ? 1 : 2; // (true ? 0 : true) ? 1 : 2 = 2 (在 PHP 8.0.0 之前)
$a = 1;
$b = 2;
$a = $b += 3; // $a = ($b += 3) -> $a = 5, $b = 5
?>
运算符优先级和结合性只决定表达式的分组方式,它们不指定求值顺序。PHP(在一般情况下)不指定表达式的求值顺序,应避免假定特定求值顺序的代码,因为行为可能会在 PHP 版本之间发生变化,或者取决于周围的代码。
示例 #2 未定义的求值顺序
<?php
$a = 1;
echo $a + $a++; // 可能打印 2 或 3
$i = 1;
$array[$i] = $i++; // 可能设置索引 1 或 2
?>
示例 #3 +
、-
和 .
具有相同的优先级(在 PHP 8.0.0 之前)
<?php
$x = 4;
// 这行代码可能导致意外输出:
echo "x minus one equals " . $x-1 . ", or so I hope\n";
// 因为它像这行代码一样进行求值(在 PHP 8.0.0 之前):
echo (("x minus one equals " . $x) - 1) . ", or so I hope\n";
// 可以使用括号来强制执行所需的优先级:
echo "x minus one equals " . ($x-1) . ", or so I hope\n";
?>
上面的示例将输出
-1, or so I hope -1, or so I hope x minus one equals 3, or so I hope
注意:
虽然
=
的优先级低于大多数其他运算符,但 PHP 仍然允许类似于以下的表达式:if (!$a = foo())
,在这种情况下,foo()
的返回值将放入 $a 中。
版本 | 说明 |
---|---|
8.0.0 | 字符串连接(. )现在的优先级低于算术加/减(+ 和 - )和按位左移/右移(<< 和 >> );以前它与 + 和 - 具有相同的优先级,并且优先级高于 << 和 >> 。 |
8.0.0 | 三元运算符(? : )现在是非结合的;以前它是左结合的。 |
7.4.0 | 依赖于字符串连接(. )相对于算术加/减(+ 或 - )或按位左移/右移(<< 或 >> )的优先级,即在未加括号的表达式中一起使用它们,已被弃用。 |
7.4.0 | 依赖于三元运算符(? : )的左结合性,即嵌套多个未加括号的三元运算符,已被弃用。 |
注意 'and 与 &&' 或 '|| 与 or' 之间的优先级差异。
<?php
$bool = true && false;
var_dump($bool); // false,预期结果
$bool = true and false;
var_dump($bool); // true,意外!
?>
因为 "and/or" 的优先级低于 "=",而 "||/&&" 的优先级更高。
如果您来这里是为了寻找 PHP 运算符的完整列表,请注意,此处的表格 *不* 完整。这里没有包含一些额外的运算符(或运算符式的标点符号),例如 "->"、"::" 和 "..."。
要查看一个真正全面的列表,请查看 "解析器令牌列表" 页面:https://php.net/manual/en/tokens.php
请注意,?? 的优先级较低,因此可能会导致意外结果
$a=[];
$a['aa']??'not set'
--> not set(预期结果)
但
"lets see if it is set".$a['aa']??'not set'
--> 注意:未定义的索引 aa
--> lets see if it is set
所以你需要使用括号
"lets see if it is set".($a['aa']??'not set')
注意位运算符和比较运算符的非寻常顺序,这在我的经验中经常会导致错误。例如
<?php if ( $flags & MASK == 1) do_something(); ?>
不会像你从其他语言中期望的那样工作。使用
<?php if (($flags & MASK) == 1) do_something(); ?>
在 PHP 中代替。
使用强制转换和三元运算符可能不清楚,
(了解以下内容很有用:declare(strict_types = 1))。
<?php
$num_str="5";
$i1 = (int) isset($num_str) ? $num_str : 0;
$i2 = (int) (isset($num_str) ? $num_str : 0);
var_dump($i1);
var_dump($i2);
?>
输出
string(1) "5"
int(5)
<?php
// 这里另一个棘手的事情是将 && 或 || 与三元 ?: 结合使用
$x && $y ? $a : $b; // ($x && $y) ? $a : $b;
// 而:
$x and $y ? $a : $b; // $x and ($y ? $a : $b);
?>
一个获取左移运算符(<<)结果的简单技巧,例如
15 << 2 = 15 * (2*2) = 60
15 << 3 = 15 * (2*2*2) = 120
15 << 5 = 15 * (2*2*2*2*2) = 480
等等...
所以是
(左边的数字)乘以(右边的数字)乘以 2。
右移运算符(>>)也是一样的,其中
(左边的数字)除以(右边的数字)乘以 2,例如
15 >> 2 = (15/2)/2 = 7/2 = 3(如果结果是十进制,则使用 floor 值)。
35 >> 3 = (((35/2)/2)/2 = (17/2)/2 = 8/2 = 4
// 不正确
$a = true ? 0 : true ? 1 : 2; // (true ? 0 : true) ? 1 : 2 = 2
// 不支持无括号的 `a ? b : c ? d : e`。请使用 `(a ? b : c) ? d : e` 或 `a ? b : (c ? d : e)`
// 正确
$a = (true ? 0 : true) ? 1 : 2; // (true ? 0 : true) ? 1 : 2 = 2
==> 更正文档。