注意“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”的优先级低于“=”,而“||/&&”的优先级更高。
注意 ?? 具有较低的优先级,因此可能导致意外结果。
$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 运算符的完整列表,请注意,此处的表格*并不*完整。这里没有包含一些额外的运算符(或类似运算符的标点符号),例如“->”、“::”和“…”。
要获得真正全面的列表,请查看“解析器标记列表”页面:https://php.net/manual/en/tokens.php
小心位运算符和比较运算符的不同寻常的顺序,根据我的经验,这经常会导致错误。例如
<?php if ( $flags & MASK == 1) do_something(); ?>
不会像你从其他语言中期望的那样工作。在 PHP 中使用
<?php if (($flags & MASK) == 1) do_something(); ?>
代替。
使用强制类型转换和三元运算符可能不清楚,
(与以下情况一起使用时需要注意: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(如果结果为小数,则使用向下取整的值)。
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
==> 文档更正。