Статьи

Переименование DOMNode в PHP

В недавнем рабочем задании я использовал PHP для извлечения данных HTML в экземпляр DOMDocument и переименования некоторых элементов, таких как b в strong или i в em . Оказывается, переименование элементов с использованием расширения DOM довольно утомительно.

Версия 3 стандарта DOM представляет метод renameNode () , но расширение PHP DOM в настоящее время не поддерживает его.

$ NODENAME свойство DOMNode класса только для чтения, поэтому он не может быть изменен таким образом.

Узел может быть создан с другим именем в том же документе, но если вы укажете значение, которое будет сопровождать его, любые объекты в этом значении будут автоматически закодированы, поэтому невозможно передать предполагаемое внутреннее содержимое узла, если он содержит другие узлы.

Единственный найденный мной метод — это репликация атрибутов и дочерних узлов исходного узла. Атрибуты довольно просты, но я столкнулся с проблемой репликации дочерних элементов, когда только первый дочерний элемент любого данного узла был реплицирован в рамках его предполагаемой замены, а остальные дочерние элементы были опущены. Вот оригинальный код, демонстрирующий это поведение.

foreach ($oldNode->childNodes as $childNode) {
    $newNode->appendChild($childNode);
}

Причиной такого поведения является то , что  $ ChildNodes свойство $ oldNode не неявно изменяется при $ childNode передается от него до $ newNode, поэтому внутренний указатель $ ChildNodes к следующему ребенку в списке больше не точны.

Чтобы обойти это, я воспользовался тем, что любой узел с любыми дочерними узлами всегда будет иметь свойство $ firstChild, указывающее на первый. Модифицированный код, который использует этот подход, представлен ниже и имеет поведение, которое я изначально намеревался реализовать

while ($oldNode->firstChild) {
    $newNode->appendChild($oldNode->firstChild);
}

Если вам интересно, ниже приведен сегмент полного кода для переименования узла.

$newNode = $oldNode->ownerDocument->createElement('new_element_name');
if ($oldNode->attributes->length) {
    foreach ($oldNode->attributes as $attribute) {
        $newNode->setAttribute($attribute->nodeName, $attribute->nodeValue);
    }
}
while ($oldNode->firstChild) {
    $newNode->appendChild($oldNode->firstChild);
}
$oldNode->ownerDocument->replaceChild($newNode, $oldNode);

Еще одна потенциальная «ошибка» — это порядок аргументов метода replaceChild () , который представляет собой новый узел, за которым следует старый узел, а не обратный, как может ожидать большинство людей. Спасибо  Джошуа Мэй за то, что указал мне на это; Я никогда не мог понять , почему я получаю  «не найден» DOMException иначе.