обнаружение бесконечной рекурсии массива в PHP? - PullRequest
14 голосов
/ 28 января 2012

Я только что переработал свой алгоритм обнаружения рекурсии в своем любимом проекте dump_r ()

https://github.com/leeoniya/dump_r.php

обнаружение рекурсии объекта не так уж сложно - вы используете spl_object_hash (), чтобы получить уникальный внутренний идентификатор экземпляра объекта, сохранить его в dict и сравнить с ним во время сброса других узлов.

для обнаружения рекурсии массива, я немного озадачен, я не нашел ничего полезного. Сам php способен идентифицировать рекурсию, хотя, кажется, делает это за один цикл слишком поздно. РЕДАКТИРОВАТЬ: Н.В.М., это происходит, где это необходимо:)

$arr = array();
$arr[] = array(&$arr);
print_r($arr);

Должно ли оно прибегать к отслеживанию всего в стеке рекурсии и делать поверхностные сравнения с любым другим элементом массива?

любая помощь будет оценена,
спасибо!

Ответы [ 2 ]

9 голосов
/ 15 февраля 2012

Из-за механизма вызова по значению в 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']);

}

Вы можете заключить эту функцию в другую функцию, которая принимает значения вместо ссылок, но тогда вы получите уведомление о РЕКУРСИИ со 2-го уровня. Я думаю, что print_r делает то же самое.

3 голосов
/ 28 января 2012

Кто-то исправит меня, если я ошибаюсь, но 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;
}
...