注意 'and vs &&' 或 '|| vs 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)
。
優先順序相同且無結合性的運算子不能並排使用,例如在 PHP 中,`1 < 2 > 1` 是非法的。另一方面,`1 <= 1 == 1` 是合法的,因為 `==` 運算子的優先順序低於 `<=` 運算子。
結合性僅對二元(和三元)運算子有意義。一元運算子是前置或後置的,因此此概念不適用。例如 `!!$a` 只能分組為 `!(!$a)`。
使用括號,即使在非嚴格必要的情況下,通常也可以通過明確分組而不是依賴隱式運算子優先順序和結合性來提高程式碼的可讀性。
下表列出了運算子的優先順序,優先順序最高的在頂部。位於同一行的運算子具有相同的優先順序,在這種情況下,結合性決定分組。
結合性 | 運算子 | 其他資訊 |
---|---|---|
(無) | `clone` `new` | clone 和 new |
右 | ** |
算術 |
(無) | `+` `-` `++` `--` `~` `(int)` `(float)` `(string)` `(array)` `(object)` `(bool)` `@` | 算術(一元 `+` 和 `-`)、遞增/遞減、位元、類型轉換 和 錯誤控制 |
左 | instanceof |
類型 |
(無) | ! |
邏輯 |
左 |
*
/
%
|
算術 |
左 |
+
-
.
|
算術(二元 `+` 和 `-`)、陣列 和 字串(PHP 8.0.0 之前為 `.`) |
左 |
<<
>>
|
位元 |
左 | . |
字串(自 PHP 8.0.0 起) |
無結合性 |
<
<=
>
>=
|
比較 |
無結合性 |
==
!=
===
!==
<>
<=>
|
比較 |
左 | & |
位元 和 參考 |
左 | ^ |
位元 |
左 | | |
位元 |
左 | && |
邏輯 |
左 | || |
邏輯 |
右 | ?? |
空值合併 |
無結合性 | ? : |
三元(PHP 8.0.0 之前為左結合性) |
右 |
=
+=
-=
*=
**=
/=
.=
%=
&=
|=
^=
<<=
>>=
??=
|
賦值 |
(無) | yield from |
yield from |
(無) | yield |
yield |
(無) | 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 減 1 等於 " . $x-1 . ", 希望如此\n";
// 因為它會像這行程式碼一樣被求值 (PHP 8.0.0 之前版本):
echo (("x 減 1 等於 " . $x) - 1) . ", 希望如此\n";
// 可以使用括號來強制執行所需的優先順序:
echo "x 減 1 等於 " . ($x-1) . ", 希望如此\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 vs &&' 或 '|| vs 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.dev.org.tw/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
==> 文件修正。