array_walk_recursive() 是 PHP 陣列操作的利器,能遞迴遍歷多維陣列的每個「葉子節點」(非陣列值),對每個值執行回呼函式。不同於 array_walk() 僅處理第一層,它自動深入巢狀結構,極適合資料清洗、編碼轉換、XSS 過濾、敏感字遮罩等場景。
array_walk_recursive(array &$array, callable $callback, mixed $userdata = null): bool
| 參數 | 說明 |
|---|---|
&$array | 輸入陣列,傳址呼叫,回呼中修改值會直接影響原陣列 |
$callback | 回呼函式,簽名 func(mixed $value, string|int $key, mixed $userdata)。若需修改原值,第一參數必須加 &:func(&$value, $key) |
$userdata | 可選,傳遞額外資料給回呼(如配置選項、服務物件) |
function htmlEscapeRecursive(array &$data): void {
array_walk_recursive($data, function(&$value, $key) {
if (is_string($value)) {
$value = htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
});
}
// 使用
$data = [
'title' => '',
'user' => ['name' => 'Tom', 'bio' => ['desc' => 'hello & world']]
];
htmlEscapeRecursive($data);
// 結果:所有字串值已轉義,結構保持不變
function convertEncodingRecursive(array &$data, string $from, string $to): void {
array_walk_recursive($data, function(&$value, $key) use ($from, $to) {
if (is_string($value)) {
$value = mb_convert_encoding($value, $to, $from);
}
});
}
function maskSensitiveRecursive(array &$data): void {
$patterns = [
'/(\d{3})\d{4}(\d{4})/' => '$1****$2', // 手機
'/(\w{1,3})[\w.-]*@/' => '$1***@', // Email 前綴
'/(\d{6})\d{8}(\d{4})/' => '$1********$2', // 身分證
];
array_walk_recursive($data, function(&$value, $key) use ($patterns) {
if (is_string($value)) {
foreach ($patterns as $pattern => $replacement) {
$value = preg_replace($pattern, $replacement, $value);
}
}
});
}
function trimStringsRecursive(array &$data, array $options = []): void {
$charset = $options['charset'] ?? 'UTF-8';
$skipKeys = $options['skip_keys'] ?? [];
array_walk_recursive($data, function(&$value, $key) use ($charset, $skipKeys) {
if (in_array($key, $skipKeys, true)) return;
if (is_string($value)) {
$value = trim($value);
// 可選:移除控制字元
$value = preg_replace('/[\x00-\x1F\x7F]/', '', $value);
}
});
}
// 使用
trimStringsRecursive($input, ['skip_keys' => ['password', 'token']]);
| 誤區 | 正確認知 |
|---|---|
| 以為會遍歷陣列鍵名 | 只遍歷「非陣列值」(葉子節點),中間層陣列不會觸發回呼 |
回呼未用 &$value 導致原陣列未修改 | 必須傳址 function(&$value) 才能修改原陣列 |
試圖在回呼中刪除元素 unset($array[$key]) | 無效,$key 是葉子鍵名,且陣列在內部迭代中,刪除會導致未定義行為 |
| 處理巨大陣列導致記憶體/遞迴深度溢出 | PHP 遞迴深度受限於 xdebug.max_nesting_level (預設 256),超深結構改用迭代器 RecursiveArrayIterator |
// 10 萬筆扁平陣列 $data = array_fill(0, 100000, ['a' => 'x', 'b' => ['c' => 'y']]); // array_walk_recursive:約 45ms // 手寫遞迴 foreach:約 38ms (較快,但程式碼冗長) // RecursiveIteratorIterator:約 52ms (物件開銷大) // 結論:中小型陣列用 array_walk_recursive 開發效率最高;極大/極深陣列考慮 Generator + 迭代器串流處理
yield 實現延遲處理,適合串流管線。array_is_list()、array_find() 等新函式,配合使用更強大。善用 array_walk_recursive(),能以極少程式碼完成多維陣列的統一轉換與清洗。記住「傳址修改、僅葉子節點、避免深度溢出」三原則,即可安全高效地應用於專案中。