Arrays7 min readExamples run on PHP 8.5.10

array_walk()

Learn how PHP 8.4 array_walk() calls a callback for each array element, how to change values by reference, and which mistakes to avoid.

Signature

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

Purpose

array_walk() visits every element of an array (or the properties of an object) and calls a function of your choosing for each one. It exists for side effects: you either change the values in place or collect information from the elements as you go. It does not build a new array. If you want a transformed copy, array_map() is usually the better fit.

Signature

Parameters and defaults

  • $array is taken by reference, so changes made through the callback show up in your original variable. The walk covers the whole array and ignores the internal array pointer.
  • $callback is normally called with the element value first and the key second. If the first parameter is declared by reference (&$value), assigning to it changes the element in the array.
  • $arg is optional and defaults to null. When you supply it, it is passed to the callback as a third argument.

Return value and failure cases

The function always returns true. Since PHP 8.2 the declared return type is true; earlier it was bool. Because the result never varies, checking it tells you nothing about whether your callback did what you wanted.

The real failure case is a callback that needs too many arguments. If it requires more than two parameters, or more than three when $arg is passed, PHP throws an ArgumentCountError. Many built-in functions behave the same way when they receive more arguments than they accept, which makes them unsuitable as direct callbacks.

Example 1: changing values with a reference and an extra argument

The callback takes the value by reference and uses the third argument as a multiplier.

Example
<?php
$prices = ['apple' => 3, 'pear' => 5];

array_walk($prices, function (&$price, $name, $multiplier) {
    $price = $price * $multiplier;
}, 2);

echo json_encode($prices, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"apple":6,"pear":10}

Example 2: collecting data without modifying the array

When the callback only reads, take the value normally. To gather results, capture outside variables by reference with use (&$variable).

Example
<?php
$cart = ['a' => 2, 'b' => 3, 'c' => 4];
$total = 0;
$keys = [];

$result = array_walk($cart, function ($qty, $key) use (&$total, &$keys) {
    $total += $qty;
    $keys[] = strtoupper($key);
});

echo json_encode(['result' => $result, 'total' => $total, 'keys' => $keys], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"result":true,"total":9,"keys":["A","B","C"]}

Example 3: built-in functions as callbacks

Passing 'strtoupper' directly fails because array_walk() also supplies the key, and strtoupper() accepts only one argument. Even if it worked, the function returns a new string rather than altering the element. A small wrapper with a reference parameter solves both problems.

Example
<?php
$words = ['red', 'blue'];

try {
    array_walk($words, 'strtoupper');
} catch (ArgumentCountError $e) {
    echo get_class($e), PHP_EOL;
}

array_walk($words, function (&$word) {
    $word = strtoupper($word);
});

echo json_encode($words, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
ArgumentCountError
["RED","BLUE"]

Edge cases

  • An empty array is fine: the callback is never called and the function still returns true.
  • Only values may be changed. Adding, removing or reordering elements from inside the callback has undefined, unpredictable behavior. Keys cannot be renamed this way.
  • The walk is shallow. If an element is itself an array, your callback receives that array as the value. Use array_walk_recursive() for nested structures.
  • When you pass an object, the function works on its properties, so visibility rules apply to what the callback can see.
  • A callback that declares its second or third parameter by reference triggers an E_WARNING since PHP 8.0. Take the key and $arg by value.
  • The callback may accept fewer than two parameters. Extra values are simply ignored for user-defined functions.

Common mistakes

  1. Forgetting the &. Without a by-reference first parameter, the callback changes only a local copy and the array stays the same.
  2. Expecting a return value from the callback to matter. The callback's return value is discarded.
  3. Using the result of array_walk() as data. It always returns true; the array itself holds the outcome.
  4. Passing a built-in function directly. Functions with strict argument counts raise ArgumentCountError when handed the key as well.
  5. Using it when a new array is wanted. array_map() returns a new array and leaves the source untouched, which is often clearer. A foreach loop with &$value is another option, but remember to unset the reference afterward.
  6. Altering the array's structure mid-walk. Build a new array separately instead.
  • array_walk_recursive() applies a callback through nested arrays.
  • array_map() returns a transformed copy.
  • iterator_apply() calls a function for each element of an iterator.
  • call_user_func_array() calls a callback with an array of arguments.

Recap

Use array_walk() to run a callback over each element for its side effects. Declare the value parameter by reference to modify elements, pass extra context through $arg or use, keep the callback's parameter count within the limit, and never rely on the return value, which is always true.

Tested examples

Change values by reference using an extra argument
<?php
$prices = ['apple' => 3, 'pear' => 5];

array_walk($prices, function (&$price, $name, $multiplier) {
    $price = $price * $multiplier;
}, 2);

echo json_encode($prices, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"apple":6,"pear":10}
Collect data with captured variables
<?php
$cart = ['a' => 2, 'b' => 3, 'c' => 4];
$total = 0;
$keys = [];

$result = array_walk($cart, function ($qty, $key) use (&$total, &$keys) {
    $total += $qty;
    $keys[] = strtoupper($key);
});

echo json_encode(['result' => $result, 'total' => $total, 'keys' => $keys], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"result":true,"total":9,"keys":["A","B","C"]}
Built-in callback fails; wrapper succeeds
<?php
$words = ['red', 'blue'];

try {
    array_walk($words, 'strtoupper');
} catch (ArgumentCountError $e) {
    echo get_class($e), PHP_EOL;
}

array_walk($words, function (&$word) {
    $word = strtoupper($word);
});

echo json_encode($words, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
ArgumentCountError
["RED","BLUE"]