in_array() 是 PHP 檢查值是否存在於陣列的常用函式,但其預設弱型別比較特性常導致難以察覺的 Bug。本文將深入剖析型別轉換陷阱、展示實際誤判案例,並建立嚴格模式使用規範。
<?php // 經典誤判案例 var_dump(in_array(0, ['s', 'sss'])); // true ← 驚人結果! var_dump(in_array(0, ['s', 'sss'], true)); // false ← 嚴格模式正確結果 var_dump(in_array(0, [1, 2, 3])); // false // 為什麼?PHP 將字串 's' 轉換為整數 0 進行比較 // (int)'s' === 0 // (int)'123abc' === 123 ?>
| 比較值 | 陣列值 | 弱型別結果 | 嚴格模式結果 | 轉換邏輯 |
|---|---|---|---|---|
| 0 (int) | 'abc' (string) | true | false | 字串轉 int = 0 |
| 0 (int) | '' (空字串) | true | false | 空字串轉 int = 0 |
| false (bool) | 0 (int) | true | false | bool/int 互轉 |
| null | '' (空字串) | true | false | null/空字串/0 互轉 |
| '0' (string) | 0 (int) | true | false | 字串數字轉 int |
| 123 (int) | '123abc' (string) | true | false | 字串前導數字轉 int |
<?php
// 危險代碼
$allowedRoles = ['admin', 'editor', 'author'];
$userRole = 0; // 可能來自資料庫整數欄位、API 回傳等
if (in_array($userRole, $allowedRoles)) {
grantAccess(); // 驚人:0 通過檢查!因為 (int)'admin' === 0
}
// 安全寫法
if (in_array($userRole, $allowedRoles, true)) { // 嚴格模式
grantAccess();
}
?>
<?php
$validStatus = ['pending', 'approved', 'rejected'];
$inputStatus = $_POST['status'] ?? ''; // 使用者輸入 "0"
// 弱型別:'0' == 0,而 (int)'pending' === 0 → true
if (in_array($inputStatus, $validStatus)) { ... } // 通過驗證!
// 嚴格模式:'0' !== 'pending' → false
if (in_array($inputStatus, $validStatus, true)) { ... } // 正確攔截
?>
<?php
$cacheKeys = ['user_1', 'user_2', 'config'];
$checkKey = 0; // 來自某個計算結果
// 錯誤:誤判存在
if (in_array($checkKey, $cacheKeys)) {
// 執行快取邏輯...
}
?>
<?php
// 專案規範:所有 in_array 必須顯式指定嚴格模式
function arrayContains(array $haystack, $needle): bool {
return in_array($needle, $haystack, true);
}
// 使用範例
if (arrayContains($allowedRoles, $userRole)) { ... }
?>
<?php
// 輸入正規化:確保比較雙方型別一致
function normalizeForCheck($value, $targetType = 'string') {
switch ($targetType) {
case 'string': return (string)$value;
case 'int': return (int)$value;
case 'bool': return (bool)$value;
default: return $value;
}
}
$allowedRoles = ['admin', 'editor'];
$userRole = normalizeForCheck($userRole, 'string'); // 強制字串
if (in_array($userRole, $allowedRoles, true)) { ... }
?>
<?php
// 將值作為鍵名,查找效能從 O(n) 降為 O(1)
$allowedRolesMap = array_flip(['admin', 'editor', 'author']);
// ['admin' => 0, 'editor' => 1, 'author' => 2]
// 檢查存在性
if (isset($allowedRolesMap[$userRole])) { ... }
// 注意:此法要求陣列值唯一且適合作鍵名
?>
<?php
$largeArray = range(1, 100000);
$searchValue = 99999;
// in_array: O(n) 線性搜尋
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
in_array($searchValue, $largeArray, true);
}
echo "in_array: " . (microtime(true) - $start) . "s\n";
// isset: O(1) 雜湊查找
$flipped = array_flip($largeArray);
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
isset($flipped[$searchValue]);
}
echo "isset: " . (microtime(true) - $start) . "s\n";
// 典型結果:isset 快 100-1000 倍
?>
in_array() 預設行為未變,仍需嚴格模式array_is_list() 搭配鍵值檢查Arr::contains()、Symfony ArrayUtils::contains() 內建嚴格模式in_array() 呼叫是否加上 , true?isset($flipped[$key])?鐵律:除非極少數明確需要弱型別比較的場景,所有 in_array() 必須使用嚴格模式 in_array($needle, $haystack, true)。這一行代碼能避免 99% 的型別相關 Bug。