Каков наилучший способ проверить, является ли массив рекурсивным в PHP?
Учитывая следующий код:
<?php $myarray = array('test',123); $myarray[] = &$myarray; print_r($myarray); ?>
Из руководства PHP:
Print_r () будет отображать RECURSION, когда он попадает в третий элемент массива.
Кажется, нет никакого другого способа сканирования массива для рекурсивных ссылок, поэтому, если вам нужно проверить их, вам придется использовать print_r () со своим вторым параметром для захвата вывода и поиска слова RECURSION ,
Есть ли более элегантный способ проверки?
PS. Вот как я проверяю и получаю рекурсивные ключи массива с помощью regex и print_r ()
$pattern = '/\n \[(\w+)\] => Array\s+\*RECURSION\*/'; preg_match_all($pattern, print_r($value, TRUE), $matches); $recursiveKeys = array_unique($matches[1]);
благодаря
Всегда интересно попробовать «невозможные» проблемы!
Вот функция, которая будет обнаруживать рекурсивные массивы, если рекурсия происходит на верхнем уровне:
function is_recursive(array &$array) { static $uniqueObject; if (!$uniqueObject) { $uniqueObject = new stdClass; } foreach ($array as &$item) { if (!is_array($item)) { continue; } $item[] = $uniqueObject; $isRecursive = end($array) === $uniqueObject; array_pop($item); if ($isRecursive) { return true; } } return false; }
Смотрите в действии .
Обнаружение рекурсии на любом уровне, очевидно, было бы более сложным, но я думаю, мы можем согласиться с тем, что это кажется выполнимым.
И вот рекурсивное (каламбур не предназначенное, но приятное, тем не менее) решение, которое обнаруживает рекурсию на любом уровне:
function is_recursive(array &$array, array &$alreadySeen = array()) { static $uniqueObject; if (!$uniqueObject) { $uniqueObject = new stdClass; } $alreadySeen[] = &$array; foreach ($array as &$item) { if (!is_array($item)) { continue; } $item[] = $uniqueObject; $recursionDetected = false; foreach ($alreadySeen as $candidate) { if (end($candidate) === $uniqueObject) { $recursionDetected = true; break; } } array_pop($item); if ($recursionDetected || is_recursive($item, $alreadySeen)) { return true; } } return false; }
Смотрите в действии .
Конечно, это также можно записать для работы с итерацией вместо рекурсии, удерживая стек вручную, что поможет в случаях, когда очень большой уровень рекурсии является проблемой.
Я глубоко вникал в это подробно, и мне не удалось найти какой-либо полезный механизм для обнаружения рекурсии в массивах PHP.
Вопрос сводится к тому, можно ли сказать, являются ли две переменные PHP ссылками на одно и то же.
Если вы работаете с объектами, а не с массивами (или даже с объектами внутри ваших массивов), то это возможно, так как можно узнать, являются ли два объекта одной и той же ссылкой с помощью spl_object_hash()
. Поэтому, если у вас есть объекты в вашей структуре, вы можете обнаружить рекурсию, пройдя по дереву и сравнивая объекты.
Однако для обычных переменных – то есть не-объектов – это невозможно легко обнаружить с помощью стандартного PHP.
Работа вокруг заключается в использовании print_r()
(как вы уже знаете) или var_dump()
, но ни один из них не является особенно элегантным решением.
Существует также функция, предоставляемая xDebug, которая может помочь, xdebug_debug_zval()
, но это, очевидно, доступно только в том случае, если вы установили xDebug, что не рекомендуется в производственной системе.
Дополнительные советы и предложения доступны здесь .
Следующая функция проще [мнение], чем код в принятом ответе, и, похоже, работает на любой случай использования, который я смог усовершенствовать. Это также кажется удивительно быстрым, как правило, с микросекундами, хотя я не провел обширного бенчмаркинга. Если есть проблема с этим, я был бы признателен, если бы кто-нибудь мог указать на это?
// returns TRUE iff the passed object or array contains // a self-referencing object or array function is_r($obj, &$visited=array()) { $visited[] = $obj; foreach ($obj as $el) { if (is_object($el) || is_array($el)) { if (in_array($el, $visited, TRUE)) return TRUE; if (is_r($el, $visited)) return TRUE; } } return FALSE; }
Я полагаю, вы не можете это проверить. Прочтите Справочный документ для получения дополнительной информации по ссылке.
Вот функция для проверки RECURSION (из комментариев PHP doc), хотя это кажется очень медленным (я бы не предложил):
function is_array_reference ($arr, $key) { $isRef = false; ob_start(); var_dump($arr); if (strpos(preg_replace("/[ \n\r]*/i", "", preg_replace("/( ){4,}.*(\n\r)*/i", "", ob_get_contents())), "[" . $key . "]=>&") !== false) $isRef = true; ob_end_clean(); return $isRef; }