How can I change the name of an element in DOM?

后端 未结 3 1601
感情败类
感情败类 2020-12-01 18:53

In PHP with DOM, I have a DomElement object which represents an element.

I have one case where I need to change this so its element name is

相关标签:
3条回答
  • 2020-12-01 19:33

    NOTE: I tried Calvin's code and it sort of worked for me but not quite. If the tag I was replacing had nested tags, some child tags would sometimes get lost.

    The reason is that childNodes is a live DOMNodeList of a node's children, and appendChild moves the nodes around in the DOM, thus affecting the list ordering. If you just do foreach on a childNodes the loop can skip some children.

    My solution was to use a while loop. This way you don't have to copy any nodes to an array.

    I have packaged everything in a convenient function that takes a string and returns a string and should work with utf-8. The following is tested in 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');
    

    Prints:

    Testing <strong foo="bar" baz="foo">nested tags in <i lol="cat"> html strings</i></strong> and <strong>stuff</strong>
    
    0 讨论(0)
  • 2020-12-01 19:40

    Could you use importNode() to copy the childNodes of your <identity> element to a 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');
    

    Perhaps something like that would work, or you could try using cloneChild().

    Edit: Actually, I just realized that the original function would lose the placement of the node. As per the question thomasrutter linked to, replaceChild() should be used.

    0 讨论(0)
  • 2020-12-01 19:44

    Thanks to your post, I could quickly solve the same issue for me. However, I had a DOM_NOT_FOUND exception. This is probably a PHP Version issue, since the original post is 5 years old.

    According to the PHP Documentation (Feb 2014)

    DOM_NOT_FOUND
      Raised if oldnode is not a child of this node. 
    

    So, I have replaced

    $newnode->ownerDocument->replaceChild($newnode, $node);
    

    with

    $node->parentNode->replaceChild($newnode, $node);
    

    Here is the complete 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 should traverse the DOM Tree in reversed order as explained in other posts.

    UPDATE: After months of using and updating to PHP Version 5.5.15 on windows, I had an error saying $attr could not be converted to a string. So I updated third for-each loop above.

    0 讨论(0)
提交回复
热议问题