in_array() 預設**鬆散比較 (loose comparison)**,導致 0 == 'abc' 為 true 等經典誤判。本文深度剖析弱型別比較行為、**第三參數 $strict=true 的關鍵作用**、效能差異、多維陣列搜尋替代方案,並給出**防禦性程式設計清單**。
一、經典誤判現場
var_dump(in_array(0, ['a', 'b', 'c'])); // true ❌ 鬆散比較: 0 == 'a' -> true
var_dump(in_array(0, ['a', 'b', 'c'], true)); // false ✅ 嚴格比較: 0 === 'a' -> false
var_dump(in_array('abc', [0, 1, 2])); // true ❌ 'abc' == 0 -> true
var_dump(in_array('123', [123], true)); // false ✅ 字串 !== 整數
二、比較模式差異表
| 模式 | 參數 | 比較運算子 | 型別轉換 | 速度 |
| 鬆散 (預設) | in_array($needle, $haystack) | == | 是 (數值字串轉數值等) | 基準 |
| 嚴格 | in_array($needle, $haystack, true) | === | 否 (型別與值皆需相同) | 略快 (無轉換開銷) |
三、為何預設鬆散比較?
- 歷史包袱:PHP 早期設計為「網頁模板語言」,表單輸入皆為字串,鬆散比較方便
in_array($_POST['id'], [1,2,3]) 直接判斷。
- 現代最佳實務:**永遠啟用嚴格模式**,除非明確需要型別轉換語意。
四、效能微基測 (100 萬次,陣列 1000 元素)
in_array($needle, $arr, true):~180ms
in_array($needle, $arr):~210ms (型別轉換開銷)
isset($flip[$needle]) (鍵名翻轉):~15ms **快 10 倍**
五、大陣列/高頻搜尋優化策略
| 策略 | 適用場景 | 代碼 |
| 鍵名翻轉 + isset | 靜態陣列、頻繁查詢、純量值 | $flip = array_flip($arr); return isset($flip[$needle]); |
| array_flip 一次性建立 | 陣列不變、生命週期長 | 建構時同步維護 $map |
| in_array + strict | 動態陣列、小規模、需值比較 | in_array($v, $arr, true) |
| 資料庫索引 / Redis Set | 海量資料、跨請求共享 | SISMEMBER key value |
六、多維陣列/物件搜尋:in_array 無能為力
$users = [
['id'=>1, 'name'=>'Alice'],
['id'=>2, 'name'=>'Bob'],
];
// 錯誤用法
in_array(['id'=>1], $users); // 總是 false (陣列比較需嚴格且順序相同)
// 正確:array_filter / foreach / array_column
$found = array_filter($users, fn($u) => $u['id'] === 1);
$idExists = in_array(1, array_column($users, 'id'), true); // 只查 id 欄位
七、防禦性清單 (Code Review 必檢)
- ✅ 所有
in_array() **明確加上第三參數** (true 或 false),消除隱性預設。
- ✅ 搜尋整數 ID 時,**確保陣列元素也是整數** (避免字串 '1' vs 整數 1)。
- ✅ 陣列來源為外部輸入 (JSON、資料庫) 時,**先統一型別轉換** (
array_map('intval', $arr))。
- ✅ 靜態白名單/黑名單**定義為鍵名陣列** (
['admin'=>true, 'root'=>true]),用 isset() O(1) 查詢。
- ✅ 針對物件陣列,實作
__equals() 或使用 spl_object_id() 比較身分。
八、相關函數對照
| 函數 | 用途 | 嚴格模式 |
| in_array | 值搜尋 | 第三參數 |
| array_search | 值搜尋並回傳鍵名 | 第三參數 |
| array_key_exists | 鍵名搜尋 | 天生嚴格 (===) |
| isset($arr[$key]) | 鍵名存在且非 null | 天生嚴格、最快 |
**鐵律**:**永遠寫 in_array($needle, $haystack, true)**,除非你能在代碼審查中口頭解釋「這裡必須用鬆散比較且理由充分」。這一習慣能消除 99% 型別比較 Bug。