2024 年 PHP 日本研討會

比較運算子

顧名思義,比較運算子允許您比較兩個值。您可能也會有興趣查看類型比較表,因為它們顯示了各種類型相關比較的範例。

比較運算子
範例 名稱 結果
$a == $b 等於 如果類型轉換後 $a 等於 $b,則為 true
$a === $b 完全相同 如果 $a 等於 $b 且它們的類型相同,則為 true
$a != $b 不等於 如果類型轉換後 $a 不等於 $b,則為 true
$a <> $b 不等於 如果類型轉換後 $a 不等於 $b,則為 true
$a !== $b 不相等 如果 $a 不等於 $b,或者它們的類型不同,則為 true
$a < $b 小於 如果 $a 嚴格小於 $b,則為 true
$a > $b 大於 如果 $a 嚴格大於 $b,則為 true
$a <= $b 小於或等於 如果 $a 小於或等於 $b,則為 true
$a >= $b 大於或等於 如果 $a 大於或等於 $b,則為 true
$a <=> $b 太空船運算子 (組合比較運算子) $a 小於、等於或大於 $b 時,分別返回小於、等於或大於零的 整數 值。

如果兩個運算元都是數值字串,或者一個運算元是數字而另一個是數值字串,則會以數值方式進行比較。這些規則也適用於 switch 陳述式。當比較為 ===!== 時,不會進行類型轉換,因為這涉及比較類型和值。

警告

在 PHP 8.0.0 之前,如果將 字串 與數字或數值字串進行比較,則會在執行比較之前將 字串 轉換為數字。這可能會導致意外的結果,如下例所示:

<?php
var_dump
(0 == "a");
var_dump("1" == "01");
var_dump("10" == "1e1");
var_dump(100 == "1e2");

switch (
"a") {
case
0:
echo
"0";
break;
case
"a":
echo
"a";
break;
}
?>

上述範例在 PHP 7 中的輸出

bool(true)
bool(true)
bool(true)
bool(true)
0

上述範例在 PHP 8 中的輸出

bool(false)
bool(true)
bool(true)
bool(true)
a

<?php
// Integers
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1

// Strings
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1

echo "a" <=> "aa"; // -1
echo "zz" <=> "aa"; // 1

// Arrays
echo [] <=> []; // 0
echo [1, 2, 3] <=> [1, 2, 3]; // 0
echo [1, 2, 3] <=> []; // 1
echo [1, 2, 3] <=> [1, 2, 1]; // 1
echo [1, 2, 3] <=> [1, 2, 4]; // -1

// Objects
$a = (object) ["a" => "b"];
$b = (object) ["a" => "b"];
echo
$a <=> $b; // 0

$a = (object) ["a" => "b"];
$b = (object) ["a" => "c"];
echo
$a <=> $b; // -1

$a = (object) ["a" => "c"];
$b = (object) ["a" => "b"];
echo
$a <=> $b; // 1

// not only values are compared; keys must match
$a = (object) ["a" => "b"];
$b = (object) ["b" => "b"];
echo
$a <=> $b; // 1

?>

對於各種類型,比較會根據下表(按順序)進行。

與各種類型的比較
運算元 1 的類型 運算元 2 的類型 結果
null字串 字串 null 轉換為 "",進行數值或詞彙比較
布林值null 任何值 將兩邊都轉換為 布林值false < true
物件 物件 內建類別可以定義自己的比較方式,不同類別無法比較,相同類別請參考 物件比較
字串資源整數浮點數 字串資源整數浮點數 將字串和資源轉換為數字,進行一般的數學運算
陣列 陣列 成員較少的陣列較小,如果運算元 1 的鍵在運算元 2 中找不到,則陣列無法比較,否則逐值比較(請參考以下範例)
物件 任何值 物件 總是較大
陣列 任何值 陣列 總是較大

範例 #1 布林值/null 比較

<?php
// 布林值和 null 總是以布林值進行比較
var_dump(1 == TRUE); // TRUE - 與 (bool) 1 == TRUE 相同
var_dump(0 == FALSE); // TRUE - 與 (bool) 0 == FALSE 相同
var_dump(100 < TRUE); // FALSE - 與 (bool) 100 < TRUE 相同
var_dump(-10 < FALSE);// FALSE - 與 (bool) -10 < FALSE 相同
var_dump(min(-100, -10, NULL, 10, 100)); // NULL - (bool) NULL < (bool) -100 即 FALSE < TRUE
?>

範例 #2 標準陣列比較的轉錄

<?php
// 使用標準比較運算子和太空船運算子比較陣列的方式如下。
function standard_array_compare($op1, $op2)
{
if (
count($op1) < count($op2)) {
return -
1; // $op1 < $op2
} elseif (count($op1) > count($op2)) {
return
1; // $op1 > $op2
}
foreach (
$op1 as $key => $val) {
if (!
array_key_exists($key, $op2)) {
return
1;
} elseif (
$val < $op2[$key]) {
return -
1;
} elseif (
$val > $op2[$key]) {
return
1;
}
}
return
0; // $op1 == $op2
}
?>

警告

浮點數的比較

由於浮點數在內部的表示方式,您不應該測試兩個浮點數是否相等。

請參閱浮點數的說明文件以了解更多資訊。

注意請注意,在比較不同類型的值時,尤其是比較整數布林值整數字串時,PHP 的類型自動轉換並不總是那麼明顯。因此,在大多數情況下,通常建議使用 ===!== 比較,而不是 ==!=

無法比較的數值

雖然識別比較(===!==)可以應用於任意值,但其他比較運算子只能應用於可比較的值。比較無法比較的值的結果是未定義的,不應該依賴它。

三元運算子

另一個條件運算子是「?:」(或三元)運算子。

範例 #3 指定預設值

<?php
// 三元運算子的使用範例
$action = (empty($_POST['action'])) ? 'default' : $_POST['action'];

// 以上等同於以下的 if/else 陳述式
if (empty($_POST['action'])) {
$action = 'default';
} else {
$action = $_POST['action'];
}
?>
表達式 (expr1) ? (expr2) : (expr3) 的求值結果為:如果 expr1 的求值結果為 true,則為 expr2;如果 expr1 的求值結果為 false,則為 expr3

可以省略三元運算子的中間部分。表達式 expr1 ?: expr3 的求值結果為:如果 expr1 的求值結果為 true,則為 expr1 的結果;否則為 expr3。在這種情況下,expr1 只會被求值一次。

注意 請注意,三元運算子是一個表達式,它求值的結果不是一個變數,而是一個表達式的結果。如果您想要透過參考傳回一個變數,這一點很重要。因此,在透過參考傳回的函式中,陳述式 return $var == 42 ? $a : $b; 將無法正常運作,並會發出警告。

注意事項:

建議避免「堆疊」三元表達式。與其他語言相比,PHP 在單個表達式中使用多個未加括號的三元運算子時的行為並不明顯。實際上,在 PHP 8.0.0 之前,三元表達式是左關聯求值的,而不是像大多數其他程式語言那樣右關聯求值。從 PHP 7.4.0 開始,依賴左關聯性已被棄用。從 PHP 8.0.0 開始,三元運算子是非關聯的。

範例 #4 不明顯的三元運算子行為

<?php
// 乍看之下,以下程式碼似乎會輸出 'true'
echo (true ? 'true' : false ? 't' : 'f');

// 然而,以上程式碼在 PHP 8.0.0 之前的實際輸出是 't'
// 這是因為三元表達式是左關聯的

// 以下是與上述程式碼相同但更清晰的版本
echo ((true ? 'true' : false) ? 't' : 'f');

// 在這裡,可以看到第一個表達式求值為 'true',而
// 'true' 又會被求值為 (bool) true,因此會返回第二個三元表達式的 true 分支。
?>

注意事項:

簡短三元運算子 (?:) 的串接是穩定的,並且行為合理。它會計算到第一個評估結果為非假值的參數。請注意,未定義的值仍然會引發警告。

範例 #5 簡短三元運算子串接

<?php
echo 0 ?: 1 ?: 2 ?: 3, PHP_EOL; //1
echo 0 ?: 0 ?: 2 ?: 3, PHP_EOL; //2
echo 0 ?: 0 ?: 0 ?: 3, PHP_EOL; //3
?>

空值合併運算子

另一個好用的簡寫運算子是 "??" (或稱空值合併) 運算子。

範例 #6 指定預設值

<?php
// 空值合併運算子的使用範例
$action = $_POST['action'] ?? 'default';

// 以上等同於以下的 if/else 陳述式
if (isset($_POST['action'])) {
$action = $_POST['action'];
} else {
$action = 'default';
}
?>
表達式 (expr1) ?? (expr2) 的計算結果為:如果 expr1null,則為 expr2,否則為 expr1

特別的是,如果左側值不存在,此運算子不會發出通知或警告,就像 isset() 一樣。這在處理陣列鍵值時特別有用。

注意請注意,空值合併運算子是一個表達式,它計算的結果不是變數,而是表達式的結果。如果您想要透過參考傳回變數,這一點很重要。因此,在透過參考傳回的函式中,return $foo ?? $bar; 陳述式將無法運作,並且會發出警告。

注意事項:

空值合併運算子的優先順序較低。這表示如果將其與其他運算子(例如字串串接或算術運算子)混合使用,則可能需要使用括號。

<?php
// 產生 $name 未定義的警告。
print 'Mr. ' . $name ?? 'Anonymous';

// 輸出 "Mr. Anonymous"
print 'Mr. ' . ($name ?? 'Anonymous');
?>

注意事項:

請注意,空值合併運算子允許簡單的巢狀使用。

範例 #7 巢狀空值合併運算子

<?php

$foo
= null;
$bar = null;
$baz = 1;
$qux = 2;

echo
$foo ?? $bar ?? $baz ?? $qux; // 輸出 1

?>

新增註解

使用者貢獻的註解 13 則註解

173
crazy888s at hotmail dot com
14 年前
我找不到太多關於堆疊新的三元運算子的資訊,所以我做了一些測試。

<?php
echo 0 ?: 1 ?: 2 ?: 3; //1
echo 1 ?: 0 ?: 3 ?: 2; //1
echo 2 ?: 1 ?: 0 ?: 3; //2
echo 3 ?: 2 ?: 1 ?: 0; //3

echo 0 ?: 1 ?: 2 ?: 3; //1
echo 0 ?: 0 ?: 2 ?: 3; //2
echo 0 ?: 0 ?: 0 ?: 3; //3
?>

它的運作方式符合預期,會在一個表達式群組中返回第一個非 false 的值。
8
Sumon Mahmud
4 年前
從這裡延伸:https://php.dev.org.tw/manual/en/language.operators.comparison.php#121907

$a = ['a' => 1, 'b' => 2, 'c' => 3, 'e' => 4];
$b = ['a' => 1, 'b' => 2, 'd' => 3, 'e' => 4];

echo $a > $b; // 0
echo $b > $a; // 0
echo $a <$b; // 0
echo $b < $a; // 0

如果使用太空船運算子,則會返回 true,例如

echo $a <=> $b; //1
echo $b <=> $a; //1
echo $a <=> $b; //1
echo $b <=> $a; //1
3
Hayley Watson
1 年前
在「簡短三元運算子」(又稱「Elvis 運算子」)和「太空船運算子」之間,您可以為 usort 及其同類函數編寫一些相當精簡的比較函數。

如果您想按照多個不同的鍵值對關聯式陣列進行排序,您可以像在 SQL ORDER BY 子句中列出欄位名稱一樣,將它們串連起來。

<?php
usort
($array, fn($a, $b) => $a['a'] <=> $b['a']
?:
$b['b'] <=> $a['b']
?:
$a['c'] <=> $b['c']);
?>
會依欄位 'a' 排序陣列,然後依欄位 'b' 遞減排序,再依欄位 'c' 排序;或者用 SQL 的說法就是 'ORDER BY a, b DESC, c'
23
adam at caucho dot com
18 年前
注意:根據規範,PHP 的比較運算子不具遞移性。例如,以下在 PHP5 中皆為真

"11" < "a" < 2 < "11"

因此,排序陣列的結果取決於元素在排序前陣列中出現的順序。以下程式碼將會印出兩個排序 *不同* 的陣列

<?php
$a
= array(2, "a", "11", 2);
$b = array(2, "11", "a", 2);
sort($a);
var_dump($a);
sort($b);
var_dump($b);
?>

這不是錯誤回報 —— 根據此文件頁面上的規範,PHP 的行為是「正確的」。但這可能不是預期的結果...
9
Tahazzot
3 年前
閱讀 PHP 文件時要非常小心,這裡有很多錯誤資訊。

根據文件,他們說 (int) 0 == (string) "a" 為真。但在 PHP 8 中並非如此。

var_dump(0 == "a"); // 0 == 0 -> true

現在在 PHP 8 中是 False。
8
admin at zeros dot co dot id
2 年前
當您嘗試比較開頭帶有加號 `+` 的字串(例如電話號碼等)時,請務必小心。當您使用等於運算子 `==` 時,PHP 會忽略加號。請改用完全相同運算子 `===`

範例

$str1 = "62";
$str2 = "+62";

var_dump($str1 == $str2); // bool(true)
var_dump($str1 === $str2); // bool(false)
18
rshawiii at yahoo dot com
18 年前
您不能僅使用 === 運算子來比較兩個陣列
就像您認為的那樣,要找出它們是否相等。當您有多維陣列時,這會更加複雜。這裡有一個遞迴比較函式。

<?php
/**
* 比較兩個陣列,查看它們是否包含相同的值。返回 TRUE 或 FALSE。
* 可用於判斷記錄或數據塊是否已被修改(可能由使用者輸入)
* 在設定「上次更新日期」之前,或在沒有變更的情況下跳過更新資料庫。
*
* @param array $a1
* @param array $a2
* @return boolean
*/
function array_compare_recursive($a1, $a2)
{
if (!(
is_array($a1) and (is_array($a2)))) { return FALSE;}

if (!
count($a1) == count($a2))
{
return
FALSE; // 陣列的項目數量不同
}

foreach (
$a1 as $key => $val)
{
if (!
array_key_exists($key, $a2))
{return
FALSE; // 無法比較的陣列鍵不匹配
}
elseif (
is_array($val) and is_array($a2[$key])) // 如果兩個項目都是陣列,則遞迴比較
{if (!array_compare_recursive($val,$a2[$key])) return FALSE;
}
elseif (!(
$val === $a2[$key])) // 比較的項目必須是同一類型。
{return FALSE;
}
}
return
TRUE; // $a1 === $a2
}
?>
3
gfilippakis at sleed dot gr
1 年前
請注意,使用 null 聯合運算子來檢查具有 __get 魔術方法(沒有 __isset 魔術方法)的類別的屬性時,會調用魔術方法。

例如:

<?php

class A
{
public function
__get($property)
{
echo
'呼叫 __get,屬性為 ' . $property . PHP_EOL;
}
}

$a = new A();

echo
'嘗試使用空值合併運算子' . PHP_EOL;
$b = $a->test ?? 5;

echo
'嘗試使用 isset()' . PHP_EOL;
if (isset(
$a->test)) {
$b = $a->test;
} else {
$b = 5;
}

?>
13
bishop
18 年前
當您想要知道兩個陣列是否包含相同的值,而不考慮值的順序時,您不能使用 "==" 或 "==="。換句話說

<?php
(array(1,2) == array(2,1)) === false;
?>

要回答這個問題,請使用

<?php
function array_equal($a, $b) {
return (
is_array($a) && is_array($b) && array_diff($a, $b) === array_diff($b, $a));
}
?>

一個相關但更嚴格的問題是,如果您需要確保兩個陣列包含相同的鍵值對,而不考慮鍵值對的順序。在這種情況下,請使用

<?php
function array_identical($a, $b) {
return (
is_array($a) && is_array($b) && array_diff_assoc($a, $b) === array_diff_assoc($b, $a));
}
?>

範例
<?php
$a
= array (2, 1);
$b = array (1, 2);
// true === array_equal($a, $b);
// false === array_identical($a, $b);

$a = array ('a' => 2, 'b' => 1);
$b = array ('b' => 1, 'a' => 2);
// true === array_identical($a, $b)
// true === array_equal($a, $b)
?>

(另請參閱「rshawiii at yahoo dot com」發表的解決方案)
4
niall at maranelda dot org
7 年前
使用太空船運算符比較鍵值不同的陣列時必須小心。

- 與上述註釋(「範例 #2 標準陣列比較的轉錄」)相反,如果左邊的陣列包含右邊陣列沒有的鍵值,它*不會*返回 null。
- 正因為如此,結果取決於您進行比較的順序。

例如:

<?php
$a
= ['a' => 1, 'b' => 2, 'c' => 3, 'e' => 4];
$b = ['a' => 1, 'b' => 2, 'd' => 3, 'e' => 4];

var_dump($a <=> $b); // int(1) : $a > $b 因為 $a 有 'c' 鍵而 $b 沒有。

var_dump($b <=> $a); // int(1) : $b > $a 因為 $b 有 'd' 鍵而 $a 沒有。
?>
3
Ryan Mott
5 年前
搜尋「雙問號」運算符應該可以找到這個頁面(希望在此評論之後,爬蟲程式會認同)。
6
Cuong Huy To
13 年前
在「與各種類型的比較」表格中,請將關於「物件」的最後一行移到「陣列」行的上方,因為物件被認為大於陣列(在 5.3.3 上測試)。

(請移除我之前發表的相同內容的「匿名」文章。您可以檢查 IP 位址,就知道我忘記輸入我的名字了。)
2
Marcin Kuzawiski
9 年前
A < B 但 B < A...

$A = [1 => 1, 2 => 0, 3 => 1];
$B = [1 => 1, 3 => 0, 2 => 1];

var_dump($A < $B); // TRUE
var_dump($B < $A); // TRUE

var_dump($A > $B); // TRUE
var_dump($B > $A); // TRUE

下一步 - C 和 D 可比較,但 C < D 和 D < C 都不成立(而且 C != D)…

$C = [1 => 1, 2 => 1, 3 => 0];
$D = [1 => 1, 3 => 1, 2 => 0];

var_dump($C < $D); // FALSE(假)
var_dump($D < $C); // FALSE(假)

var_dump($C > $D); // FALSE(假)
var_dump($D > $C); // FALSE(假)

var_dump($D == $C); // FALSE(假)
To Top