我有一个很小的类,它将帮助我用有效的HTML标签替换自定义标签。我的问题是,无论出于何种原因,它仅替换第一个自定义标签。我的猜测是我在某处打破了参考,但是我不知道在哪里...向下滚动到这篇文章的底部以查看实际结果和预期的输出。
<?php
class DomParser {
protected $tags = [];
protected $document;
public function __construct($html) {
$this->document = new DOMDocument();
$this->document->loadXML($html);
}
public function addTag(string $name, callable $callable) {
$this->tags[$name] = $callable;
}
public function replace() {
foreach ($this->tags as $name => $callable) {
$elements = $this->document->getElementsByTagName($name);
foreach ($elements as $element) {
$callable($element, $this->document);
}
}
return $this->document->saveHTML();
}
}
运行该类的示例代码:
<?php
require_once 'DomParser.php';
//require_once 'RenameTag.php';
//require_once 'Container.php';
$html = '<html>
<container>
<col>
<p>
<test attribute="test" attribute2="this">test<br />test2</test>
</p>
</col>
<col>
test col
</col>
</container>
<container fluid="test"><test>dsdshsh</test></container>
</html>';
$parser = new DomParser($html);
//$parser->addTag('test', RenameTag::create('othertag'));
//$parser->addTag('container', Container::create());
$parser->addTag('col', function($oldTag) {
$document = $oldTag->ownerDocument;
$newTag = $document->createElement('div');
$oldTag->parentNode->replaceChild($newTag, $oldTag);
foreach (iterator_to_array($oldTag->childNodes) as $child) {
$newTag->appendChild($oldTag->removeChild($child));
}
$newTag->setAttribute('class', 'col');
});
echo $parser->replace();
我得到这个结果:
<html>
<container>
<div class="col">
<p>
<test attribute="test" attribute2="this">test<br>test2</test>
</p>
</div>
<col>
</container>
<container fluid="true"><test>dsdshsh</test></container>
</html>
预期输出应为:
<html>
<container>
<div class="col">
<p>
<test attribute="test" attribute2="this">test<br>test2</test>
</p>
</div>
<div class="col">
test col
</div>
</container>
<container fluid="test"><test>dsdshsh</test></container>
</html>
答案 0 :(得分:1)
问题似乎是您在尝试遍历文档结构时正在更改文档结构。
另一种选择是使用XPath,它将使用它自己的节点副本供您遍历,更改很小,但是会在输出后给您...
public function replace() {
$xp = new DOMXPath($this->document);
foreach ($this->tags as $name => $callable) {
$elements = $xp->query("//".$name);
foreach ($elements as $element) {
$callable($element, $this->document);
}
}
return $this->document->saveHTML();
}
答案 1 :(得分:0)
如果我没记错的话,我之前已经处理过,或者可以使用回归循环:
public function replace() {
foreach ($this->tags as $name => $callable) {
$elements = $this->document->getElementsByTagName($name);
$i = $elements->length - 1;
while ($i > -1) {
$element = $elements->item($i);
$callable($element, $this->document);
$i--;
}
}
return $this->document->saveHTML();
}
答案 2 :(得分:0)
DOMNode::getElementsByTagName()
返回“实时”结果。项目和列表随文档的更改而更改。您修改文档,以便列表中的项目也更改。这是避免问题的树方法。
您可以反向迭代列表(使用for循环)。在大多数情况下,这意味着您仅更改文档的不影响节点列表中先前元素的部分。
使用返回稳定结果的方法。 DOMXpath::evaluate()
(和DOMXpath::query()
)返回稳定列表。 Xpath表达式也减少了获取节点所需的代码量。
使用iterator_to_array()
将节点列表转换为数组。这将创建一个包含节点对象的节点列表的数组副本。您实际上在示例代码中使用了该方法。