我正在学习使用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> | </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&id=<zuq:data name="postId" />
</zuq:attr>
<zuq:data name="postAuthor" />
</a>
</zuq:param>
</zuq:format>
</zuq:data>
</p>
</div>
</zuq:region>
</body>
</html>
我正在尝试使用值为<zuq:data />
的简单文本节点替换所有foo
个节点。我正在使用以下代码片段:
$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();
它有点工作,但我的输出似乎仍包含<zuq:data />
个节点,如下所示:
<?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>—</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>
为什么会留下一些<zuq:data />
个节点?
答案 0 :(得分:3)
我认为这与你如何迭代有关。你正在改变结果列表,因为它正在被反复,所以它最终会破坏(副作用)。尝试将循环更改为:
$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--;
}
基本上,它只是在节点列表上向后迭代,因此当删除节点时,它们将从末尾而不是从头开始删除......
答案 1 :(得分:3)
ircmaxell提供的解释
您正在更改结果列表,因为它正在迭代,
是正确的,虽然我认为我添加了一些更多的细节,所以你可以理解为什么会这样。
以下是您的代码在运行时所执行的操作
一开始,NodeList中将有七个节点。
第一个是
<zuq:data name="siteHeader"></zuq:data>
删除之后,节点数降至6。要删除的下一个节点是
<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后,节点数降至5,即
<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&id=
<zuq:data name="postId"></zuq:data>
</zuq:attr>
<zuq:data name="postAuthor"></zuq:data>
</a>
</zuq:param>
</zuq:format>
</zuq:data>
正如您所看到的,还有两个zuq:数据元素。因此,节点数将减少到2(5 - 1个当前节点 - 2个子节点)。
之后,NodeList上的迭代结束,只剩下
<zuq:data name="postHeader"></zuq:data>
和
<zuq:data name="pageHeaderName"></zuq:data>
仍在文件中。