PHP 的三元運算子 ?: 與 Null 合併運算子 ?? 是簡化條件判斷、賦予預設值的利器。然而,巢狀三元運算子的左結合性、兩者語意差異、PHP 8 新增的 match 運算式常被誤解。本文全面解析語法行為、陷阱案例與現代最佳實踐。
<?php
// 語法:condition ? valueIfTrue : valueIfFalse
$result = $condition ? 'true value' : 'false value';
// 等價於
if ($condition) {
$result = 'true value';
} else {
$result = 'false value';
}
?>
<?php
// PHP 5.3+:Elvis 運算子
$result = $value ?: 'default';
// 等價於
$result = $value ? $value : 'default';
// 注意:使用「鬆散比較」判斷真假
// 0, '', '0', [], null, false 都會使用 'default'
?>
<?php
// PHP 7.0+:?? 僅檢查 null/未定義
$result = $value ?? 'default';
// 等價於
$result = isset($value) ? $value : 'default';
// 差異示範
$value = '';
var_dump($value ?: 'default'); // 'default'(空字串為假值)
var_dump($value ?? 'default'); // ''(空字串非 null,保留原值)
$value = 0;
var_dump($value ?: 'default'); // 'default'
var_dump($value ?? 'default'); // 0
$value = null;
var_dump($value ?: 'default'); // 'default'
var_dump($value ?? 'default'); // 'default'
?>
| 運算子 | 檢查條件 | 適用場景 | 陷阱 |
|---|---|---|---|
?: (Elvis) | 鬆散真假值 | 提供「假值時」的預設值 | 0、''、'0'、[] 會觸發預設值 |
?? | 是否為 null/未定義 | 提供「未設定時」的預設值 | 不檢查空字串、0、false |
PHP 的三元運算子是左結合,這與大多數語言(右結合)不同:
<?php
// PHP 左結合:((a ? b : c) ? d : e)
$result = true ? 'a' : false ? 'b' : 'c';
// 解析為:(true ? 'a' : false) ? 'b' : 'c'
// 第一步:true ? 'a' : false → 'a'
// 第二步:'a' ? 'b' : 'c' → 'b'(非空字串為真)
// 結果:'b'
// 大多數語言(C, Java, JS)右結合:
// true ? 'a' : (false ? 'b' : 'c') → 'a'
?>
<?php
// 正確:明確分組
$result = $a ? 'x' : ($b ? 'y' : 'z');
// 或使用多行格式化
$result = $a
? 'x'
: ($b
? 'y'
: 'z'
);
// PHP 8+ 最佳替代:match 運算式
$result = match (true) {
$a => 'x',
$b => 'y',
default => 'z',
};
?>
<?php
// 配置優先級:環境變數 > 設定檔 > 預設值
$apiTimeout = $_ENV['API_TIMEOUT']
?? $config['api_timeout']
?? 30; // 秒
// 陣列鍵名安全存取
$userName = $_POST['user']['name'] ?? 'Guest';
?>
<?php
class User {
public function __construct(
public string $name,
public ?string $email = null,
public ?string $phone = null
) {}
public function toArray(): array {
return [
'name' => $this->name,
'email' => $this->email ?? '未提供',
'phone' => $this->phone ?? '未提供',
];
}
}
?>
<?php
// ❌ 反模式:副作用混雜在表達式中
$condition ? doSomething() : doOtherThing();
// ✅ 正確:分離邏輯與副作用
if ($condition) {
doSomething();
} else {
doOtherThing();
}
// ✅ 或用於純值計算
$status = $isActive ? 'active' : 'inactive';
?>
| 特性 | 三元運算子 | match 運算式 |
|---|---|---|
| 比較模式 | 鬆散 (==) | 嚴格 (===) |
| 回傳值 | 可選(語句) | 必須回傳(運算式) |
| Fall-through | 無 | 無(自動 break) |
| 多條件 | 巢狀難讀 | 清晰列表 |
| 型別檢查 | 靜態分析困難 | PHPStan/Psalm 完整支援 |
| 預設分支 | 隱性 | 明確 default |
<?php
// 狀態碼轉換
$message = match ($statusCode) {
200, 201 => '成功',
400 => '請求錯誤',
401 => '未授權',
403 => '禁止存取',
404 => '找不到資源',
500 => '伺服器錯誤',
default => '未知狀態',
};
// 型別安全的多條件
$result = match (true) {
$score >= 90 => 'A',
$score >= 80 => 'B',
$score >= 70 => 'C',
$score >= 60 => 'D',
default => 'F',
};
// 物件模式匹配(PHP 8.2+)
$result = match ($user) {
User {role: 'admin'} => '管理員',
User {role: 'editor', status: 'active'} => '編輯',
User {isBanned: true} => '封禁用戶',
default => '一般用戶',
};
?>
?? 比 isset() 略慢(需評估運算元),但可讀性高得多match(1) { '1' => 'string' } 不匹配(整數 !== 字串)三元運算子與 Null 合併運算子是 PHP 簡化條件邏輯的利器,但左結合性與語意差異是兩大陷阱。現代 PHP 開發建議:
??(null 安全)或 ?:(假值預設)match(PHP 8+),清晰、型別安全、無 fall-throughif/elseif/else,可讀性優於巧妙declare(strict_types=1) 與 PHPStan,靜態分析可捕獲大多數比較錯誤