xdebug 的奇怪行为和删除 SimpleXMLElement 中的子节点

Strange behaviour with xdebug and removing child node in SimpleXMLElement

我有一个 xml 具有多维结构的文档:

<?xml version="1.0" encoding="UTF-8"?>
<main>
  <products>
    <product id="87" state="active">
      <options>
        <option id="99" state="active">
          <item id="33" value="somevalue" />
          <item id="35" value="somevalue2" />
        </option>
        <option id="12" state="deleted">
          <item id="56" value="somevalue" />
          <item id="34" value="somevalue2" />
        </option>
      </options>
      <reports>
        <report type="json">
          <field id="123" state="active" />
          <field id="234" state="deleted" />
          <field id="238" state="active" />
          <field id="568" state="deleted" />
        </report>
      </reports>
    </product>
  </products>
</main>

在 PHP 后端,我编写了方法来检测具有 "deleted" 状态的项目并将其删除。 这是 PHP 部分:

public function loadAndModify() {
    $xml = simplexml_load_file($this->request->file('import_xml'));

    $this->processXml($xml);
}

/**
 * @param $element
 *
 * @return bool
 */
private function shouldRemove($element): bool
{
    return ($element['state'] == SomeClass::STATE_DELETED);
}

/**
 * @param $xml
 *
 * @return void
 */
private function processXml(&$xml): void
{
    if ($xml->children()->count() > 0) {
        foreach ($xml->children() as $child) {
            if ($this->shouldRemove($child)) {
                // this code works as expected with or without xdebug
                //$node = dom_import_simplexml($child);
                //$node->parentNode->removeChild($node);

                // this code will work only with xdebug when breakpoint is set
                unset($child[0]);
                continue;
                // end
            } else {
                $this->processXml($child);
            }
        }
    }
}

我通过将 simpleXMLElement 转换为 DOMElement 解决了我的问题。

然而,当我将 unset 与 xdebug 一起使用时,PHP 似乎有一些错误。当我将断点添加到 unset 并在调试器中转到 下一步 然后 恢复应用程序 - 没有问题。但是当断点处于活动状态并且我只是单击 恢复应用程序 时它会导致错误:

Uncaught ErrorException: Trying to get property of non-object in \project\vendor\symfony\var-dumper\Cloner\AbstractCloner.php

如果其他人有此错误,请解释为什么在这种情况下会发生这种情况。 谢谢。

正如 the comments under this previous answer 中所讨论的,您遇到的问题是您正在操作一个您正在迭代的对象(在本例中,$xml->children() 的 return 值) (带有 foreach 循环)。

在内部,SimpleXMLElement 对象有一个子项列表,它将依次呈现给 foreach 中的迭代器代码。当您删除当前子项时,您必然会更改该内部列表的形状,因此 "next item" 没有明确定义。删除列表中的其他项目也可能有奇怪的行为 - 例如,在检查项目 2 时删除项目 1 可能会导致迭代器 "skip ahead" 因为项目 4 现在已经移动到项目 3 所在的位置。

正如 hakre 在上面链接的评论中所建议的,最可靠的解决方案是将原始项目列表复制到一个数组中,这可以使用 iterator_to_array 来实现。将 false 作为第二个参数传递会丢弃键,这对于 SimpleXML 很重要,因为它使用标记名称作为键,并且数组中的每个键只能有一个值。

foreach ( iterator_to_array($xml->children(), false) as $child) {
    // Carry on as you were
}

唯一需要注意的是 iterator_to_array 会在 returning 之前遍历整个列表,所以如果你有一个大列表并且想跳出循环early, or stream output, 这个可能有问题