Как я могу изменить имя элемента в DOM? - PullRequest
7 голосов
/ 22 апреля 2009

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

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

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

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

Ответы [ 3 ]

13 голосов
/ 22 апреля 2009

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

Редактировать: На самом деле, я только что понял, что исходная функция потеряет расположение узла. В соответствии с вопросом, с которым связан thomasrutter, следует использовать replaceChild().

8 голосов
/ 19 февраля 2014

Благодаря вашему посту я смог быстро решить ту же проблему для меня. Однако у меня было исключение 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 в обратном порядке, как объяснено в других публикациях.

ОБНОВЛЕНИЕ: После нескольких месяцев использования и обновления до версии PHP 5.5.15 в Windows у меня была ошибка, что $ attr не удалось преобразовать в строку. Так что я обновил третий цикл для каждого выше.

1 голос
/ 04 февраля 2016

ПРИМЕЧАНИЕ: я попробовал код Кальвина, и он вроде работал для меня, но не совсем. Если бы у заменяемого тега были вложенные теги, некоторые дочерние теги иногда терялись.

Причина в том, что childNodes - это live DOMNodeList дочерних узлов, а appendChild перемещает узлы в DOM, что влияет на порядок списка. Если вы просто делаете 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...