Как проверить циклические ссылки в PHP при рекурсивном разборе ассоциативного массива?
Я создал этот массив с круговой ссылкой:
$arr = array(1 => 'one', 2 => 'two');
$arr[3] = &$arr;
У меня есть функция, которая рекурсивно выводит значения в массиве, но я действительно не смог решить проблему создания циклической проверки ссылок. Как ты можешь это делать?
Текущая функция, которую я использую для печати массива, скопирована ниже. Я не включил различные попытки, которые я сделал при выполнении круговой проверки ссылок. Они в основном вращались вокруг стратегии поддержания массива $seen
элементов, которые имеют уже были напечатаны для каждой ветви рекурсии . Это связано с тем, что я все еще хочу разрешить печать повторяющихся значений, но не печатать значение, если оно является родительским для текущего массива, который анализируется.
Проблемы, которые у меня были, состояли в том, чтобы выяснить, как добавить ссылки, а не копии массива к этой переменной $seen
. Но я был бы счастлив использовать другую стратегию все вместе, если бы это сработало.
function HTMLStringify($arr)
{
if(is_array($arr)){
$html = '<ul>';
foreach ($arr as $key => $value) {
$html .= '<li>' . $key;
if(is_array($value)){
//Conspicuously missing is a circular reference check,
//causing infinite recursion. After a few failed attempts
//at checking for this (e.g. discovering that array_push doesn't take references)
//I have left it for further study.
//(After all, Javascript's JSON.stringify() doesn't check for circular references)
//TODO: Check for circular references
$html .= HTMLStringify($value, $seen);
}
elseif(is_numeric($value) || is_string($value) || is_null($value))
{
$html .= ' = ' . $value;
}
else
{
$html .= ' [couldn't parse ' . gettype($value) . ']';
}
$html .= '</li>';
}
$html .= '</ul>';
return $html;
}
else
{
return null;
}
}
2 ответа:
Адаптированная версия вашего кода, использующая строгую проверку
in_array
из ответа, связанного Райаном Винсентом, показана ниже:function HTMLStringify($arr, array $seen = array()) { if (is_array($arr)) { $seen[] = $arr; $html = '<ul>'; foreach ($arr as $key => $value) { $html .= '<li>' . $key; if (is_array($value)) { if (in_array($value, $seen, true)) { // Deal with recursion in your own way here $html .= ' [RECURSION]'; } else { $html .= HTMLStringify($value, $seen); } } elseif (is_numeric($value) || is_string($value) || is_null($value)) { $html .= ' = ' . $value; } else { $html .= ' [couldn\'t parse ' . gettype($value) . ']'; } $html .= '</li>'; } return $html . '</ul>'; } else { return null; } } $arr = array(1 => 'one', 2 => 'two'); $arr[3] = &$arr; echo HTMLStringify($arr);
Сравнивая несколько версий PHP , похоже, что это будет работать для PHP 5.3.15+ и PHP 5.4.5+.
Я использую эту функцию для отладки. Также обновлено для обнаружения рекурсивной связи.
Как вы видите, я собираю сериализованный объект, а затем сравниваю его. Сериализация требуется, потому что просто сравнение рекурсивного объекта выбрасывает фатальную ошибку:function print_table($mixed, $level=9, $_callstack=array()){ if($level<=0){ echo '**LIMIT**'; return; } if( array_search(serialize($mixed), $_callstack)!==false){ echo '***recursive detected***'; return ; } $_callstack[] = serialize($mixed); if(is_array($mixed)){ echo '<table cellspacing="0" width="100%" border="1">'; foreach($mixed as $key=>$val){ echo '<tr><td width="20%">'.$key.'</td><td>'; if(is_array($val)){ print_table($val,$level-1, $_callstack); }elseif(is_null($val)){ echo '<span style="color:blue">null</span>'; }elseif($val===false){ echo '<span style="color:red">false</span>'; }elseif($val===true){ echo '<span style="color:green">true</span>'; }elseif(is_numeric($val) && $val>1000000000){ echo $val,' <span style="color:gray">[',date('d-m-Y H:i:s',$val),']</span>'; }elseif($val===''){ echo '<span style="color:blue">empty string</span>'; }else{ echo $val; } echo '</td></tr>'; } echo '</table>'; }else{ var_dump($mixed); } }
$arr=array(&$arr); $arr==$arr; // Fatal error: Nesting level too deep - recursive dependency? // php 5.2.9
Но сериализация поддерживает рекурсивные объекты! Итак, мы должны сравнить сериализованные строки,но сериализация может занять много времени и памяти. Если вы найдете другой способ-дайте мне знать:)