PHP DOMDocument用HTML字符串替换DOMElement子节点

时间:2010-02-10 00:54:17

标签: php dom domdocument

使用PHP我试图获取从WYSIWYG编辑器传递的HTML字符串,并用新的HTML替换预加载的HTML文档中的元素的子元素。

到目前为止,我正在加载文档,用于标识我想要通过ID更改的元素,但是将HTML转换为可放置在DOMElement中的内容的过程正在躲避我。

libxml_use_internal_errors(true);

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

$element = $doc->getElementById($item_id);
if(isset($element)){
    //Remove the old children from the element
    while($element->childNodes->length){
        $element->removeChild($element->firstChild);
    }

    //Need to build the new children from $html_string and append to $element
}

5 个答案:

答案 0 :(得分:13)

如果HTML字符串可以解析为XML,则可以执行此操作(清除所有子节点的元素后):

$fragment = $doc->createDocumentFragment();
$fragment->appendXML($html_string);
$element->appendChild($fragment);

如果$ html_string无法解析为XML,则会失败。如果是这样,你将不得不使用loadHTML(),它不那么严格 - 但它会在片段周围添加你必须去除的元素。

与PHP不同,Javascript具有innerHTML属性,允许您非常轻松地执行此操作。我需要类似于项目的东西,所以我扩展了PHP的DOMElement以包含类似Javascript的innerHTML访问。

有了它,您可以访问innerHTML属性并像在Javascript中一样进行更改:

echo $element->innerHTML;
$elem->innerHTML = '<a href="http://example.org">example</a>';

来源:http://www.keyvan.net/2012/11/php-domdocument-replace-domelement-child-with-html-string/

答案 1 :(得分:1)

您可以在代码片段上使用loadHTML(),然后将生成的创建节点附加到原始DOM树中。

答案 2 :(得分:1)

当前接受的答案建议使用appendXML(),但承认它不会处理复杂的html,例如原始问题中指定的从WYSISYG编辑器返回的内容。正如建议的loadHTML()可以解决这个问题。但还没有人表明如何。

我认为这是解决编码问题的原始问题的最佳/正确答案,&#34;文档片段是空的&#34;警告和&#34;错误的文档错误&#34;如果他们从头开始写这个,可能会遇到的错误。我知道在按照之前的回复中的提示后我找到了它们。

这是我支持的网站的代码,它将WordPress边栏内容插入到帖子的$ content中。它假定$ doc是一个有效的DOMDocument,类似于原始问题中$ doc的定义方式。它还假设$ element是您希望插入sidebarcontent(或其他)的标记。

            // NOTE: Cannot use a document fragment here as the AMP html is too complex for the appendXML function to accept.
            // Instead create it as a document element and insert that way.
            $node = new DOMDocument();
            // Note that we must encode it correctly or strange characters may appear.
            $node->loadHTML( mb_convert_encoding( $sidebarContent, 'HTML-ENTITIES', 'UTF-8') );
            // Now we need to move this document element into the scope of the content document 
            // created above or the insert/append will be rejected.
            $node = $doc->importNode( $node->documentElement, true );
            // If there is a next sibling, insert before it.
            // If not, just add it at the end of the element we did find.
            if (  $element->nextSibling ) {
                $element->parentNode->insertBefore( $node, $element->nextSibling );
            } else {
                $element->parentNode->appendChild($node);
            }

完成所有这些操作后,如果您不想拥有带有body标签的完整HTML文档的来源,那么您可以使用以下内容生成更加本地化的html:

    // Now because we have moved the post content into a full document, we need to get rid of the 
    // extra elements that make it a document and not a fragment
    $body = $doc->getElementsByTagName( 'body' );
    $body = $body->item(0);

    // If you need an element with a body tag, you can do this.
    // return $doc->savehtml( $body );

    // Extract the html from the body tag piece by piece to ensure valid html syntax in destination document
    $bodyContent = ''; 
    foreach( $body->childNodes as $node ) { 
            $bodyContent .= $body->ownerDocument->saveHTML( $node ); 
    } 
    // Now return the full content with the new content added. 
    return $bodyContent;

答案 3 :(得分:1)

我知道这很老了,但是当前的答案都没有一个最小的工作示例,说明如何用存储在字符串中的HTML替换DOMDocument中的DOMNode。

application/json

答案 4 :(得分:0)

我知道这是一个旧线程(但回复此问题,因为也在寻找解决方案)。我在使用它时只用一行代替了内容。为了更好地理解该方法,我还添加了一些名为functions的上下文。

现在这是我的库的一部分,所以这就是所有函数名称的原因,所有函数都以前缀'su'开头。

它非常易于使用且功能非常强大(而且代码非常少)。

以下是代码:

function suSetHtmlElementById( &$oDoc, &$s, $sId, $sHtml, $bAppend = false, $bInsert = false, $bAddToOuter = false )
 {
    if( suIsValidString( $s ) && suIsValidString( $sId ))
    {
     $bCreate = true;
     if( is_object( $oDoc ))
     {
       if( !( $oDoc instanceof DOMDocument ))
        { return false; }
       $bCreate = false;
     }

     if( $bCreate )
      { $oDoc = new DOMDocument(); }

     libxml_use_internal_errors(true);
     $oDoc->loadHTML($s);
     libxml_use_internal_errors(false);
     $oNode = $oDoc->getElementById( $sId );

     if( is_object( $oNode ))
     { 
       $bReplaceOuter = ( !$bAppend && !$bInsert );

       $sId = uniqid('SHEBI-');
       $aId = array( "<!-- $sId -->", "<!--$sId-->" );

       if( $bReplaceOuter )
       {
         if( suIsValidString( $sHtml ) )
         {
             $oNode->parentNode->replaceChild( $oDoc->createComment( $sId ), $oNode );
             $s = $oDoc->saveHtml();
             $s = str_replace( $aId, $sHtml, $oDoc->saveHtml());
         }
         else { $oNode->parentNode->removeChild( $oNode ); 
                $s = $oDoc->saveHtml();
              }
         return true;
       }

       $bReplaceInner = ( $bAppend && $bInsert );
       $sThis = null;

       if( !$bReplaceInner )
       {
         $sThis = $oDoc->saveHTML( $oNode );
         $sThis = ($bInsert?$sHtml:'').($bAddToOuter?$sThis:(substr($sThis,strpos($sThis,'>')+1,-(strlen($oNode->nodeName)+3)))).($bAppend?$sHtml:''); 
       }

       if( !$bReplaceInner && $bAddToOuter )
       { 
          $oNode->parentNode->replaceChild( $oDoc->createComment( $sId ), $oNode );
          $sId = &$aId;
       }
       else { $oNode->nodeValue = $sId; }

       $s = str_replace( $sId, $bReplaceInner?$sHtml:$sThis, $oDoc->saveHtml());
       return true;
     }
    } 
    return false; 
 }

// A function of my library used in the function above:
function suIsValidString( &$s, &$iLen = null, $minLen = null, $maxLen = null )
{
  if( !is_string( $s ) || !isset( $s{0} ))
   { return false; }

  if( $iLen !== null )
   { $iLen = strlen( $s ); }

  return (( $minLen===null?true:($minLen > 0 && isset( $s{$minLen-1} ))) && 
           $maxLen===null?true:($maxLen >= $minLen && !isset( $s{$maxLen})));   
}   

某些上下文功能:

 function suAppendHtmlById( &$s, $sId, $sHtml, &$oDoc = null )
 { return suSetHtmlElementById( $oDoc, $s, $sId, $sHtml, true, false ); }

 function suInsertHtmlById( &$s, $sId, $sHtml, &$oDoc = null )
 { return suSetHtmlElementById( $oDoc, $s, $sId, $sHtml, false, true ); }

 function suAddHtmlBeforeById( &$s, $sId, $sHtml, &$oDoc = null )
 { return suSetHtmlElementById( $oDoc, $s, $sId, $sHtml, false, true, true ); }

 function suAddHtmlAfterById( &$s, $sId, $sHtml, &$oDoc = null )
 { return suSetHtmlElementById( $oDoc, $s, $sId, $sHtml, true, false, true ); }

 function suSetHtmlById( &$s, $sId, $sHtml, &$oDoc = null )
 { return suSetHtmlElementById( $oDoc, $s, $sId, $sHtml, true, true ); }

 function suReplaceHtmlElementById( &$s, $sId, $sHtml, &$oDoc = null )
 { return suSetHtmlElementById( $oDoc, $s, $sId, $sHtml, false, false ); }

 function suRemoveHtmlElementById( &$s, $sId, &$oDoc = null )
 { return suSetHtmlElementById( $oDoc, $s, $sId, null, false, false ); }

如何使用

在以下示例中,我假设已将内容加载到名为$sMyHtml的变量中,变量$sMyNewContent包含一些新的html。变量$sMyHtml包含一个名为/的元素,其标识为“example_id”。

// Example 1: Append new content to the innerHTML of an element (bottom of element):
if( suAppendHtmlById( $sMyHtml, 'example_id', $sMyNewContent ))
 { echo $sMyHtml; }
 else { echo 'Element not found?'; }

// Example 2: Insert new content to the innerHTML of an element (top of element):
suInsertHtmlById( $sMyHtml, 'example_id', $sMyNewContent );    

// Example 3: Add new content ABOVE element:
suAddHtmlBeforeById( $sMyHtml, 'example_id', $sMyNewContent );    

// Example 3: Add new content BELOW/NEXT TO element:
suAddHtmlAfterById( $sMyHtml, 'example_id', $sMyNewContent );    

// Example 4: SET new innerHTML content of element:
suSetHtmlById( $sMyHtml, 'example_id', $sMyNewContent );    

// Example 5: Replace entire element with new content:
suReplaceHtmlElementById( $sMyHtml, 'example_id', $sMyNewContent );    

// Example 6: Remove entire element:
suSetHtmlElementById( $sMyHtml, 'example_id' );