致命错误:嵌套级别太深 - 递归依赖?

时间:2010-09-30 20:58:03

标签: php oop recursion hierarchical-data

我有一个复杂的嵌套对象层次结构,所有子对象(存储在父类中的对象数组)包含一个链接回其父级的属性:相当简单和直接,没有实际问题。如果我对层次结构中的任何对象执行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()除外。我已经尝试创建一个更简单的例子来演示这个问题,这样我就可以在提出这个问题时提供一个例子;但是在短期测试中无法复制它,只能在我更复杂的代码中复制它。

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

6 个答案:

答案 0 :(得分:71)

如果使用==而不是===

比较递归对象,也会出现这种情况

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

简短说明:

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

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

正如Josh Stuart和mazatwork的评论中所述,通过将in_array()参数设置为array_search(),使用$stricttrue等数组函数时可以强制进行严格比较}。

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

PHP Manual: "Comparing Objects"

答案 1 :(得分:10)

在自引用代码中看起来像PHP限制,并尝试使用print_rvar_dumpvar_export显示它,或使用in_array搜索它。基本上,如果对象被引用,那么这些函数无法知道在何处停止递归。

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

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

其他错误报告mention也是如此,还有一些测试用例。我会说,如果这只是在var_dump中触发,你不应该太担心它。如果这是出于调试目的,我肯定是第二个Wrikken关于xdebug的建议。

答案 2 :(得分:3)

有时(但很少,因为这种争用的有效性有限)会发生这种情况,并且只要你的代码正常工作,我就不会过多考虑var_dump(调试工具,不是生产一个)无法应付它。但是,如果您仍然需要 var_dump才能工作,我可以衷心地推荐运行xdebug,您可以在其中设置var_dump将显示的最大深度,最大长度为字符串转储和最大数量的子项。

答案 3 :(得分:1)

我遇到了与你相同的错误,但情况完全不同。我发布了答案,以防其他人像我一样来到这里。

如果你正在尝试使用一系列对象进行自定义排序(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->num_estimates偶尔会返回一个对象而不是一个数字。一旦我确定它总是返回一个数字,那么错误消失了。

答案 4 :(得分:0)

您可以使用魔术方法__toString来定义字符串的自定义转换。在实现__toString时,查看您的对象并避免过于递归,并且一切都应该没问题。永远不要忘记并且不小心调用var_dump,var_export,print_r等。

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

echo $ yourObjectHere;

这是我目前的解决方案,效果很好,但我仍然希望保护我不要忘记不调用var_dump,var_export和print_r。

答案 5 :(得分:0)

也许这有助于某人。

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