Используя PHP, я пытаюсь взять строку HTML, переданную из редактора WYSIWYG, и заменить дочерние элементы элемента внутри предварительно загруженного 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 }
Если HTML-строку можно проанализировать как XML, вы можете сделать это (после очистки элемента всех дочерних узлов):
$fragment = $doc->createDocumentFragment(); $fragment->appendXML($html_string); $element->appendChild($fragment);
Если $ html_string не может быть проанализирован как XML, он будет терпеть неудачу. Если это так, вам придется использовать loadHTML (), который является менее строгим, но он добавит элементы вокруг фрагмента, который вам придется разбить.
В отличие от PHP, Javascript имеет свойство innerHTML, которое позволяет вам сделать это очень легко. Мне нужен был что-то вроде этого для проекта, поэтому я расширил DOMElement PHP, чтобы включить доступ к Javascript для внутреннего доступаHTML.
С его помощью вы можете получить доступ к свойству 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/
Вы можете использовать loadHTML()
на фрагменте кода, а затем добавить созданные созданные узлы в исходное дерево DOM.
В текущем принятом ответе предлагается использовать appendXML (), но признает, что он не будет обрабатывать сложные html, такие как то, что возвращается из редактора WYSISYG, как указано в исходном вопросе. Как предложено loadHTML () может решить эту проблему. но никто пока не показал, как это сделать.
Это то, что, по моему мнению, является лучшим / правильным ответом на исходный вопрос, касающийся проблем с кодировкой, «Ошибки документа являются пустыми» и «Ошибки ошибочного документа», которые могут пострадать, если они напишут это с нуля. Я знаю, что нашел их после следующих подсказок в предыдущих ответах.
Это код с сайта, который я поддерживаю, который вставляет содержимое боковой панели 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); }
После того, как все это будет сделано, если вы не хотите иметь источник полного 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;
Я знаю, что это старый поток (но ответ на это, потому что ищем решение этого). Я использовал простой метод для замены содержимого только одной строкой при его использовании. Чтобы лучше понять метод, я также добавляю некоторые контекстные функции.
Теперь это часть моей библиотеки, поэтому в этом причина всех имен функций, все функции начинаются с префикса '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
содержит элемент с / с id ' 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' );