问题 检查数组是否递归


在PHP中检查数组是否递归的最佳方法是什么?

给出以下代码:

<?php 
$myarray = array('test',123); 
$myarray[] = &$myarray; 
print_r($myarray); 
?> 

来自 PHP手册:

print_r()将显示 递推 当它到达第三个   数组的元素。

似乎没有任何其他方法可以扫描数组   递归引用,所以如果你需要检查它们,你必须这样做   使用print_r()及其第二个参数来捕获输出并查看   为了这个词 递推

有更优雅的检查方式吗?

PS。这是我使用regex和print_r()检查并获取递归数组键的方法

$pattern = '/\n            \[(\w+)\] => Array\s+\*RECURSION\*/';
preg_match_all($pattern, print_r($value, TRUE), $matches);
$recursiveKeys =  array_unique($matches[1]);

谢谢


9920
2018-02-07 12:59


起源

不是 递推 这个术语只有在深入了解3个元素时才会显示,而不是在它引用自身时?另外,我很确定这是php.ini中的可配置深度 - JamesHalsall
我使用spl_object_hash()对象做了这个,但是对于数组我没有任何线索。 - hugo_leonardo


答案:


尝试解决“不可能”的问题总是很有趣!

这是一个函数,如果递归发生在顶层,它将检测递归数组:

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;
}

看到它在行动

当然,也可以通过手动保持堆栈来编写迭代而不是递归,这对于非常大的递归级别是个问题的情况会有所帮助。


7
2018-02-07 13:25





我前段时间深入研究过,我无法找到任何有用的机制来检测PHP数组中的递归。

问题归结为是否可以判断两个PHP变量是否是对同一事物的引用。

如果您正在使用对象而不是数组(甚至是数组中的对象),那么就有可能,因为可以找出两个对象是否使用相同的引用 spl_object_hash()。因此,如果结构中有对象,则可以通过遍历树并比较对象来检测递归。

但是对于常规变量 - 即非对象 - 使用标准PHP无法轻松检测到这一点。

工作是要使用 print_r() (如你所知)或 var_dump(),但这些都不是特别优雅的解决方案。

xDebug还提供了一个可以提供帮助的功能, xdebug_debug_zval(),但这显然只有在你安装了xDebug时才可用,这在生产系统上是不推荐的。

进一步的意见和建议 在这里


2
2018-02-07 13:14





以下函数比接受答案中的代码更简单[看法],并且似乎适用于我能够设想的任何用例。它似乎也出乎意料地快,通常需要几微秒,尽管我没有做过广泛的基准测试。如果有问题,如果有人能说出来,我将不胜感激?

// 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;
  }

2
2017-11-02 17:27



不错,简单快捷。 - Bas


我相信你无法检查。阅读 参考文件 有关参考的更多信息。

这是检查RECURSION的函数(来自PHP文档注释),尽管看起来很慢(我不建议):

  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;
    }

1
2018-02-07 13:06