【问题标题】:DOMElement absolutely exists, but PHP gives fatal error saying it doesn't, right after saying it doesDOMElement 绝对存在,但是 PHP 给出了致命错误,说它不存在,就在说它存在之后
【发布时间】:2021-05-17 07:45:20
【问题描述】:

考虑以下代码:

$xmlstr = new DOMDocument();
$xmlstr->loadXML($text, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING);
$xp = new DOMXPath($xmlstr);
$xp->registerNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
$contents = $xp->query("//w:p");
foreach ($contents as $node)
{
        $children = $node->childNodes;
}

这适用于数百个节点,然后突然:

Fatal error: Uncaught Error: Couldn't fetch DOMElement. Node no longer exists

此错误是在以下行中生成的:

$children = $node->childNodes;

如您所见,没有任何处理:foreach $contents as $node 只能在 $node 存在的情况下给出 $node,但是在 PHP 大喊它存在之后 $node 突然不存在。这是不可能的。这是怎么回事?

预先检查节点是否有子节点,甚至检查 (if ($node) 没有改变任何东西。试图捕捉也没有改变任何东西:

try {
     $children = $node->childNodes;
}
catch (Exception $e)
{
     echo 'Okay, that didn't work<br>';
}

仍然会产生同样的错误。错误未被捕获。

当我在实际对节点执行任何操作之前执行 print_r 时,它会说: DOM 元素 ( ) IE。节点无效。

我已经接近了一点。该节点消失,因为前一个节点中的文本被错误地修改。如果我修改除节点号 195 之外的所有节点,则读取节点 196 时不会出错。如果我也修改节点号 195,节点 196 会突然消失。节点 195 的长度超过 118.000 个字符。我想知道是否有某种限制?如果我将其内容导出到文件中,字符串不会被截断,所以我想知道 $node->textContent 是否有限制?

【问题讨论】:

  • DOMXPath() 期望 DOM 不是字符串! php.net/manual/en/domxpath.construct
  • 您无法使用catch (Exception $e) 捕获错误异常;为此,您需要 catch (Error $e)catch(Throwable $e)。不仅如此,无法捕获致命错误。 php.net/manual/en/language.errors.php7.php
  • @MarkusZeller 道歉,这个名字具有欺骗性。我现在在它前面添加了两行。它不是一个字符串。如前所述,这适用于之前的数百个节点。
  • @ZoliSzabó 好的,这就解释了为什么 catch 不起作用,但仍然不能解决问题。
  • 很难在没有看到 DOM 的情况下进行调试。

标签: php dom


【解决方案1】:

事实证明,w:p 节点实际上可以有其他 w:p 节点作为它们的后代(准确地说,包含嵌入表格的段落包含包含包含新段落的单元格的行)。这导致了两个问题:函数两次导入被其他段落包围的段落,一次作为封闭段落的一部分,一次作为封闭段落本身。封闭的段落也太长了(PHP 代码中断了 118.000 个字符),因此插入到节点的文本实际上会破坏它们,导致致命错误,只有在我们尝试打开 时才检测到下一个节点。

如果我们在一个节点中检测到innerHTML,实际上每个节点的HTML内容都会被解析和简化。如果节点为空或我们忽略它,则修改后的内容不会写入该节点。我在代码中添加了额外的一行:当现在检测到节点内的 w:p 内容时,简单地忽略所述节点。这样,如果 w:p 节点包含其他 w:p 节点,我们将永远不会处理它们。

    $innerHTML .= $child->ownerDocument->saveXML($child);

改成

if (!str_contains($child->ownerDocument->saveXML($child), '<w:p'))
{
    $innerHTML .= $child->ownerDocument->saveXML($child);
}

【讨论】:

    猜你喜欢
    • 2012-01-24
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-22
    相关资源
    最近更新 更多