in_array() 是 PHP 判斷值是否存在於數組的內建函數,但**只能檢測一維數組**。面對多維數組直接使用會永遠返回 false。本文深度解析原理,提供遞歸與迭代兩種通用解決方案,並對比性能優劣。
<?php
$os = ['Mac', 'NT', 'Irix', 'Linux'];
var_dump(in_array('Irix', $os)); // true
var_dump(in_array('mac', $os)); // false(區分大小寫)
var_dump(in_array('mac', $os, true)); // false(嚴格模式:類型+值)
$a = ['1.10', 12.4, 1.13];
var_dump(in_array('12.4', $a, true)); // false(字符串 vs 浮點數)
var_dump(in_array(1.13, $a, true)); // true
// 支持陣列作為 needle
$a = [['p', 'h'], ['p', 'r'], 'o'];
var_dump(in_array(['p', 'h'], $a)); // true
var_dump(in_array(['f', 'i'], $a)); // false
var_dump(in_array('o', $a)); // true
?>
<?php
$arr = [
['a', 'b'],
['c', 'd']
];
var_dump(in_array('a', $arr)); // bool(false) —— 只檢查頂層元素:['a','b'] 和 ['c','d']
// 'a' 既不等於 ['a','b'] 也不等於 ['c','d']
?>
根因:in_array 遍歷數組的**第一層鍵值**,將每個值與 $needle 比較。多維數組的第一層值是子陣列,而非標量,自然匹配失敗。
<?php
/**
* 遞歸檢查值是否存在於多維數組任意層級
* @param mixed $needle 要查找的值
* @param array $haystack 被搜索的數組
* @param bool $strict 是否嚴格模式(類型+值)
* @return bool
*/
function deep_in_array($needle, $haystack, $strict = false) {
foreach ($haystack as $item) {
if (is_array($item)) {
// 子陣列:遞歸搜索
if (deep_in_array($needle, $item, $strict)) {
return true;
}
} else {
// 標量值:直接比較
if ($strict ? $item === $needle : $item == $needle) {
return true;
}
}
}
return false;
}
// 測試
$arr = [
'level1' => [
'level2' => [
'target' => 'found_me',
'other' => 123
],
'also_here' => 'found_me'
],
'top_level' => 'not_target'
];
var_dump(deep_in_array('found_me', $arr)); // true
var_dump(deep_in_array('found_me', $arr, true)); // true
var_dump(deep_in_array(123, $arr)); // true
var_dump(deep_in_array('123', $arr, true)); // false(嚴格模式)
var_dump(deep_in_array('not_exist', $arr)); // false
?>
<?php
/**
* 使用 RecursiveIteratorIterator 扁平化多維數組
* 適合超大、超深數組,避免棧溢出
*/
function deep_in_array_iter($needle, $haystack, $strict = false) {
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($haystack),
RecursiveIteratorIterator::LEAVES_ONLY // 只遍歷葉子節點(標量值)
);
foreach ($iterator as $value) {
if ($strict ? $value === $needle : $value == $needle) {
return true;
}
}
return false;
}
// 性能對比:百萬級數組
$big = range(1, 1000000);
$big = array_chunk($big, 1000); // 二維
$big = array_chunk($big, 100); // 三維
$start = microtime(true);
deep_in_array(999999, $big);
echo "遞歸耗時: " . (microtime(true) - $start) . "s\n";
$start = microtime(true);
deep_in_array_iter(999999, $big);
echo "迭代器耗時: " . (microtime(true) - $start) . "s\n";
/*
典型輸出:
遞歸耗時: 0.045s
迭代器耗時: 0.012s ← 迭代器更快、無棧溢出風險
*/
?>
<?php
function deep_in_array_walk($needle, $haystack, $strict = false) {
$found = false;
array_walk_recursive($haystack, function($value) use ($needle, $strict, &$found) {
if ($found) return; // 無法真正短路,只是跳過回調
if ($strict ? $value === $needle : $value == $needle) {
$found = true;
}
});
return $found;
}
// 缺點:必須遍歷完所有元素,性能差,不推薦大數組
?>
<?php
function deep_in_array_flatten($needle, $haystack, $strict = false) {
$flat = [];
array_walk_recursive($haystack, function($v) use (&$flat) { $flat[] = $v; });
return in_array($needle, $flat, $strict);
}
// 適用於小數組、多次查找同一數組(扁平化一次、多次 in_array)
?>
| 方案 | 時間複雜度 | 空間複雜度 | 遞歸深度限制 | 短路優化 | 推薦場景 |
|---|---|---|---|---|---|
| 遞歸函數 | O(n) | O(d) 棧 | 有(PHP 默認 ~256,可調) | 是 | 中小數組、代碼簡潔優先 |
| RecursiveIteratorIterator | O(n) | O(d) 堆 | 無(C 層實現) | 是 | 大數組、深層嵌套、高性能要求 |
| array_walk_recursive | O(n) | O(d) 棧 | 有 | 否(偽短路) | 不推薦 |
| 扁平化 + in_array | O(n) + O(m) | O(n) | 有 | 是 | 同一數組多次查找、預處理場景 |
<?php
class ArrayHelper {
/**
* 多維數組任意層級查找值
*/
public static function deepInArray($needle, $haystack, $strict = false) {
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($haystack),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $value) {
if ($strict ? $value === $needle : $value == $needle) {
return true;
}
}
return false;
}
/**
* 多維數組查找鍵是否存在
*/
public static function deepKeyExists($key, $haystack) {
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($haystack),
RecursiveIteratorIterator::CATCH_GET_CHILD
);
foreach ($iterator as $k => $v) {
if ($k === $key) return true;
}
return false;
}
/**
* 多維數組提取所有符合條件的值
*/
public static function deepFindAll($callback, $haystack) {
$results = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($haystack),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $key => $value) {
if ($callback($value, $key)) {
$results[] = $value;
}
}
return $results;
}
}
// 使用
$data = ['a' => [1,2,['target'=>3]], 'b' => 4];
var_dump(ArrayHelper::deepInArray(3, $data)); // true
var_dump(ArrayHelper::deepKeyExists('target', $data)); // true
print_r(ArrayHelper::deepFindAll(fn($v) => $v > 2, $data)); // [3, 4]
?>
in_array **不支持多維數組**。生產代碼**首選 RecursiveIteratorIterator 迭代器方案**:無遞歸深度限制、C 層實現性能優、支持短路、語義清晰。遞歸函數適合中小項目快速開發。封裝為工具類統一項目口徑,避免重複造輪子與 Bug 滋生。