使用 DOMXPath 清理弃用的 HTML 代码(将嵌套的 <div> 标签转换为 <p> 标签)

Cleaning up deprecated HTML code with DOMXPath (convert nested <div> tags to <p> tags)

我正在尝试将存储在旧 MS Access 数据库中的富文本读入新的 PHP 网络应用程序。清理后的数据将使用 CKEditor 显示给用户,CKEditor 对符合 HTML 代码的解析标准非常严格。但是,存储在 MS Access 中的数据通常格式不正确或使用已弃用的 HTML 代码。

下面是我要清理的数据示例:

<div align="right">Previous claim $ &nbsp;&nbsp;935.00<div align="right">&nbsp;&nbsp;This claim ,572.50</div></div>

此数据应该是两行右对齐的文本,但是 MS Access 使用已弃用的 align 属性来设置 <div> 标签的样式style 属性,并且错误地嵌套了它们,而在这种情况下它们应该是顺序的。

为了将此示例数据转换为两行右对齐的文本,并且 CKEditor 将按预期读取和显示(即文本显示为右对齐),我正在尝试替换 <div>带有 <p> 标签的标签,并注入带有右文本对齐的内联样式属性以替换已弃用的对齐属性。

我正在使用PHP的DOMXPath清理数据,代码如下:

$dom = new DOMDocument();
$dom->loadHTML($dataForCleaning, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

$xpath = new DOMXPath($dom);

foreach ($xpath->query('//div[@align]') as $node) {
    $alignment = $node->getAttribute('align');

    $newNode = $dom->createElement('p');
    $newNode->setAttribute("style", "text-align:".$alignment);
    $node->parentNode->insertBefore($newNode, $node);

    foreach ($node->childNodes as $child) {
        $newNode->appendChild($child);
    }

    $node->parentNode->removeChild($node);
}

我正在使用 insertBefore 代替 appendChild 来尝试保持元素的顺序相同,但这就是导致此嵌套数据示例出现问题的原因。

对于非嵌套 <div> 标签作为要清理的输入数据,清理后的输出 html 是正确的。但是,在这个嵌套的 <div> 示例中,输出最终为:

<p style="text-align:right">Previous claim $ &nbsp;&nbsp;935.00</p>

请注意,第二行文本 (This claim...) 已被删除,因为它在嵌套 <div> 中作为父项的子项<div>

我不介意生成的 <p> 标签是否保持嵌套,因为 CKEditor 最终会清理这些标签,但我确实需要确保我不会像当前代码那样丢失数据。

在此先感谢您的帮助和指导。 -马克

我更改了一些内容。首先是,我不只是附加现有节点,而是克隆节点并附加副本(在 $newNode->appendChild($child->cloneNode(true)); 中),第二件事是当您移动封闭的节点时,我认为XPath 不再指向这个移动的节点。因此,取而代之的是,我在复制子节点时检查是否具有相同的 <div align="right"> 节点模式,如果是,我将以新格式创建一个新节点并添加它...

foreach ($xpath->query('//div[@align]') as $node) {
    $alignment = $node->getAttribute('align');

    $newNode = $dom->createElement('p');
    $newNode->setAttribute("style", "text-align:".$alignment);

    $node->parentNode->insertBefore($newNode, $node);
    foreach ($node->childNodes as $child) {
        if ( $child instanceof DOMElement && $child->localName == "div"
                && $child->attributes->getNamedItem("align")->nodeValue == "right" )    {
            $subNode = $dom->createElement('p', $child->nodeValue );
            $subNode->setAttribute("style", "text-align:".$alignment);
            $newNode->appendChild($subNode);
        }
        else    {
            $newNode->appendChild($child->cloneNode(true));
        }
    }

    $node->parentNode->removeChild($node);
}

对于您给出的示例,输出的是...

<p style="text-align:right">
    Previous claim $ &nbsp;&nbsp;935.00
    <p style="text-align:right">&nbsp;&nbsp;This claim ,572.50</p>
</p>