在带DOM的PHP中,我有一个代表<identity />元素的DomElement对象。
我有一个案例,我需要更改它,因此它的元素名称是<person />,但保持相同的子元素和属性。
更改DomElement的元素名称并保留其子项和属性的最简单方法是什么?
编辑:我刚刚找到一个very similar question(虽然它没有很好的答案)。
你能用importNode()
将你的<identity>
元素的childNodes复制到新创建的<person>
元素吗?
function changeName($node, $name) {
$newnode = $node->ownerDocument->createElement($name);
foreach ($node->childNodes as $child){
$child = $node->ownerDocument->importNode($child, true);
$newnode->appendChild($child, true);
}
foreach ($node->attributes as $attrName => $attrNode) {
$newnode->setAttribute($attrName, $attrNode);
}
$newnode->ownerDocument->replaceChild($newnode, $node);
return $newnode;
}
$domElement = changeName($domElement, 'person');
也许这样的东西可行,或者你可以尝试使用cloneChild()
。
编辑:实际上,我刚刚意识到原始函数会丢失节点的位置。根据thomasrutter所关联的问题,应该使用replaceChild()
。
感谢您的帖子,我可以快速为我解决同样的问题。但是,我有一个DOM_NOT_FOUND异常。这可能是PHP版本问题,因为原帖是5年。
根据PHP文档(2014年2月)
DOM_NOT_FOUND
Raised if oldnode is not a child of this node.
所以,我已经取代了
$newnode->ownerDocument->replaceChild($newnode, $node);
同
$node->parentNode->replaceChild($newnode, $node);
这是完整的功能(已测试):
public static function changeTagName($node, $name) {
$childnodes = array();
foreach ($node->childNodes as $child){
$childnodes[] = $child;
}
$newnode = $node->ownerDocument->createElement($name);
foreach ($childnodes as $child){
$child2 = $node->ownerDocument->importNode($child, true);
$newnode->appendChild($child2);
}
foreach ($node->attributes as $attrName => $attrNode) {
$attrName = $attrNode->nodeName;
$attrValue = $attrNode->nodeValue;
$newnode->setAttribute($attrName, $attrValue);
}
$node->parentNode->replaceChild($newnode, $node);
return $newnode;
}
还值得一提的是,当您想要使用此函数时,您应该按照其他帖子中的说明以相反的顺序遍历DOM树。
更新:在Windows上使用和更新到PHP版本5.5.15几个月后,我遇到错误,说$ attr无法转换为字符串。所以我更新了第三个for-each循环。
注意:我尝试了Calvin的代码,它对我有用,但并不完全。如果我替换的标签有嵌套标签,有些子标签有时会丢失。
原因是childNodes是节点子节点的实时DOMNodeList,appendChild
在DOM中移动节点,从而影响列表排序。如果您只是在childNodes上执行foreach
,则循环可以跳过一些孩子。
我的解决方案是使用while循环。这样您就不必将任何节点复制到数组中。
我已将所有内容打包在一个方便的函数中,该函数接受一个字符串并返回一个字符串,并且应该使用utf-8。以下是在PHP 5.5.9中测试的。
function renameTags($html, $oldname, $name) {
$dom = new DOMDocument( '1.0', 'utf-8' );
$fragment = $dom->createDocumentFragment();
if ( $fragment->appendXML( $html ) ) {
$dom->appendChild( $fragment );
$nodesToAlter = $dom->getElementsByTagName( $oldname );
while ($nodesToAlter->length) {
$node = $nodesToAlter->item(0);
$newnode = $node->ownerDocument->createElement($name);
while ($node->hasChildNodes()) {
$child = $node->childNodes->item(0);
$child = $node->ownerDocument->importNode($child, true);
$newnode->appendChild($child);
}
foreach ($node->attributes as $attr) {
$attrName = $attr->nodeName;
$attrValue = $attr->nodeValue;
$newnode->setAttribute($attrName, $attrValue);
}
$newnode->ownerDocument->replaceChild($newnode, $node);
}
return $dom->saveHTML();
} else {
//error
}
}
$html = 'Testing <b foo="bar" baz="foo">nested tags in <i lol="cat"> html strings</i></b> and <b>stuff</b>';
echo renameTags($html, 'b', 'strong');
打印:
Testing <strong foo="bar" baz="foo">nested tags in <i lol="cat"> html strings</i></strong> and <strong>stuff</strong>