PHPのお勉強!

PHP TOP

array_walk

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

array_walk配列の全ての要素にユーザー定義の関数を適用する

説明

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

array 配列の各要素にユーザー定義関数 callback を適用します。

array_walk()array の内部配列ポインタに影響されません。array_walk() はポインタの位置に関わらず配列の全てに渡って適用されます。

パラメータ

array

入力の配列。

callback

通常、 callback は引数を二つとります。 array パラメータの値が最初の引数、 キー/添字は二番目の引数となります。

注意:

callback により配列の値そのものを変更する必要がある場合、 callback の最初の引数は リファレンス として渡す必要があります。この場合、配列の要素に加えた変更は、 配列自体に対して行われます。

注意:

内部関数の多く (たとえば strtolower()) は、 期待しているよりも多い引数を受け取ると警告が発生します。 そのため、callback に直接指定することはできません。

変更される可能性があるのは配列の値だけであり、 その構造を変更することはできません。つまり、 プログラマは要素の追加や削除そして並べ替えなどができないということです。 コールバック関数がこの条件を満たさない場合は、 この関数の動作は未定義で予期せぬ結果を得ることになります。

arg

オプションの arg パラメータが指定された場合、 コールバック関数 callback への三番目の引数として渡されます。

戻り値

常に true を返します。

エラー / 例外

PHP 7.1.0 以降では、callback 関数が 2個より多いパラメータ(配列メンバーの値およびキー)を必要とする場合、 または、(arg も渡されている場合) 3個より多いパラメータを必要とする場合、 ArgumentCountError がスローされます。 7.1.0 より前のバージョンでは、callback 関数が 2 つより多いパラメータを必要とする場合、 array_walk()callback をコールする度にエラーレベル E_WARNING が 発生させていました。

変更履歴

バージョン 説明
8.2.0 戻り値の型が、true になりました。これより前のバージョンでは、bool でした。
8.0.0 callback が、 2番目と3番目の引数にリファレンス渡しされる引数を期待している場合、 この関数は 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

参考

  • array_walk_recursive() - 配列の全ての要素に、ユーザー関数を再帰的に適用する
  • iterator_apply() - ユーザー関数をイテレータのすべての要素でコールする
  • list() - 配列と同様の形式で、複数の変数への代入を行う
  • each() - 配列から現在のキーと値のペアを返して、カーソルを進める
  • call_user_func_array() - パラメータの配列を指定してコールバック関数をコールする
  • array_map() - 指定した配列の要素にコールバック関数を適用する
  • foreach

add a note

User Contributed Notes 35 notes

up
248
bisqwit at iki dot fi
20 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
10 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
47
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
13 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
10 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
15 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
8 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
3
alex_stanhope at hotmail dot com
13 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
19 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
10
taj at yahoo dot fr
5 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
4
EllisGL
6 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