开发者

Replace specific <font> tag with <span> using DOM manipulation in PHP

开发者 https://www.devze.com 2023-02-11 20:00 出处:网络
I\'m trying to replace alltags withtags using DOMDocument in PHP and almost all my tests passed. I\'m sure there are other scenarios I\'m forgetting, but for now, I\'m missing just one:

I'm trying to replace all tags with tags using DOMDocument in PHP and almost all my tests passed. I'm sure there are other scenarios I'm forgetting, but for now, I'm missing just one:

ORIGINAL:

<p><font color="#ff0000">BEFORE <font color="#00ff00">BEFORE <font color="#0000ff">VAL</font> AFTER</font> AFTER</font></p>

RESULT:

<p><span style="color: #ff0000">BEFORE BEFORE VAL AFTER AFTER</span></p>

The PHP code for this:

$html = '<p><font color="#ff0000">BEFORE <font color="#00ff00">BEFORE <font color="#0000ff">VAL</font> AFTER</font> AFTER</font></p>';

$dom = new DOMDocument();
$dom->loadHTML($html);

foreach($dom->getElementsByTagName('font') as $node) {
    $font_nodes[] = $node;
}

//$font_nodes = array_reverse($font_nodes);

foreach($font_nodes as $font) {
    $a_style = array_filter(explode(';', $font->getAttribute('style')));

    if($a_color = $font->getAttribute('color')) {
        $a_style[] 开发者_JS百科= 'color: '.$a_color;
    }

    $span = $dom->createElement('span', $font->nodeValue);
    $span->setAttribute('style', implode('; ', $a_style));

    $font->parentNode->replaceChild($span, $font);
}

echo preg_replace("#(<!DOCTYPE.+|<\/?html>|<\/?body>)#", '', $dom->saveHTML());

I thought that getElementsByTagName was the culprit since it was loading the nodes in order, so I tried to start from the deepest tag by reversing the array, but that didn't work so the line is commented.

P.S: In case you're wondering why the first loop is needed to save all the nodes and loop them again, please read this: http://robrosenbaum.com/php/domnodelist-gotchas/


This works. I tested it. :-)

Replace the line:

$span = $dom->createElement('span', $font->nodeValue);

With this:

$span = $dom->createElement('span');
$children = array(); 
foreach ($font->childNodes as $child) $children[] = $child;
foreach ($children as $child) $span->appendChild($child);


When you call replaceChild, the child nodes of the font element remain attached to the removed node, rather than become attached to the new span element. The font elements are all individually being replaced, but they're no longer attached to the rest of the dom elements. This is why your visible output ends at the top level span element, which is empty.

To solve this, you will need to add a few extra lines of code to copy all of the child nodes from $font to $span after replaceNode.


I'm pretty sure:

$font->parentNode->replaceChild($span, $font);

is your culprit because the nested elements' parents are no longer in the document. You were on the right track trying to reverse the order, but when you reversed, you overwrote your changes because you were using a copy of the parent font that still included the child font. I don't have a system I can test on, but I imagine code like this will do what you want it to:

$dom = new DOMDocument();
$dom->loadHTML($html);

$font_nodes = $dom->getElementsByTagName('font');
while($font = $font_nodes->item(0)) {
    $a_style = array_filter(explode(';', $font->getAttribute('style')));

    if($a_color = $font->getAttribute('color')) {
        $a_style[] = 'color: '.$a_color;
    }

    $span = $dom->createElement('span', $font->nodeValue);
    $span->setAttribute('style', implode('; ', $a_style));

    $font->parentNode->replaceChild($span, $font);

    $font_nodes = $dom->getElementsByTagName('font');
}
0

精彩评论

暂无评论...
验证码 换一张
取 消