2019年12月8日

比較 PHP 問號問號 ?? 及問號冒號 ?: 的差別 - Null Coalescing Operator vs Ternary Operator

本篇為PHP系列

兩個問號 ?? 及問號冒號 ?: 的差別其實不大,兩者在使用方式上的差異,主要在於什麼時候回傳第二個變數。

三元運算子

要聊 ?? ?: 不得不先提一下三元運算子 (ternary),三元運算子的用法是像這樣:

$a ? $a : $b;

如果 $a 可以被視為 TRUE 時,則回傳 $a,若 $a 被視為 FALSE 時,則回傳 $b

大概是這樣的感覺:

if ($a) {
    return $a;
} else {
    return $b;
}

當然也可以這樣寫:

$a ? $b : $c;

那就是這樣的意思:

if ($a) {
    return $b;
} else {
    return $c;
}

哪些值可以被視為 true 或 false,可以參考: PHP type comparison tables

基本上常見的空字串""null0、空陣列[]等,都算是 FALSE

?? 運算子 Null Coalescing Operator

兩個問號 ?? 其實就是Null coalescing operator,直接翻譯是 null連接運算子(真的是??)。

那這個東西其實是 PHP 7 之後才有的新功能,擷取官方說明文件的說明:

The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.

稍微翻譯一下這段話如下:

對於需要結合三元運算子及 isset() 的常見使用案例,null coalescing operator已經作為一個語法糖被加入 PHP 了。如果第一個運算元存在不是 NULL ,就會回傳第一個運算元,否則就會回傳第二個。

總結而言, ???: 搭配 isset() 使用的語法糖。

換句話說:如果第一個變數的 isset 檢查通過(變數存在且非 NULL),那就會回傳第一個變數,否則則回傳第二個,

舉個例子來說,原本我們想要判斷第一個變數有沒有存在,若沒有則回傳第二個,使用傳統的三元運算子,寫法會像是:

isset($a) ? $a : $b;

其實也就等同於這樣的感覺:

if (isset($a)) {
    return $a;
} else {
    return $b;
}

有了null coalescing operator以後,可以簡化成:

$a ?? $b;

另外,由於 isset() 的定義是:

Determine if a variable is declared and is different than NULL。

所以只有同時滿足這兩個條件:

  1. 變數有被宣告
  2. 其值不是 NULL

isset()才會回傳TRUE

也就是說,即便該變數有宣告,但它的值卻是 NULLisset() 也會是 FALSE

看看實際的案例:

/// 1. $a不存在,回傳第二個運算元$b的值"b"。
$b = "b";
var_dump($a ?? $b); // "b"

// 2. $a存在,且不為NULL,回傳$a的值"a"。
$a = "a";
var_dump($a ?? $b); // "a"

// 3. $a存在,且不為NULL(false !== NUll),回傳$a目前的值false。
$a = false;
var_dump($a ?? $b); // false

// 4. $a存在,卻為NULL,回傳$b的值"b"。
$a = null;
var_dump($a ?? $b); // "b"

// 5. 由左讀到右,$a、$b、$c都是NULL,所以都回傳$d的值"d"。
$a = null;
$b = null;
$c = null;
$d = "d";
var_dump($a ?? $b ?? $c ?? $d); // "d"
var_dump($a ?? $b ?? $c ?? $d ?? $e); // "d"

?:運算子

?:其實是短版的三元運算子(shorthand ternary operator)。

有些時候我們並不需要用到三個變數:

$a ? $a : $b;

這種就可以用 ?: 來省略:

$a ?: $b;

所以 ???: 最大的差異其實就是 isset() 而已。

?? → 第一個變數是 null 或未宣告,回傳第二個變數,否則回傳第一個。

?: → 第一個變數是等同於 False,回傳第二個變數,否則回傳第一個。

再比較一下 ?:??

PHP Notice

?:遇到未宣告的變數會跳 PHP Notice

<?php
$b = "b";
echo $a ?: $b;
PHP Notice:  Undefined variable: a

但是 ?? 不會跳 PHP Notice

<?php
$b = "b";
echo $a ?? $b; 
"b"

對於 false 的判斷

<?php
$a = false;
$b = "b";
var_dump($a ?: $b); // "b"
var_dump($a ?? $b); // false

參考資料

看更多PHP系列