PHP 8.5.0 RC 5 available for testing

array_walk

(PHP 4, PHP 5, PHP 7, PHP 8)

array_walk使用用户自定义函数对数组中的每个元素做回调处理

说明

array_walk(array|object &$array, callable $callback, mixed $arg = null): true

将用户自定义函数 callback 应用到 array 数组中的每个单元。

array_walk() 不会受到 array 内部数组指针的影响。array_walk() 会遍历整个数组而不管指针的位置。

参数

array

输入的数组。

callback

典型情况下 callback 接受两个参数。array 参数的值作为第一个,键名作为第二个。

注意:

如果 callback 需要直接作用于数组中的值,则给 callback 的第一个参数指定为引用。这样任何对这些单元的改变也将会改变原始数组本身。

注意:

许多内部函数(例如 strtolower())在传入超出预期数量的参数时会抛出异常,并且无法直接作为 callback 使用。

只有 array 的值才可以被改变,用户不应在回调函数中改变该数组本身的结构。例如增加/删除单元,unset 单元等等。如果 array_walk() 作用的数组改变了,则此函数的的行为未经定义,且不可预期。

arg

如果提供了可选参数 arg,将被作为第三个参数传递给 callback

返回值

总是返回 true

错误/异常

从 PHP 7.1.0 起,如果 callback 函数需要的参数(数组成员的值和键)超过2个或者 也传递了 arg 参数需要的参数超过3个时,会抛出 ArgumentCountError。 在此之前,每次 array_walk() 调用 callback 的时候都会生成 E_WARNING 级别的错误。

更新日志

版本 说明
8.2.0 现在返回类型为 true;之前是 bool
8.0.0 如果 callback 期待通过引用传递第二个和第三个参数, 此函数现在将抛出 E_WARNING

示例

示例 #1 array_walk() 示例

<?php
$fruits
= array("d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple");

function
test_alter(&$item1, $key, $prefix)
{
$item1 = "$prefix: $item1";
}

function
test_print($item2, $key)
{
echo
"$key. $item2\n";
}

echo
"Before ...:\n";
array_walk($fruits, 'test_print');

array_walk($fruits, 'test_alter', 'fruit');
echo
"... and after:\n";

array_walk($fruits, 'test_print');
?>

以上示例会输出:

Before ...:
d. lemon
a. orange
b. banana
c. apple
... and after:
d. fruit: lemon
a. fruit: orange
b. fruit: banana
c. fruit: apple

示例 #2 使用匿名函数的 array_walk() 示例

<?php
$elements
= ['a', 'b', 'c'];
array_walk($elements, function ($value, $key) {
echo
"{$key} => {$value}\n";
});
?>

以上示例会输出:

0 => a
1 => b
2 => c

参见

添加备注

用户贡献的备注 15 notes

up
253
bisqwit at iki dot fi
21 years ago
It's worth nothing that array_walk can not be used to change keys in the array.
The function may be defined as (&$value, $key) but not (&$value, &$key).
Even though PHP does not complain/warn, it does not modify the key.
up
72
ezhacher at gmail dot com
11 years ago
Calling an array Walk inside a class

If the class is static:
array_walk($array, array('self', 'walkFunction'));
or
array_walk($array, array('className', 'walkFunction'));

Otherwise:
array_walk($array, array($this, 'walkFunction'));
up
48
01001coder at gmail dot com
7 years ago
I noticed that :

PHP ignored arguments type when using array_walk() even if there was

declare(strict_types=1) .

See this code as an example ...

<?php
declare(strict_types=1);

$fruits = array("butter" => 5.3, "meat" => 7, "banana" => 3);

function
test_print(int $item2, $key) {
echo
"$key: $item2<br />\n";
}

array_walk($fruits, 'test_print');

?>

The output is :

butter: 5
meat: 7
banana: 3

whilst the expecting output is :

Fatal error: Uncaught TypeError: Argument 1 passed to test_print() must be of the type integer

because "butter" => 5.3 is float

I asked someone about it and they said "this was caused by the fact that callbacks called from internal code will always use weak type". But I tried to do some tests and this behavior is not an issue when using call_user_func().
up
3
ludvig dot ericson at gmail dot com
19 years ago
In response to 'ibolmo', this is an extended version of string_walk, allowing to pass userdata (like array_walk) and to have the function edit the string in the same manner as array_walk allows, note now though that you have to pass a variable, since PHP cannot pass string literals by reference (logically).

<?php
function string_walk(&$string, $funcname, $userdata = null) {
for(
$i = 0; $i < strlen($string); $i++) {
# NOTE: PHP's dereference sucks, we have to do this.
$hack = $string{$i};
call_user_func($funcname, &$hack, $i, $userdata);
$string{$i} = $hack;
}
}

function
yourFunc($value, $position) {
echo
$value . ' ';
}

function
yourOtherFunc(&$value, $position) {
$value = str_rot13($value);
}

# NOTE: We now need this ugly $x = hack.
string_walk($x = 'interesting', 'yourFunc');
// Ouput: i n t e r e s t i n g

string_walk($x = 'interesting', 'yourOtherFunc');
echo
$x;
// Output: vagrerfgvat
?>

Also note that calling str_rot13() directly on $x would be much faster ;-) just a sample.
up
18
Maxim
14 years ago
Note that using array_walk with intval is inappropriate.
There are many examples on internet that suggest to use following code to safely escape $_POST arrays of integers:
<?php
array_walk
($_POST['something'],'intval'); // does nothing in PHP 5.3.3
?>
It works in _some_ older PHP versions (5.2), but is against specifications. Since intval() does not modify it's arguments, but returns modified result, the code above has no effect on the array and will leave security hole in your website.

You can use following instead:
<?php
$_POST
['something'] = array_map(intval,$_POST['something']);
?>
up
8
chaley at brtransport dot com
11 years ago
There is a note about 3 years ago regarding using this for trimming. array_map() may be cleaner for this. I haven't checked the time/resource impact:

$result = array_map("trim", $array);
up
12
erelsgl at gmail dot com
16 years ago
If you want to unset elements from the callback function, maybe what you really need is array_filter.
up
18
rustamabd at gmail dot com
15 years ago
Don't forget about the array_map() function, it may be easier to use!

Here's how to lower-case all elements in an array:

<?php
$arr
= array_map('strtolower', $arr);
?>
up
10
fantomx1 at gmail dot com
9 years ago
Since array_walk cannot modify / change / reindex keys as already mentioned, i provide this small wrapping function which accomplishes passing array reference and index using closures , "use" keyword.

function indexArrayByElement($array, $element)
{
$arrayReindexed = [];
array_walk(
$array,
function ($item, $key) use (&$arrayReindexed, $element) {
$arrayReindexed[$item[$element]] = $item;
}
);
return $arrayReindexed;
}
up
10
taj at yahoo dot fr
6 years ago
// We can make that with this simple FOREACH loop :

$fruits = array("d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple");

foreach($fruits as $cls => $vls)
{
$fruits[$cls] = "fruit: ".$vls;
}

Results:

Array
(
[d] => fruit: lemon
[a] => fruit: orange
[b] => fruit: banana
[c] => fruit: apple
)
up
3
alex_stanhope at hotmail dot com
14 years ago
I wanted to walk an array and reverse map it into a second array. I decided to use array_walk because it should be faster than a reset,next loop or foreach(x as &$y) loop.

<?php
$output
= array();
array_walk($input, 'gmapmark_reverse', $output);

function
gmapmark_reverse(&$item, $index, &$target) {
$target[$item['form_key']] = $index;
}
?>

In my debugger I can see that $target is progressively updated, but when array_walk returns, $output is empty. If however I use a (deprecated) call-by-reference:

<?php
array_walk
($input, 'gmapmark_reverse', &$output);
?>

$output is returned correctly. Unfortunately there's not an easy way to suppress the warnings:

<?php
@array_walk($input, 'gmapmark_reverse', &$output);
?>

doesn't silence them. I've designed a workaround using a static array:

<?php
$reverse
= array();
array_walk($input, 'gmapmark_reverse');
// call function one last time to get target array out, because parameters don't work
$reverse = gmapmark_reverse($reverse);

function
gmapmark_reverse(&$item, $index = 0) {
static
$target;
if (!
$target) {
$target = array();
}
if (isset(
$item['form_key'])) {
$target[$item['form_key']] = $index;
}
return(
$target);
}
?>
up
13
Andrzej Martynowicz at gmail dot com
20 years ago
It can be very useful to pass the third (optional) parameter by reference while modifying it permanently in callback function. This will cause passing modified parameter to next iteration of array_walk(). The exaple below enumerates items in the array:

<?php
function enumerate( &$item1, $key, &$startNum ) {
$item1 = $startNum++ ." $item1";
}

$num = 1;

$fruits = array( "lemon", "orange", "banana", "apple");
array_walk($fruits, 'enumerate', $num );

print_r( $fruits );

echo
'$num is: '. $num ."\n";
?>

This outputs:

Array
(
[0] => 1 lemon
[1] => 2 orange
[2] => 3 banana
[3] => 4 apple
)
$num is: 1

Notice at the last line of output that outside of array_walk() the $num parameter has initial value of 1. This is because array_walk() does not take the third parameter by reference.. so what if we pass the reference as the optional parameter..

<?php
$num
= 1;

$fruits = array( "lemon", "orange", "banana", "apple");
array_walk($fruits, 'enumerate', &$num ); // reference here

print_r( $fruits );

echo
'$num is: '. $num ."\n";
echo
"we've got ". ($num - 1) ." fruits in the basket!";
?>

This outputs:
Array
(
[0] => 1 lemon
[1] => 2 orange
[2] => 3 banana
[3] => 4 apple
)
$num is: 5
we've got 4 fruits in the basket!

Now $num has changed so we are able to count the items (without calling count() unnecessarily).

As a conclusion, using references with array_walk() can be powerful toy but this should be done carefully since modifying third parameter outside the array_walk() is not always what we want.
up
4
EllisGL
7 years ago
For those that think they can't use array_walk to change / replace a key name, here you go:

<?php
function array_explore(array &$array, callable $callback)
{
array_walk($array, function(&$value, $key) use (&$array, $callback)
{
$callback($array, $key, $value);

if(
is_array($value))
{
array_explore($value, $callback);
}
});
}

/**
* Stolen from: https://stackoverflow.com/questions/13233405/change-key-in-associative-array-in-php
*/
function renameKey(array &$data, $oldKey, $newKey, $ignoreMissing = false, $replaceExisting = false)
{
if (!empty(
$data))
{
if (!
array_key_exists($oldKey, $data))
{
if (
$ignoreMissing)
{
return
FALSE;
}

throw new
\Exception('Old key does not exist.');
}
else
{
if (
array_key_exists($newKey, $data))
{
if (
$replaceExisting)
{
unset(
$data[$newKey]);
}
else
{
throw new
\Exception('New key already exist.');
}
}

$keys = array_keys($data);

// Fix from EllisGL: http://php.net/manual/en/function.array-search.php#122377
$keys[array_search($oldKey, array_map('strval', $keys))] = $newKey;

$data = array_combine($keys, $data);

return
TRUE;
}
}

return
FALSE;
}

$array = [
"_10fish" => 'xyz',
"_11fish" => [
"_22" => "a", "b", "c"
],
"someFish" => [
'xyz',
'@attributes' => ['type' => 'cod']
]
];

array_explore($array, function(&$value, $key)
{
// Replace key '@attrutes' with '_attributes'
if('@attributes' === $key)
{
renameKey($value, $key, '_attributes');
}

});

print_r($array);
?>
up
6
matthew at codenaked dot org
15 years ago
Using lambdas you can create a handy zip function to zip together the keys and values of an array. I extended it to allow you to pass in the "glue" string as the optional userdata parameter. The following example is used to zip an array of email headers:

<?php

/**
* Zip together the keys and values of an array using the provided glue
*
* The values of the array are replaced with the new computed value
*
* @param array $data
* @param string $glue
*/
function zip(&$data, $glue=': ')
{
if(!
is_array($data)) {
throw new
InvalidArgumentException('First parameter must be an array');
}

array_walk($data, function(&$value, $key, $joinUsing) {
$value = $key . $joinUsing . $value;
},
$glue);
}

$myName = 'Matthew Purdon';
$myEmail = '[email protected]';
$from = "$myName <$myEmail>";

$headers['From'] = $from;
$headers['Reply-To'] = $from;
$headers['Return-path'] = "<$myEmail>";
$headers['X-Mailer'] = "PHP" . phpversion() . "";
$headers['Content-Type'] = 'text/plain; charset="UTF-8"';

zip($headers);

$headers = implode("\n", $headers);
$headers .= "\n";

echo
$headers;

/*
From: Matthew Purdon <[email protected]>
Reply-To: Matthew Purdon <[email protected]>
Return-path: <[email protected]>
X-Mailer: PHP5.3.2
Content-Type: text/plain; charset="UTF-8"
*/
?>
up
3
manuscle at gmail dot com
13 years ago
example with closures, checking and deleting value in array:

<?php
$array
= array('foo' => 'bar', 'baz' => 'bat');

array_walk($array, function($val,$key) use(&$array){
if (
$val == 'bar') {
unset(
$array[$key]);
}
});

var_dump($array);
To Top