Обнаружение бесконечной рекурсии массива в PHP?
Я только что переработал свой алгоритм обнаружения рекурсии в моем проекте-проекте dump_r()
https://github.com/leeoniya/dump_r.php
обнаружение рекурсии объекта не слишком сложно - вы используете spl_object_hash(), чтобы получить уникальный внутренний идентификатор экземпляра объекта, сохранить его в dict и сравнить с ним при сбрасывании других узлов.
для обнаружения рекурсии массива, я немного озадачен, я не нашел ничего полезного. Сам php способен идентифицировать рекурсию, хотя кажется, что это слишком поздно. EDIT: nvm, это происходит там, где ему нужно:)
$arr = array();
$arr[] = array(&$arr);
print_r($arr);
нужно ли прибегать к отслеживанию всего в стеке рекурсии и делать мелкие сравнения с каждым другим элементом массива?
любая помощь будет оценена,
спасибо!
Ответы
Ответ 1
Из-за механизма вызова по принципу PHP единственное решение, которое я вижу здесь, это перебрать массив по ссылке и установить в нем произвольное значение, которое вы позже проверите, существует ли оно, чтобы узнать, были ли вы там раньше
function iterate_array(&$arr){
if(!is_array($arr)){
print $arr;
return;
}
// if this key is present, it means you already walked this array
if(isset($arr['__been_here'])){
print 'RECURSION';
return;
}
$arr['__been_here'] = true;
foreach($arr as $key => &$value){
// print your values here, or do your stuff
if($key !== '__been_here'){
if(is_array($value)){
iterate_array($value);
}
print $value;
}
}
// you need to unset it when done because you're working with a reference...
unset($arr['__been_here']);
}
Вы можете перенести эту функцию в другую функцию, которая принимает значения вместо ссылок, но затем вы получите уведомление RECURSION со второго уровня. Я думаю, print_r тоже делает то же самое.
Ответ 2
Кто-то исправит меня, если я ошибаюсь, но PHP на самом деле обнаруживает рекурсию в нужный момент. Ваше назначение просто создает дополнительный цикл. Пример:
$arr = array();
$arr = array(&$arr);
Что приведет к
array(1) { [0]=> &array(1) { [0]=> *RECURSION* } }
Как и ожидалось.
Хорошо, мне стало немного любопытно, как обнаружить рекурсию, и я начал работать с Google. Я нашел эту статью http://noteslog.com/post/detecting-recursive-dependencies-in-php-composite-values/ и это решение:
function hasRecursiveDependency($value)
{
//if PHP detects recursion in a $value, then a printed $value
//will contain at least one match for the pattern /\*RECURSION\*/
$printed = print_r($value, true);
$recursionMetaUser = preg_match_all('@\*RECURSION\*@', $printed, $matches);
if ($recursionMetaUser == 0)
{
return false;
}
//if PHP detects recursion in a $value, then a serialized $value
//will contain matches for the pattern /\*RECURSION\*/ never because
//of metadata of the serialized $value, but only because of user data
$serialized = serialize($value);
$recursionUser = preg_match_all('@\*RECURSION\*@', $serialized, $matches);
//all the matches that are user data instead of metadata of the
//printed $value must be ignored
$result = $recursionMetaUser > $recursionUser;
return $result;
}