How to change element name in DOM?
In PHP with the DOM, I have a DomElement object that represents a <identity / "> element.
I have one case where I need to change this so that its element name is <person / "> but retains the same child elements and attributes.
What would be the easiest way to change the name of a DomElement element and save its children and attributes?
Edit: I just found a very similar question (although it does not have very good answers).
Could you use importNode() to copy the childNodes of your <identity> element to the newly created <person> element?
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'); Maybe something like this works, or you can try using cloneChild() .
Edit: Actually, I just realized that the original function would lose the placement of the node. According to the question that should be related to tomasrutter, replaceChild() .
Thanks to your post, I could quickly solve the same problem for me. However, I had a DOM_NOT_FOUND exception. This is probably a problem with the PHP version since the original post is 5 years old.
According to PHP documentation (February 2014)
DOM_NOT_FOUND Raised if oldnode is not a child of this node. So I replaced
$newnode->ownerDocument->replaceChild($newnode, $node); from
$node->parentNode->replaceChild($newnode, $node); Here is the full function (tested):
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; } It is also worth mentioning that when you want to use this function, you must traverse the DOM Tree in the reverse order, as described in other posts.
UPDATE: after several months of using and updating to PHP 5.5.15 on Windows, I had an error saying that $ attr could not be converted to a string. Therefore, I updated the third for each cycle above.
NOTE. I tried Calvin code and it worked for me, but not really. If the tag that I replaced had nested tags, some child tags were sometimes lost.
The reason is that childNodes is a live DOMNodeList for children node, and appendChild moves nodes around in the DOM, which affects the ordering of the list. If you just do foreach on childNodes, the loop may skip some children.
My solution was to use a while loop. This way you do not need to copy any nodes to the array.
I packed everything in a convenient function that takes a string and returns a string and should work with utf-8. In PHP 5.5.9, the following is tested.
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>