【问题标题】:Fatal error: Nesting level too deep - recursive dependency?致命错误:嵌套级别太深 - 递归依赖?
【发布时间】:2010-09-30 20:58:02
【问题描述】:

我有一个复杂的嵌套对象层次结构,所有子对象(存储在父类中的对象数组)都包含一个链接回其父对象的属性:相当简单明了,没有真正的问题。如果我对层次结构中的任何对象执行 var_dump,我将在转储中获得递归引用,正如我所期望的那样。

FIRSTGEN 
   _children array of objects of type SECONDGEN
      SECONDGEN #1
         _parent object of type FIRSTGEN
         _children array of objects of type THIRDGEN
            THIRDGEN #1
               _parent object of type SECONDGEN
            THIRDGEN #2
               _parent object of type SECONDGEN
      SECONDGEN #2
         _parent object of type FIRSTGEN
         _children array of objects of type THIRDGEN
            THIRDGEN #3
               _parent object of type SECONDGEN

我最近在该层次结构中添加了一些新元素,但它们并不遵循完全相同的模式。它们存储在顶级父对象的一个​​对象数组中,但包含一个将它们链接回的属性,而不是它们的父对象,而是兄弟对象。当我现在执行 var_dump 时,我收到“致命错误:嵌套级别太深 - 递归依赖?”。

FIRSTGEN 
   _children_1 array of objects of type SECONDGEN_1
      SECONDGEN_1 #1
         _parent object of type FIRSTGEN
         _children array of objects of type THIRDGEN
            THIRDGEN #1
               _parent object of type SECONDGEN_1
            THIRDGEN #2
               _parent object of type SECONDGEN_1
      SECONDGEN_1 #2
         _parent object of type FIRSTGEN
         _children array of objects of type THIRDGEN
            THIRDGEN #3
               _parent object of type SECONDGEN_1
   _children_2 array of objects of type SECONDGEN_2
      SECONDGEN_2 #1
         _parent object of type SECONDGEN_1

除了 var_dump() 之外,代码中的其他所有内容都可以正常工作。我尝试创建一个更简单的示例来演示该问题,以便在提出这个问题时可以提供一个示例;但无法在简短的测试中复制它,只能在我更复杂的代码中。

我知道解决方案是重构关系,以便我的 SECONDGEN_2 对象的 _children_2 数组保存在适当的 SECONDGEN_1 父级中,使父级关系“正确”...我已经开始这样做了。 但是,我对这个错误很感兴趣,想知道是否有其他人遇到过它(以及你自己是如何处理的)。

【问题讨论】:

  • 一些代码会有所帮助,但如果我猜的话,我会说你有无限递归(因为我是我兄弟的兄弟)
  • 我希望我可以用一个简单的 sn-p 代码来复制可以在这里发布的代码,但到目前为止我还没有设法在 40 行或更少的时间内复制(我仍在尝试制作某物);甚至从我的主要项目中剥离所有无关的东西(当我可以重现它时)它仍然有太多的 100 行来发布。

标签: php oop recursion hierarchical-data


【解决方案1】:

如果您使用 == 而不是 === 比较递归对象,也会出现这种情况

如果您需要比较实际的对象实例,请始终使用严格比较运算符===,因为它仅比较对象是否引用同一类的同一实例。

简短说明:

如果您使用$object == $objectToCompareWith 比较对象,PHP 会将第一个对象的每个属性和值与第二个对象进行比较。这种比较是递归的,对象是被比较对象的属性。

这意味着如果两个对象共享一个属性作为其值的对象,PHP 在这些属性对象之间进行相同的== 比较。现在只要这些属性对象中的一个是递归的(例如自引用对象),比较也会向下递归,直到达到最大嵌套级别。

正如 Josh Stuart 和 mazatwork 在 cmets 中所述,在使用 in_array()array_search() 等数组函数时,可以通过将它们各自的 $strict 参数设置为 true 来强制进行严格比较。

Richard Lord: "Nesting level too deep – recursive dependency?"

PHP Manual: "Comparing Objects"

【讨论】:

  • 流感 - 你是我今天的英雄。一读就明白了……谢谢!
  • 不错的一个!这也适用于in_array 并比较对象数组。
  • array_search() 还有一个用于在对象数组中搜索的 $strict 参数。
  • 但在使用array_search(而不是in_array)时请注意不要使用松散比较,因为这也会丢弃integer索引数组中的第一个元素。使用if (array_search($needle, $haystack, true) !== false) {doSmth();}
【解决方案2】:

在自引用代码中似乎存在 PHP 限制,并尝试使用 print_rvar_dumpvar_export 显示它,或使用 in_array 搜索它。如果一个对象被循环引用,这些函数基本上没有办法知道在哪里停止递归。

根据this bug reportreproduce this最简单的方法是:

$outText = var_export( $GLOBALS, true );
print_r($outText) ;

其他错误报告mention 也有,还有一些测试用例。我想说的是,如果这仅在var_dump 中触发,您不必太担心。如果这是出于调试目的,我肯定会支持 Wrikken 关于 xdebug 的建议。

【讨论】:

  • 您提到的第二个错误报告是一个简单的 parent->child->parent 递归,它不会导致 var_dump() 任何问题......它只是报告递归。它甚至不会导致 parent->child->grandchild->parent 递归的问题。不过,我对兄弟情况感到惊讶......从成功处理更常见的递归类型来看,我预计兄弟关系会以同样的方式陷入困境。
  • 如果它发生在 var_export 那么在某些情况下,例如生成 CSV,无法调试现有变量,此处提供了解决方法php.net/manual/ru/function.var-export.php,搜索关键字 - 解决方法错误“嵌套级别太深 - 递归依赖”
【解决方案3】:

有时(但很少,因为此类 contrustcs 使用有限的有效值)会发生这种情况,只要您的代码正常工作,我不会过多考虑 var_dump(调试工具,而不是生产一)无法应对。但是,如果您仍然需要 var_dump 工作,我可以衷心推荐运行 xdebug,您可以在其中设置最大深度 var_dump 将显示,字符串转储的最大长度和孩子的最大数量。

【讨论】:

  • 我认为这是我的兄弟关系的原因之一(提供一种直接从 FIRSTGEN 父级列出所有 SECONDGEN_2 子级的方法)可以通过将 SECONDGEN_2 子级附加到它们对应的 SECONDGEN_1 对象,并且 FIRSTGEN 方法循环通过 SECONDGEN_1 对象调用 listSECONDGEN_2 方法并合并列表;所以不是真正的有效用途。所以无论如何我都在重新设计它。
  • 我很难可视化引用的实际结构,但在足够的样本中,PHP 总是有一条路径可以跳转节点,而不会在最大节点之前遇到相同的节点嵌套级别。你有多少个节点,有没有这样的路径?
【解决方案4】:

我遇到了与您相同的错误,但情况完全不同。我将发布答案以防其他人以与我相同的方式到达这里。

如果您尝试对对象数组进行自定义排序 (usort),我必须这样做:

function cmp($a, $b) {
    if($a->num_estimates == $b->num_estimates) return 0;

    return($a->num_estimates < $b->num_estimates) ? -1 : 1;
}
$c = usort(Company::$companies, "cmp");

原来$object-&gt;num_estimates 偶尔会返回一个对象而不是一个数字。一旦我确定它总是返回一个数字,错误就消失了。

【讨论】:

    【解决方案5】:

    您可以使用魔术方法__toString 定义自定义转换为字符串。在实现 __toString 时查看您的对象并避免通过递归太深,一切都应该没问题。只是永远不要忘记并意外调用 var_dump、var_export、print_r 等。

    一旦定义了 __toString 方法,下面的工作就很好了:

    回显 $yourObjectHere;

    这是我当前运行良好的解决方案,但我仍然想要一些东西来保护我不会忘记不调用 var_dump、var_export 和 print_r。

    【讨论】:

      【解决方案6】:

      也许这对某人有帮助。

      对我来说,一个解决方案是在 php.ini 中提出 pcre.recursion_limit。不过,当您阅读其他答案时,这更像是一种临时解决方法,因为问题很可能出在您自己的代码中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-10
        相关资源
        最近更新 更多