Передача по ссылке не работает с дополнительными параметрами для array_walk_recursive, если только это не устарелая передача по ссылке во время вызова - PullRequest
6 голосов
/ 21 декабря 2011

Некоторое время назад я использовал "традиционную" рекурсивную функцию для сглаживания многомерных массивов, таких как

$baseArray = array(array('alpha'),
                   array('beta','gamma'),
                   array(),
                   array(array('delta','epsilon'),
                         array('zeta',array('eta',
                                            'theta'
                                           ),
                              ),
                        ),
                   array('iota'),
                  );

, в простой 1-й массив.

Прошлой ночью я подумал, что мне стоит взглянуть на использование array_walk_recursive () , чтобы посмотреть, смогу ли я сделать его более эффективным и чистым.

Моя первая попытка оказалась не очень успешной:

function flattenArray($arrayValue, $arrayKey, &$flatArray) {
    $flatArray[] = $arrayValue;
}


$flattenedArray = array();
array_walk_recursive($baseArray,'flattenArray',$flattenedArray);

Я думал, что это должно работать, но все, что я получил, это ряд ошибок:

Warning: Cannot use a scalar value as an array in C:\xampp\htdocs\arrayTest.php on line 16

и результат:

array(0) { }

Тип подсказки вмоя функция flattenArray () дала мне

Catchable fatal error: Argument 3 passed to flattenArray() must be an array, integer given in C:\xampp\htdocs\arrayTest.php on line 16

Использование замыкания дало идентичные результаты

Единственный способ заставить его работать (без обращения к использованию global или static для моего flattenArray).) использовал обход по ссылке:

function flattenArray($arrayValue, $arrayKey, $flatArray) {
    $flatArray[] = $arrayValue;
}


$flattenedArray = array();
array_walk_recursive($baseArray,'flattenArray',&$flattenedArray);

, который дает правильный результат

array(9) { [0]=> string(5) "alpha" [1]=> string(4) "beta" [2]=> string(5) "gamma" [3]=> string(5) "delta" [4]=> string(7) "epsilon" [5]=> string(4) "zeta" [6]=> string(3) "eta" [7]=> string(5) "theta" [8]=> string(4) "iota" }

, но дает мне неожиданное предупреждение

Deprecated: Call-time pass-by-reference has been deprecated in C:\xampp\htdocs\arrayTest.php on line 22

Я знаю, что PHP - причудливый язык, но это кажется немного экстремальным.Документация ясно показывает, что первым параметром для array_walk_recursive является передача по ссылке, но кажется, что дополнительные аргументы могут передаваться только по ссылке во время вызова.Странно!

Версия PHP - 5.3.8

Любые предложения о том, как я могу использовать array_walk_recursive для корректного выравнивания массива, без получения устаревших ошибок (кроме подачи отчета об ошибке).

РЕДАКТИРОВАТЬ

PS

Я знаю, что могу обойти эту проблему, используя замыкание:

$flattenedArray = array();
array_walk_recursive($baseArray, function($arrayValue, $arrayKey) use(&$flattenedArray){ $flattenedArray[] = $arrayValue; } );
var_dump($flattenedArray);

, но так как это требуетсядля библиотеки, которая в настоящее время позволяет использовать с PHP 5.2.0, нецелесообразно использовать функцию, которая требует значительно более поздней версии PHP

Ответы [ 4 ]

6 голосов
/ 21 декабря 2011

Думайте об этом так: вы передаете $flatArray в array_walk_recursive по значению .array_walk_recursive затем передаст аргумент по ссылке в вашу функцию.Но так как он был передан array_walk_recursive по значению, ссылка уже будет указывать на другое значение.

Я знаю, это может показаться странным ограничением, но когда вы думаете об этом, это тоже вполне логично.

Кстати, я думаю, что вы случайно обнаружили еще одну проблему с этим, на самом деле это выглядит как серьезное повреждение памяти (посмотрите на третьи элементы напечатанного массива @ http://codepad.viper -7.com/ ZYNrNd ).Я рассмотрю это.

На заметке, простой способ сгладить это использование RecursiveArrayIterator:

$flattenedArray = array();
foreach (new RecursiveIteratorIterator(
             new RecursiveArrayIterator($baseArray),
             RecursiveIteratorIterator::LEAVES_ONLY
         ) as $value) {
    $flattenedArray[] = $value;
}
1 голос
/ 21 декабря 2011

Поскольку ваши решения зависят от версии (согласно документации, PHP 5.2 не должен жаловаться на время вызова передача по ссылке ), одним из вариантов является создание различных сценариев, связанных с каждой реализацией, затем попросите другой скрипт условно включить скрипт, который определяет функцию выравнивания (и любой другой код, зависящий от версии) в зависимости от версии PHP.

if (! defined('PHP_VERSION_ID')) {
    $version = explode('.', PHP_VERSION);
    define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
    if (PHP_VERSION_ID < 50207) {
        define('PHP_MAJOR_VERSION',   $version[0]);
        define('PHP_MINOR_VERSION',   $version[1]);
        define('PHP_RELEASE_VERSION', $version[2]);
    }
}
if (PHP_VERSION_ID < 50300) {
    include_once('php-5.2-.php');
} else {
    include_once('php-5.3+.php');
}
1 голос
/ 21 декабря 2011

Не особенно полезно на этом этапе.

Читая документы PHP, я обнаружил, что call_user_func () имеет примечание против описания аргументов параметров:

Обратите внимание, что параметры для call_user_func () не передаются ссылка.

, но array_walk_recursive () не имеет такого уведомления.

Это может быть чисто проблема с документацией ... если бы я видел подобное уведомление в документации к array_walk_recursive (), я бы, наверное, не пробовал его (хотя любопытный мог бы попытаться независимо, просто чтобы посмотреть, что произошло ).

Однако я не понимаю, почему ни один из случаев не должен принимать аргументы передачи по ссылке в определении функции обратного вызова. Это похоже на шаг назад ... языковая функция, которая работала (хотя и использовала передачу по времени вызова по ссылке), больше не делает этого, не полагаясь на эту устаревшую функцию.

0 голосов
/ 21 декабря 2011

Передача по ссылке во время разговора устарела:

http://uk3.php.net/manual/en/ini.core.php#ini.allow-call-time-pass-reference

Это не отмечено в документации array_walk_recursive, потому что это не относится к этой функции.

Одна вещь, которую вы могли бы сделать, это передать имя объекта и метода, а не имя функции, в качестве обратного вызова и поддерживать состояние уплощенного массива как члена этого объекта.

1010 *, например *

class ArrayFlattener
{
    //implementation and array state
}

$flattener = new ArrayFlattener();
array_walk_recursive($baseArray, array($flattener, 'flatten'));

print_r($flattener->getFlattenedArray());
...