Php DOMDocument-如何替换嵌套元素

Php DOMDocument-如何替换嵌套元素,php,domdocument,domxpath,Php,Domdocument,Domxpath,我有一个html片段: <font color="#ff0000">Lorem <font size="4">ipsum dolor</font> sit amet</font> 我期望这个输出: <span style="color:#ff0000; ">Lorem <span style="font-size:2em;">ipsum etc.. Lorem ipsum等。。 但我得到: <span style

我有一个html片段:

<font color="#ff0000">Lorem <font size="4">ipsum dolor</font> sit amet</font>
我期望这个输出:

<span style="color:#ff0000; ">Lorem <span style="font-size:2em;">ipsum etc..
Lorem ipsum等。。
但我得到:

<span style="color:#ff0000; ">Lorem ipsum dolor sit amet</span>
Lorem ipsum dolor sit amet
为什么?



我猜这是因为
$font->parentNode->replaceChild($newFont,$font)正在以某种方式将外部跨距仅替换为其文本值。。。或者这个查询
$xPath->query('//font')
是错误的。我喜欢有经验的建议。。。谢谢

看来您的代码示例遇到了几个不同的问题

  • 查询结果包含正在更改的项
  • $node->nodValue不包含子节点
  • 发现从foreach更改为while,并多次运行查询解决了在更改树中查找节点的问题

    $fonts = $xPath->query('//font');
    while ($fonts->length > 0) {
        $font = $fonts->item(0);
    
        // Get bits of data before touching the tree
    
        $style   = '';
        if($font->hasAttribute('size')){
            $size   = $font->getAttribute('size');
            $style .= 'font-size:' . round($size/2, 1) . 'em; ';
        }
        if($font->hasAttribute('color')){
            $style .= 'color:' . $font->getAttribute('color') . '; ';
        }
    
        // Create the new node
    
        $newFont = $dom->createElement('span');
        if(!empty($style)) {
            $newFont->setAttribute('style', $style);
        }
    
    
        // Copy all children into a basic array to avoid an iterator
        // on a changing tree
        $children = iterator_to_array($font->childNodes);
        foreach ($children as $child) {
            // This has a side effect of removing the child from its old
            // location, which changes the tree
            $newFont->appendChild($child);
        }
    
        // Replace the parent's child, which changes the tree
        $font->parentNode->replaceChild($newFont, $font);
    
    
        // query again on the new tree
        $fonts = $xPath->query('//font');
    }
    

    您的代码示例似乎遇到了几个不同的问题

  • 查询结果包含正在更改的项
  • $node->nodValue不包含子节点
  • 发现从foreach更改为while,并多次运行查询解决了在更改树中查找节点的问题

    $fonts = $xPath->query('//font');
    while ($fonts->length > 0) {
        $font = $fonts->item(0);
    
        // Get bits of data before touching the tree
    
        $style   = '';
        if($font->hasAttribute('size')){
            $size   = $font->getAttribute('size');
            $style .= 'font-size:' . round($size/2, 1) . 'em; ';
        }
        if($font->hasAttribute('color')){
            $style .= 'color:' . $font->getAttribute('color') . '; ';
        }
    
        // Create the new node
    
        $newFont = $dom->createElement('span');
        if(!empty($style)) {
            $newFont->setAttribute('style', $style);
        }
    
    
        // Copy all children into a basic array to avoid an iterator
        // on a changing tree
        $children = iterator_to_array($font->childNodes);
        foreach ($children as $child) {
            // This has a side effect of removing the child from its old
            // location, which changes the tree
            $newFont->appendChild($child);
        }
    
        // Replace the parent's child, which changes the tree
        $font->parentNode->replaceChild($newFont, $font);
    
    
        // query again on the new tree
        $fonts = $xPath->query('//font');
    }
    

    使用XSL可以将标记更改为跨度

    <?php
    
    $dom = new DOMDocument();
    
    $dom->loadXML('<font color="#ff0000">Lorem <font size="4">ipsum dolor</font> sit amet</font>');
    
    echo "Starting Point:" . $dom->saveXML() . PHP_EOL;
    
    $xsl = new DOMDocument('1.0', 'UTF-8');
    // Could be a seperate file
    $xsl->loadXML(<<<XSLT
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    version="1.0">
    
        <!-- Identity rule -->
        <xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template>
        <xsl:template match="text()"><xsl:value-of disable-output-escaping="yes" select="."/></xsl:template>
    
        <xsl:template match="font">
            <xsl:element name="span">
                <xsl:attribute name="style" xsl:space="default">
                    <xsl:if test="@size">font-size: <xsl:value-of select="round(@size * 10 div 2) div 10" /> em;</xsl:if>
                    <xsl:if test="@color">color: <xsl:value-of select="@color" />;</xsl:if>
                </xsl:attribute>
                <xsl:apply-templates select="node()"/>
            </xsl:element>
        </xsl:template>
    </xsl:stylesheet>
    XSLT
    );
    
    $proc = new XSLTProcessor();
    $proc->importStylesheet($xsl);
    echo $proc->transformToXML($dom);
    

    使用XSL可以将标记更改为span

    <?php
    
    $dom = new DOMDocument();
    
    $dom->loadXML('<font color="#ff0000">Lorem <font size="4">ipsum dolor</font> sit amet</font>');
    
    echo "Starting Point:" . $dom->saveXML() . PHP_EOL;
    
    $xsl = new DOMDocument('1.0', 'UTF-8');
    // Could be a seperate file
    $xsl->loadXML(<<<XSLT
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    version="1.0">
    
        <!-- Identity rule -->
        <xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template>
        <xsl:template match="text()"><xsl:value-of disable-output-escaping="yes" select="."/></xsl:template>
    
        <xsl:template match="font">
            <xsl:element name="span">
                <xsl:attribute name="style" xsl:space="default">
                    <xsl:if test="@size">font-size: <xsl:value-of select="round(@size * 10 div 2) div 10" /> em;</xsl:if>
                    <xsl:if test="@color">color: <xsl:value-of select="@color" />;</xsl:if>
                </xsl:attribute>
                <xsl:apply-templates select="node()"/>
            </xsl:element>
        </xsl:template>
    </xsl:stylesheet>
    XSLT
    );
    
    $proc = new XSLTProcessor();
    $proc->importStylesheet($xsl);
    echo $proc->transformToXML($dom);
    
    导言
    从下面的对话中

    雷基尔


    为什么不直接使用正则表达式呢

    乔纳夫

    我已经做了很长一段时间了,但我正在尝试切换到DOMDocument/html5lib。。。codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html`

    我完全同意这一点,这就是为什么我认为这不是
    DomDocument
    Regular expression
    的工作,因为您正在处理HTML 5中不再支持的
    折旧HTML标记的问题

    含意 这意味着
    font
    不是您可能还需要更换的唯一问题

    • 首字母缩略词
    • 小程序
    • 基本字体
    • 大的
    • 居中
    • 迪尔
    • 框架
    • 框架集
    • 无框
    • 罢工
    • tt
    • xmp
    使用整洁的 我会推荐一种设计,这样你就不必做你要做的事情

    表单PHP文档

    Tidy是Tidy HTMLcleanrepair实用程序的绑定,它不仅允许您清理和以其他方式操作HTML文档,,还允许您遍历文档树

    实例
    $html='Lorem ipsum door sit amet';
    $config=array(
    “缩进”=>true,
    “仅显示正文”=>false,
    “干净”=>正确,
    “输出xhtml”=>true,
    “保留实体”=>true);
    $tidy=新的tidy();
    echo$tidy->repairString($html,$config,'UTF8');
    
    输出

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
            <title></title>
            <style type="text/css">
                /*<![CDATA[*/
                span.c2 {
                    color: #FF0000
                }
                span.c1 {
                    font-size: 120%
                }
                /*]]>*/
            </style>
        </head>
        <body><span class="c2">Lorem <span class="c1">ipsum dolor</span> sit amet</span>
        </body>
    </html>
    
    
    /**/
    Lorem ipsum dolor sit amet
    
    有关示例,请参见

    更好的窗台:HTMLPurifier 您可以使用,它也可以使用Tidy来清理HTML,您所需要的只是设置TidyLevel

    HTML净化器是一个标准兼容的HTML过滤器库,用PHP编写。HTML净化器不仅将通过彻底审核、安全且允许的白名单删除所有恶意代码(更好地称为XSS),还将确保您的文档符合标准,只有全面了解W3C规范才能实现这一点

    require_once 'htmlpurifier-4.4.0/library/HTMLPurifier.auto.php';
    
    $html = '<font color="#ff0000">Lorem <font size="4">ipsum dolor</font> sit amet</font>';
    $config = HTMLPurifier_Config::createDefault();
    $config->set('HTML.TidyLevel', 'heavy'); 
    $purifier = new HTMLPurifier($config);
    $clean = $purifier->purify($html);
    
    var_dump($clean);
    
    require_once'htmlpurifier-4.4.0/library/htmlpurifier.auto.php';
    $html='Lorem ipsum dolor sit amet';
    $config=HTMLPurifier_config::createDefault();
    $config->set('HTML.TidyLevel','heavy');
    $punizer=新的HTMLPurifier($config);
    $clean=$purizer->purify($html);
    var_dump($清洁);
    
    输出

    string '<span style="color:#ff0000;">Lorem <span style="font-size:large;">ipsum dolor</span> sit amet</span>' (length=100)
    
    字符串“Lorem ipsum dolor sit amet”(长度=100)
    
    我要一份文件 如果你想要的只是dom,而你不在乎我所有的解释,那么你可以使用

    $html = '<font color="#ff0000">Lorem <font size="4">ipsum dolor</font> sit amet</font>';
    $dom = new DOMDocument();
    $dom->loadHTML($html);
    $nodes = iterator_to_array($dom->getElementsByTagName('font'));
    foreach ( $nodes as $font ) {
        $css = array();
        $font->hasAttribute('size') and $css[] = 'font-size:' . round($font->getAttribute('size') / 2, 1) . 'em;';
        $font->hasAttribute('color') and $css[]  = 'color:' . $font->getAttribute('color') . ';';
        $span = $dom->createElement('span');
        $children = array();
        foreach ( $font->childNodes as $child )
            $children[] = $child;
        foreach ( $children as $child )
            $span->appendChild($child);
        $span->setAttribute('style', implode('; ', $css));
        $font->parentNode->replaceChild($span, $font);
    }
    echo "<pre>";
    $dom->formatOutput = true;
    print(htmlentities($dom->saveXML()));
    
    $html='Lorem ipsum door sit amet';
    $dom=新的DOMDocument();
    $dom->loadHTML($html);
    $nodes=iterator_to_数组($dom->getElementsByTagName('font'));
    foreach($font形式的节点){
    $css=array();
    $font->hasAttribute('size')和$css[]='font size:'。舍入($font->getAttribute('size')/2,1)。'em;';
    $font->hasAttribute('color')和$css[]='color:'。$font->getAttribute('color');';
    $span=$dom->createElement('span');
    $children=array();
    foreach($font->childNodes作为$child)
    $children[]=$child;
    foreach($childrenas$child)
    $span->appendChild($child);
    $span->setAttribute('style',内爆(';',$css));
    $font->parentNode->replaceChild($span,$font);
    }
    回声“;
    $dom->formatOutput=true;
    打印(htmlentities($dom->saveXML());
    
    简介 从下面的对话中

    雷基尔


    为什么不直接使用正则表达式呢

    乔纳夫

    我已经做了很长一段时间了,但我正在尝试切换到DOMDocument/html5lib。。。codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html`

    我完全同意这一点,这就是为什么我认为这不是
    DomDocument
    Regular expression
    的工作,因为您正在处理HTML 5中不再支持的
    折旧HTML标记的问题

    含意 这意味着
    font
    不是您可能还需要更换的唯一问题

    • 首字母缩略词
    • 小程序
    • 基本字体
    • 大的
    • 居中
    • 迪尔
    • 框架
    • 框架集
    • 无框
    • 罢工
    • tt
    • xmp
    使用整洁的 我会推荐一种设计,这样你就不必做你要做的事情

    表单PHP文档

    Tidy是Tidy HTMLcleanrepair实用程序的绑定,该实用程序允许