一、函数定位与核心机制
array_walk是PHP内置的数组遍历函数,从PHP4.0开始提供。它的作用是把用户自定义的函数应用到数组的每一个元素上。
名字里的walk可以理解为「走过一遍」,逐个访问数组元素,每访问一个就调用一次回调。
这个函数和array_map容易混,但两者的设计思路不同:
-
array_map:把数组元素映射成新值,返回一个新数组,不修改原数组
-
array_walk:遍历数组元素,回调通过引用可以直接修改原数组,返回布尔值
简单说,array_map是「转换」,array_walk是「访问并可能修改」。这个区别决定了它们的适用场景。
语法结构
bool array_walk(array &$array, callable $callback, mixed $userdata = null): bool
参数说明:
| 参数 | 是否必需 | 说明 |
|---|---|---|
| array | 必需 | 输入数组,传引用 |
| callback | 必需 | 用户自定义的回调函数 |
| userdata | 可选 | 传递给回调的第三个参数 |
返回值是布尔值:成功返回true,失败返回false。
回调参数顺序
array_walk的回调接收两个必需参数,顺序是值在前、键在后:
function codehub_callback($value, $key) {
// $value 是当前元素的值
// $key 是当前元素的键名
}
这个顺序和很多人的直觉相反。一般说到「键值对」,习惯先键后值,但array_walk的回调是先值后键。这一点在第一次用时容易写反,导致输出结果错位。写反了不会报错,只是逻辑不对,排查起来要花点时间。
如果传了userdata,回调的第三个参数会收到它:
function codehub_callback($value, $key, $extra) {
// $extra 是 userdata 传进来的值
}
二、传引用修改原数组
array_walk的第一个参数是&$array,传引用。但更关键的是,回调的第一个参数也可以声明为引用,这样就能直接修改数组元素的值。
$numbers = array(1, 2, 3, 4);
array_walk($numbers, function(&$value, $key) {
$value = $value * 2;
});
print_r($numbers);
输出:
Array
(
[0] => 2
[1] => 4
[2] => 6
[3] => 8
)
回调参数前加了&,value成为原数组元素的引用,赋值直接改写原数组。如果回调参数不加`&`,value是副本,修改它不影响原数组。
这是array_walk和array_map的一个核心差异。array_map无法修改原数组,只能返回新数组;array_walk通过引用可以直接改写原数组,节省一次数组复制。
个人经验:在批量格式化数据的场景里,比如把用户列表里的手机号中间四位打码、把金额字段统一转成两位小数,array_walk配合引用比array_map更直接。array_map需要接收返回值再赋回原变量,array_walk一步到位。不过要注意,如果原数组后续还要用到原始数据,就不能用引用修改,得先用array_map生成新数组。
三、userdata第三参数的用法
第三个参数userdata是可选的,传给回调作为第三个参数。它的作用是把外部数据带进回调,避免用全局变量。
$prefix = "codehub_";
$langs = array("php", "java", "python");
array_walk($langs, function($value, $key, $prefix) {
echo $prefix . $value . "\n";
}, $prefix);
输出:
codehub_php
codehub_java
codehub_python
不用userdata的话,就得把$prefix声明成全局变量,或者用use关键字闭包捕获。userdata提供了更干净的传参方式。
注意一点:userdata是按值传递的,回调里修改它不会影响外部变量。如果需要回调修改外部数据,得传引用或者用对象。
四、项目实战示例
示例一:基础遍历输出
<?php
function codehub_show($value, $key) {
echo "The key $key has the value $value \n";
}
$tech = array("a" => "", "b" => "google", "c" => "whatsapp");
array_walk($tech, "codehub_show");
输出:
The key a has the value
The key b has the value google
The key c has the value whatsapp
回调先接收值,再接收键。遍历顺序按数组内部顺序,不额外排序。
示例二:带前缀的遍历
<?php
function codehub_prefix($value, $key) {
echo "codehub $key has $value \n";
}
$tutorials = array("tutorial1" => "php", "tutorial2" => "java", "tutorial3" => "python");
array_walk($tutorials, "codehub_prefix");
输出:
codehub tutorial1 has php
codehub tutorial2 has java
codehub tutorial3 has python
回调里拼接固定前缀,适合生成日志、生成报告标题这类场景。
示例三:引用修改数组元素
<?php
$prices = array("apple" => 5, "banana" => 3, "orange" => 4);
array_walk($prices, function(&$value, $key) {
$value = $value * 1.1;
});
print_r($prices);
输出:
Array
(
[apple] => 5.5
[banana] => 3.3
[orange] => 4.4
)
回调参数加引用,直接改写原数组的值。这个用法在批量调价、批量格式转换场景里很实用。
示例四:用userdata传外部配置
<?php
$config = array("prefix" => "news_", "suffix" => "_2026");
$news = array("a" => "aajtak", "b" => "zee", "c" => "ndtv");
array_walk($news, function($value, $key, $config) {
echo $config["prefix"] . $value . $config["suffix"] . "\n";
}, $config);
输出:
news_aajtak_2026
news_zee_2026
news_ndtv_2026
userdata可以传数组,把多个外部参数打包进去。这样回调不用依赖全局变量,代码更清晰。
示例五:结合键名做条件处理
<?php
$scores = array("math" => 88, "english" => 72, "science" => 95);
array_walk($scores, function(&$value, $key) {
if ($key === "english") {
$value = $value + 5;
}
});
print_r($scores);
输出:
Array
(
[math] => 88
[english] => 77
[science] => 95
)
回调里可以根据键名做条件判断,只修改特定元素。这个能力是array_map不具备的,因为array_map的回调拿不到键名。
五、容易踩的坑
回调参数顺序写反。array_walk的回调是先值后键,和很多人的直觉相反。写反了不报错,但逻辑全错。这一点在第一次用时容易中招。
想修改原数组但回调参数没加引用。回调参数不加&,$value是副本,修改它不影响原数组。很多人以为array_walk传引用了就能改原数组,实际上第一个参数的引用只保证函数能操作原数组,回调里的修改还需要单独加引用。
userdata不能直接修改外部变量。userdata按值传递,回调里改它不影响外部。需要修改外部数据时,传引用或传对象。
回调里不要做重操作。array_walk遍历整个数组,每个元素调用一次回调。如果回调里做数据库查询、文件读写、网络请求,性能会随数组长度线性下降。这类操作应该先批量查询,再在回调里做内存处理。
array_walk不返回新数组。它的返回值是布尔值,不是处理后的数组。如果需要处理后的新数组,用array_map。如果需要同时保留原数组和处理结果,可以用array_map生成新数组,或者复制一份原数组再用array_walk引用修改。
对多维数组只处理最外层。array_walk只遍历最外层元素,子数组会被当作一个值传给回调。如果需要递归处理多维数组,用array_walk_recursive。这一点在树形结构数据处理时要注意。
回调不能是内置函数。像strtoupper、trim这类内置函数,参数签名和array_walk的回调不匹配(内置函数通常只接收一个参数,或者参数顺序不同),直接传给array_walk会报错。要包装成闭包再用:
array_walk($arr, function(&$v, $k) {
$v = strtoupper($v);
});
六、知识点笔记
-
array_walk遍历数组,对每个元素调用一次回调
-
回调参数顺序是值在前、键在后
-
回调参数声明为引用时,可以直接修改原数组元素
-
第一个参数传引用,函数操作原数组
-
返回布尔值,成功true,失败false
-
userdata是可选的第三参数,按值传递给回调
-
与array_map的区别:array_walk可修改原数组、能拿到键名、不返回新数组
-
多维数组递归处理用array_walk_recursive
-
回调里避免做数据库查询、网络请求这类重操作
七、适用场景
这个函数适合以下场景:
-
批量格式化数组元素(金额、日期、字符串)
-
遍历数组生成日志或报告
-
需要按键名做条件处理的场景
-
需要直接修改原数组、不想生成新数组的场景
-
多维数组递归处理(用array_walk_recursive)
不适合的场景:
-
需要返回新数组、保留原数组不变,用array_map
-
简单的元素转换,用array_map更直观
-
需要过滤元素,用array_filter
-
需要聚合结果,用array_reduce