Как изменить имя элемента в DOM?

в PHP с DOM у меня есть объект DomElement, который представляет элемент .

У меня есть один случай, когда мне нужно изменить это, чтобы его имя элемента было , но сохранить те же дочерние элементы и атрибуты.

каков был бы самый простой способ изменения имени элемента DomElement и сохранения его дочерних элементов и атрибутов?

Edit: я только что нашел очень похожий вопрос (хотя это не очень хорошо находить ответы.)

3 ответов


не могли бы вы использовать importNode() чтобы скопировать childNodes вашего <identity> элемент для вновь созданной <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().

Edit: на самом деле, я только что понял, что исходная функция потеряет размещение узла. Согласно thomasrutter вопрос связан с, replaceChild() должен быть использован.


благодаря вашему сообщению, я мог бы быстро решить ту же проблему для меня. Однако у меня было исключение DOM_NOT_FOUND. Это, вероятно, проблема с версией PHP, так как исходному сообщению 5 лет.

согласно документации PHP (февраль 2014)

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 в обратном порядке, как описано в других сообщениях.

UPDATE: после нескольких месяцев использования и обновления до версии PHP 5.5.15 в windows, у меня была ошибка, говорящая, что $attr не может быть преобразован в строку. Поэтому я обновил третий цикл для каждого цикла выше.


Примечание: я попробовал код Кельвина, и это вроде как сработало для меня, но не совсем. Если тег, который я заменял, имел вложенные теги, некоторые дочерние теги иногда терялись.

причина в том, что childNodes-это live DOMNodeList детей узла, и appendChild перемещает узлы в DOM, что влияет на порядок списка. Если вы просто делаете foreach на childNodes цикл может пропустить некоторые дети.

моим решением было использовать некоторое время петля. Таким образом, вам не нужно копировать какие-либо узлы в массив.

я упаковал все в удобную функцию, которая принимает строку и возвращает строку и должна работать с 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>