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, 这个可能有问题
我有一个 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, 这个可能有问题