检测PHP中的无限数组递归?

时间:2022-12-15 05:33:27

i've just reworked my recursion detection algorithm in my pet project dump_r()

我刚刚在我的宠物项目dump_r()中重写了我的递归检测算法

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

detecting object recursion is not too difficult - you use spl_object_hash() to get the unique internal id of the object instance, store it in a dict and compare against it while dumping other nodes.

检测对象递归并不太难 - 您使用spl_object_hash()来获取对象实例的唯一内部id,将其存储在dict中并在转储其他节点时与其进行比较。

for array recursion detection, i'm a bit puzzled, i have not found anything helpful. php itself is able to identify recursion, though it seems to do it one cycle too late. EDIT: nvm, it occurs where it needs to :)

对于数组递归检测,我有点困惑,我没有发现任何有用的东西。 php本身能够识别递归,虽然它似乎太晚了一个周期。编辑:nvm,它发生在它需要的地方:)

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

does it have to resort to keeping track of everything in the recursion stack and do shallow comparisons against every other array element?

它是否必须求助于跟踪递归堆栈中的所有内容并对每个其他数组元素进行浅层比较?

any help would be appreciated,
thanks!

任何帮助将不胜感激,谢谢!

2 个解决方案

#1


9  

Because of PHP's call-by-value mechanism, the only solution I see here is to iterate the array by reference, and set an arbitrary value in it, which you later check if it exists to find out if you were there before:

由于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']);

}

You could wrap this function into another function that accepts values instead of references, but then you would get the RECURSION notice from the 2nd level on. I think print_r does the same too.

您可以将此函数包装到另一个接受值而不是引用的函数中,但是您将从第二级获得RECURSION通知。我认为print_r也是如此。

#2


3  

Someone will correct me if I am wrong, but PHP is actually detecting recursion at the right moment. Your assignation simply creates the additional cycle. The example should be:

如果我错了,有人会纠正我,但PHP实际上是在适当的时候检测递归。您的分配只会创建附加周期。示例应该是:

$arr    = array();
$arr    = array(&$arr);

Which will result in

这将导致

array(1) { [0]=> &array(1) { [0]=> *RECURSION* } } 

As expected.


Well, I got a bit curious myself how to detect recursion and I started to Google. I found this article http://noteslog.com/post/detecting-recursive-dependencies-in-php-composite-values/ and this solution:

嗯,我有点好奇自己如何检测递归,我开始使用谷歌。我发现这篇文章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;
}

#1


9  

Because of PHP's call-by-value mechanism, the only solution I see here is to iterate the array by reference, and set an arbitrary value in it, which you later check if it exists to find out if you were there before:

由于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']);

}

You could wrap this function into another function that accepts values instead of references, but then you would get the RECURSION notice from the 2nd level on. I think print_r does the same too.

您可以将此函数包装到另一个接受值而不是引用的函数中,但是您将从第二级获得RECURSION通知。我认为print_r也是如此。

#2


3  

Someone will correct me if I am wrong, but PHP is actually detecting recursion at the right moment. Your assignation simply creates the additional cycle. The example should be:

如果我错了,有人会纠正我,但PHP实际上是在适当的时候检测递归。您的分配只会创建附加周期。示例应该是:

$arr    = array();
$arr    = array(&$arr);

Which will result in

这将导致

array(1) { [0]=> &array(1) { [0]=> *RECURSION* } } 

As expected.


Well, I got a bit curious myself how to detect recursion and I started to Google. I found this article http://noteslog.com/post/detecting-recursive-dependencies-in-php-composite-values/ and this solution:

嗯,我有点好奇自己如何检测递归,我开始使用谷歌。我发现这篇文章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;
}