【问题标题】:DOMNode replacement with PHP's DOM classes用 PHP 的 DOM 类替换 DOMNode
【发布时间】:2011-06-04 16:17:59
【问题描述】:

我正在学习使用 PHP 中可用的 DOM* 类,并注意到(我认为是)我的测试中存在异常情况。

鉴于这份文件,ZuqML_test_100.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:zuq="http://localhost/~/zuqml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body>
    <h1>
        <zuq:data name="siteHeader" />
    </h1>
    <h2>
        <zuq:data name="pageHeaderName" />
        <span>&nbsp;|&nbsp;</span>
        <zuq:data name="pageHeaderTitle" />
    </h2>
    <zuq:region name="post">
        <zuq:param name="onEmpty">
            <div class="post noposts">
                <p>There are no posts to show at this time.</p>
            </div>
        </zuq:param>
        <div class="post">
            <h3><zuq:data name="postHeader" /></h3>
            <p>
                <zuq:data name="postText">
                    <zuq:format type="trim">
                        <zuq:param name="length">300</zuq:param>
                        <zuq:param name="append">
                            <a>
                                <zuq:attr name="href">
                                    ./?action=viewpost&amp;id=<zuq:data name="postId" />
                                </zuq:attr>
                                <zuq:data name="postAuthor" />
                            </a>
                        </zuq:param>
                    </zuq:format>
                </zuq:data>
            </p>
        </div>
    </zuq:region>
</body>
</html>

我正在尝试将所有 &lt;zuq:data /&gt; 节点替换为值为 foo 的简单文本节点。我正在使用以下 sn-p 这样做:

$root = new DOMDocument();
@$root->load('ZuqML_test_100.html');

foreach($root->getElementsByTagNameNS($root->lookupNamespaceURI('zuq'), 'data') as $node){
    $node->parentNode->replaceChild($node->ownerDocument->createTextNode('foo'), $node);
}

echo $root->saveXML();

它有点工作,但是我的输出似乎仍然包含&lt;zuq:data /&gt; 节点,如下所示:

<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:zuq="http://ichorworkstudios.no-ip.org/~/zuqml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>

<body>

        <h1>
        foo
    </h1>

    <h2>
        <zuq:data name="pageHeaderName"></zuq:data>
        <span>&mdash;</span>
        foo
    </h2>

    <zuq:region name="post">
        <zuq:param name="onEmpty">
                <div class="post noposts">
                <p>There are no posts to show at this time.</p>
            </div>
        </zuq:param>
        <div class="post">
                <h3><zuq:data name="postHeader"></zuq:data></h3>
            <p>
                foo
                </p>
        </div>
    </zuq:region>

</body>
</html>

为什么会留下一些&lt;zuq:data /&gt; 节点?

【问题讨论】:

    标签: php dom domdocument


    【解决方案1】:

    我认为这与您的迭代方式有关。您正在更改结果列表,因为它正在被迭代,所以它最终会破坏(副作用)。尝试将循环更改为:

    $nodes = $root->getElementsByTagNameNS($root->lookupNamespaceURI('zuq'), 'data');
    $i = $nodes->length - 1;
    while ($i >= 0) {
        $node = $nodes->item($i);
        $node->parentNode->replaceChild(
            $node->ownerDocument->createTextNode('foo'), 
            $node
        );
        $i--;
    }
    

    基本上,它只是在节点列表上向后迭代,因此当删除节点时,它们是从末尾而不是开头删除...

    【讨论】:

    • 提供Fatal error: Cannot use object of type DOMNodeList as array in F:\Work\code\gordonoheim\sandbox\newfile.php on line 25,除非你提供$nodes-&gt;item($i)
    • 谢谢ircmaxell;复制粘贴被证明是不成功的,使用Fatal error: Cannot use object of type DOMNodeList as array 退出。 @戈登;我以为会是这样。
    • @Gordon:谢谢,我忘了那个。假设迭代器实现了数组访问,这是一个简单的错误。愚蠢的错误......感谢您抓住它。
    【解决方案2】:

    ircmaxell 提供的解释

    您正在更改结果列表,因为它正在被迭代,

    是正确的,尽管我认为我添加了更多细节,以便您了解为什么会发生这种情况。

    这是您的代码在运行时的作用

    一开始NodeList中会有7个节点。

    第一个是

    <zuq:data name="siteHeader"></zuq:data>
    

    删除之后,节点数下降到六个。下一个要移除的节点是

    <zuq:data name="pageHeaderTitle"></zuq:data>
    

    但是如果你查看你的标记,你会发现下一个 zuq:data 元素实际上是

    <zuq:data name="pageHeaderName" />
    

    现在的问题是,当您从当前也在当前正在迭代的 NodeList 中的文档中删除一个节点时,该节点也将从 NodeList 中删除。但是NodeList中的当前位置还是一样的(或者自动前进,不知道往哪边走),例如

    0 siteHeader
    1 pageHeaderName
    2 pageHeaderTitle
    n …
    

    当当前位置为 0 并且您从文档中删除该节点时,您会得到一个这样的列表

    0 pageHeaderName
    1 pageHeaderTitle
    n …
    

    但您仍处于位置 0,因此,当您转到 NodeList 中的下一个元素时,您将跳过新位置 0 处的节点。您直接转到 pageHeaderTitle,而 pageHeaderName 未处理。

    pageHeaderTitle 被移除后,节点数下降到五个,使得

    <zuq:data name="pageHeaderName"></zuq:data>
    

    当前位置的新元素。因此,下一个要删除的节点是

    <zuq:data name="postText">
        <zuq:format type="trim">
        <zuq:param name="length">300</zuq:param>
            <zuq:param name="append">
            <a>
            <zuq:attr name="href">
            ./?action=viewpost&amp;id=
            <zuq:data name="postId"></zuq:data>
            </zuq:attr>
            <zuq:data name="postAuthor"></zuq:data>
            </a>
        </zuq:param>
        </zuq:format>
    </zuq:data>
    

    如您所见,其中还有两个 zuq:data 元素。因此,节点数将降至 2(5 - 1 个当前节点 - 2 个子节点)。

    之后,对 NodeList 的迭代结束,剩下的就是

    <zuq:data name="postHeader"></zuq:data>
    

    <zuq:data name="pageHeaderName"></zuq:data>
    

    仍在文档中。

    【讨论】:

    • 感谢 Gordon 的全面解释,非常感谢。我一定会牢记这一点。
    猜你喜欢
    • 1970-01-01
    • 2017-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-11
    • 2013-01-24
    相关资源
    最近更新 更多