Полное руководство по функции array_walk_recursive() в PHP
Изучите array_walk_recursive() в PHP: применение callback-функции к каждому листу вложенных массивов, изменение по ссылке, примеры и подводные камни.
array_walk_recursive() применяет callback-функцию к каждому не-массивному значению (листу) в массиве, автоматически спускаясь во вложенные массивы. В отличие от ручного foreach, вам не нужно самостоятельно писать рекурсию, а в отличие от array_map(), функция может изменять исходный массив на месте, принимая каждое значение по ссылке.
В этой главе рассматриваются сигнатура функции, способ обхода вложенных массивов, изменение значений по ссылке, роль необязательного третьего аргумента, а также подводные камни (объекты, ключи и что считается «листом»).
Синтаксис
array_walk_recursive(array|object &$array, callable $callback, mixed $arg = null): true$array— массив для обхода. Передаётся по ссылке, поэтому callback-функция может изменять его содержимое.$callback— callable, вызываемый для каждого листа и получающий($value, $key). Объявите$valueкак&$value, чтобы редактировать массив на месте.$arg— необязательный дополнительный аргумент, передаваемый (по значению) в callback-функцию в качестве третьего параметра.
Функция возвращает true. Только листовые значения передаются в callback — ключи, содержащие подмассивы, обходятся рекурсивно, но сами в callback не передаются.
Как функция обходит вложенные массивы
Callback вызывается для каждого скалярного элемента, а если элемент сам является массивом, array_walk_recursive() спускается в него. В следующем примере печатаются все пары key: value, включая находящиеся во вложенном массиве:
Вывод:
0: a
1: b
0: c
1: d
2: e
3: fОбратите внимание, что сам вложенный массив (содержащий c, d, e) не передаётся в callback — только его листья. Получаемые ключи являются ключами внутри каждого уровня, поэтому c, d, e снова выводятся с ключами 0, 1, 2.
Изменение значений по ссылке
Наиболее распространённый вариант использования этой функции — преобразование всей вложенной структуры на месте. Принимайте значение &$value по ссылке и присваивайте ему новое значение. В примере к каждой цене добавляется налог 10%, независимо от глубины вложенности:
<?php
$prices = [
'fruit' => ['apple' => 1.00, 'pear' => 2.00],
'drinks' => ['water' => 0.50],
];
array_walk_recursive($prices, function (&$value, $key) {
$value = round($value * 1.10, 2); // add 10% tax
});
print_r($prices);
?>Вывод:
Array
(
[fruit] => Array
(
[apple] => 1.1
[pear] => 2.2
)
[drinks] => Array
(
[water] => 0.55
)
)Передача дополнительных данных через третий аргумент
Необязательный $arg передаётся в callback (по значению) третьим параметром — удобно для передачи конфигурации без использования замыкания. В примере строка-префикс задаётся один раз и используется для каждого листа:
<?php
$data = ['name' => 'ada', 'team' => ['bob', 'cara']];
array_walk_recursive($data, function ($value, $key, $prefix) {
echo $prefix . ucfirst($value) . "\n";
}, ">> ");
?>Вывод:
>> Ada
>> Bob
>> CaraПоскольку $arg передаётся по значению, накапливать результаты между вызовами с его помощью невозможно. Чтобы накапливать значение, используйте захват переменной по ссылке через use (&$total):
<?php
$data = [1, [2, 3], 4];
$sum = 0;
array_walk_recursive($data, function ($value, $key) use (&$sum) {
$sum += $value;
});
echo "Sum: $sum\n"; // Sum: 10
?>Подводные камни
- Рекурсия выполняется только для массивов. Лист — это всё, что не является массивом, включая объекты. Объект
stdClassвнутри данных передаётся в callback как единое значение, а не обходится рекурсивно. - Узлы-контейнеры (сами вложенные массивы) в callback не попадают. Если нужно работать с контейнерными массивами (а не только с листьями), используйте
array_walk()или ручной рекурсивныйforeach. - Для изменений необходим
&$value. Без ссылки присвоения внутри callback отбрасываются, и исходный массив остаётся неизменным. - Ключи могут повторяться на разных уровнях. Получаемый
$keyявляется ключом на своём уровне, поэтому одно и то же значение ключа может встречаться в разных ветках.
Связанные функции
array_walk()— аналогичная идея, но только на один уровень вглубь.array_map()— возвращает новый массив вместо изменения исходного.array_filter()— оставляет элементы, прошедшие проверку.- PHP Arrays — основы работы с массивами и другие вспомогательные функции.