核心概念區分
- 未初始化:從未賦值的變數
- NULL:明確賦值
null 或 unset() 後
- 空值:
''、0、0.0、'0'、false、[]、無屬性物件
五大函式/運算子對比表
| 表達式 | 未初始化 | NULL | ''/0/false/[] | 非空值 | 主要用途 |
isset($v) | false | false | true | true | 檢查變數是否存在且非 NULL |
empty($v) | true | true | true | false | 檢查是否為「空值」(廣義) |
is_null($v) | 錯誤 | true | false | false | 嚴格檢查 NULL |
$v == null | 錯誤 | true | true | false | 鬆散比較 NULL/空值 |
$v === null | 錯誤 | true | false | false | 嚴格比較 NULL(同 is_null) |
程式碼驗證
<?php
$cases = [
'uninit' => null, // 實際無法直接表示,需透過 unset
'null' => null,
'emptyStr' => '',
'zero' => 0,
'false' => false,
'emptyArr' => [],
'nonEmpty' => 'hello',
];
// 模擬未初始化
unset($uninit);
$cases['uninit'] = &$uninit; // 只為演示,實際會報錯
foreach ($cases as $name => $val) {
echo "=== $name ===\n";
@$isset = isset($val); // @ 抑制未初始化錯誤
@$empty = empty($val);
@$isnull = is_null($val);
@$eq = $val == null;
@$id = $val === null;
echo "isset: " . ($isset ? 'T' : 'F') . "\n";
echo "empty: " . ($empty ? 'T' : 'F') . "\n";
echo "is_null: " . ($isnull ? 'T' : 'F') . "\n";
echo "== null: " . ($eq ? 'T' : 'F') . "\n";
echo "=== null: " . ($id ? 'T' : 'F') . "\n";
}
實務選擇指南
| 場景 | 推薦 | 理由 |
| 檢查陣列鍵是否存在 | isset($arr['key']) | 不觸發錯誤、效能最好、不視空值為不存在 |
| 檢查表單必填欄位 | empty($_POST['field']) | 涵蓋未提交、空字串、0 等皆視為未填 |
| 檢查變數明確為 null | $var === null | 避免 0/false/'' 干擾、語意明確 |
| 函式參數預設值檢查 | func_num_args() 或 ...$args | 區分「未傳遞」與「傳遞 null」 |
| 資料庫 NULL 欄位判斷 | is_null($row['col']) | 明確語意、配合嚴格模式 |
常見錯誤
<?php
// 錯誤:empty 檢查不存在變數不報錯,但 isset 也不報錯
// 但 empty 會將 '0' 視為空,導致 ID 為 0 的記錄被誤判
if (empty($_GET['id'])) { /* ... */ } // id=0 時進入這裡!
// 正確:isset 檢查存在,再驗證值
if (!isset($_GET['id']) || $_GET['id'] === '') { /* 真正缺失 */ }
// 錯誤:is_null 未初始化會報錯
if (is_null($maybeUndefined)) { /* ... */ }
// 正確:先 isset 或用 nullsafe 運算子 PHP 8+
if ($maybeUndefined === null) { /* ... */ } // 前提是變數存在
PHP 8.1+ 新語法
<?php
// nullsafe 運算子
$value = $obj?->method()?->prop; // 任一環節為 null 直接回傳 null
// 不再支援在未定義變數上使用 is_null/=== null
// 會拋出 Error: Undefined variable